initial checkin of ruby bindings

This commit is contained in:
Sears Russell 2010-09-20 16:09:44 +00:00
parent aa8142d708
commit c3b9345bd2
10 changed files with 455 additions and 0 deletions

22
lang/ruby/stasis.rb Normal file
View file

@ -0,0 +1,22 @@
require 'stasis/string'
require 'stasis/hash'
require 'stasis/string_iterator'
class Stasis
Stasis.init_count = 0;
def initialize(xid=nil, rid=nil)
if Stasis.init_count == 0 then
Raw.Tinit
end
Stasis.init_count++;
if rid == nil then
rid = Raw.ROOT_RID
end
if xid == nil then
@xid = -1
@autoxact = true
else
@xid = xid
@autoxact = false
end
end

99
lang/ruby/stasis/ffi.rb Normal file
View file

@ -0,0 +1,99 @@
require 'ffi'
module Stasis
class Native
extend FFI::Library
## Stasis typedefs
typedef :uchar, :byte
typedef :int, :xid
typedef :int64, :lsn
typedef :int64, :pageid
typedef :int32, :slotid
typedef :int16, :pageoff
typedef :int16, :pagetype
typedef :pointer, :iterator
typedef :pointer, :bytes
# Not a managed struct, won't be GC'ed
class RecordId < FFI::Struct
layout :page, :pageid,
:slot, :slotid,
:size, :int64,
end
typedef RecordId.by_value, :recordid
## Functions exported by libstasis.so
ffi_lib "stasis"
attach_function 'Tinit', [ ], :int
attach_function 'Tdeinit', [ ], :int
attach_function 'Tbegin', [ ], :xid
attach_function 'Tcommit', [ :xid ], :int
attach_function 'TsoftCommit', [ :xid ], :int
attach_function 'TforceCommits', [ ], :void
attach_function 'Tabort', [ :xid ], :int
attach_function 'Tprepare', [ :xid ], :int
attach_function 'Talloc', [ :xid, :int ], :recordid
attach_function 'TrecordType', [ :xid, :recordid ], :int
attach_function 'TrecordSize', [ :xid, :recordid ], :int
attach_function 'Tset', [ :xid, :recordid, :bytes ], :int
# the string is an out parameter
attach_function 'Tread', [ :xid, :recordid, :bytes ], :int
# The second two parameters should be -1.
attach_function 'ThashCreate', [ :xid, :int, :int ], :recordid
attach_function 'ThashInsert', [ :xid, :recordid,
:bytes, :int,
:bytes, :int ], :int
attach_function 'ThashRemove', [ :xid, :recordid,
:bytes, :int ], :int
## Note: The pointer is an OUT param
attach_function 'ThashLookup', [ :xid, :recordid,
:bytes, :int, :pointer ], :int
attach_function 'ThashGenericIterator', [ :xid, :recordid ], :iterator
attach_function 'Titerator_next', [:xid, :iterator], :int
## Note: THe pointer is an OUT param
attach_function 'Titerator_value', [:xid, :iterator, :pointer], :int
attach_function 'Titerator_tupleDone', [:xid, :iterator ], :void
attach_function 'Titerator_close', [:xid, :iterator], :void
# XXX move to a different class!
def Native.ThashLookupHelper(xid, rid, string)
objptrptr = MemoryPointer.new :pointer
len = Stasis.ThashLookup(xid, rid, string, objptrptr);
objptr = objptr.get_pointer(0)
str = objptr.null ? nil : objptr.read_string
objptr.free
return str
end
def Native.TallocString(xid, str)
rid = Talloc(xid, str.length+1) ## XXX str.length is wrong.
## want the size of the underlying buffer.
Tset(xid, rid, str)
return rid
end
def Native.TgetString(xid, rid)
ret = " " * (TrecordSize(xid, rid)-1)
Tread(xid, rid, ret)
return ret
end
def Native.TsetString(xid, rid, str)
if(str.length + 1 > TrecordSize(xid, rid)-1)
return false # throw exception?
else
Tset(xid, rid, str)
return true
end
end
end
end

53
lang/ruby/stasis/hash.rb Normal file
View file

@ -0,0 +1,53 @@
require 'stasis/raw'
module Stasis
class Hash
extend FFI::Library
ffi_lib FFI::Library::LIBC
attach_function 'free', [:pointer], :void # called by Hash.lookup
def Hash.alloc(xid)
return Raw.ThashCreate xid, -1, -1
end
def Hash.insert(xid, rid, key, val)
keylen = key.length+1
vallen = val.length+1
keyptr = FFI::MemoryPointer.new :char, keylen
valptr = FFI::MemoryPointer.new :char, vallen
keyptr.put_string(0, key)
valptr.put_string(0, val)
ret = Raw.ThashInsert xid, rid, keyptr, keylen, valptr, vallen;
keyptr.free
valptr.free
return ret
end
def Hash.remove(xid, rid, key)
keylen = key.length+1
keyptr = FFI::MemoryPointer.new :char, keylen
ret = Raw.ThashRemove xid, rid, keyptr, keylen
keyptr.free
return ret
end
def Hash.iterator(xid, rid)
return Raw.ThashGenericIterator xid, rid
end
def Hash.lookup(xid, rid, key)
keylen = key.length+1
keyptr = FFI::MemoryPointer.new :char, keylen
keyptr.put_string(0, key)
objptrptr = FFI::MemoryPointer.new :pointer
len = Raw.ThashLookup(xid, rid, keyptr, keylen, objptrptr);
objptr = objptrptr.get_pointer(0)
str = objptr.null? ? nil : objptr.get_string(0)
keyptr.free
free objptr
return str
end
end
end

77
lang/ruby/stasis/raw.rb Normal file
View file

@ -0,0 +1,77 @@
require 'ffi'
module Stasis
class Raw
extend FFI::Library
## Stasis typedefs
typedef :uchar, :byte
typedef :int, :xid
typedef :int64, :lsn
typedef :int64, :pageid
typedef :int32, :slotid
typedef :int16, :pageoff
typedef :int16, :pagetype
typedef :pointer, :iterator
typedef :pointer, :bytes
# Not a managed struct, won't be GC'ed
class RecordId < FFI::Struct
layout :page, :pageid,
:slot, :slotid,
:size, :int64,
end
Stasis::ROOT_RID = RecordId.new
Stasis::ROOT_RID[:page] = 1
Stasis::ROOT_RID[:slot] = 0
Stasis::ROOT_RID[:size] = -1
Stasis::NULLRID = RecordId.new
Stasis::NULLRID[:page] = 0
Stasis::NULLRID[:slot] = 0
Stasis::NULLRID[:size] = -1
typedef RecordId.by_value, :recordid
## Functions exported by libstasis.so
ffi_lib "stasis"
attach_function 'Tinit', [ ], :int
attach_function 'Tdeinit', [ ], :int
attach_function 'Tbegin', [ ], :xid
attach_function 'Tcommit', [ :xid ], :int
attach_function 'TsoftCommit', [ :xid ], :int
attach_function 'TforceCommits', [ ], :void
attach_function 'Tabort', [ :xid ], :int
attach_function 'Tprepare', [ :xid ], :int
attach_function 'Talloc', [ :xid, :int ], :recordid
attach_function 'TrecordType', [ :xid, :recordid ], :int
attach_function 'TrecordSize', [ :xid, :recordid ], :int
attach_function 'Tset', [ :xid, :recordid, :bytes ], :int
# the string is an out parameter
attach_function 'Tread', [ :xid, :recordid, :bytes ], :int
# The second two parameters should be -1.
attach_function 'ThashCreate', [ :xid, :int, :int ], :recordid
attach_function 'ThashInsert', [ :xid, :recordid,
:bytes, :int,
:bytes, :int ], :int
attach_function 'ThashRemove', [ :xid, :recordid,
:bytes, :int ], :int
## Note: The pointer is an OUT param
attach_function 'ThashLookup', [ :xid, :recordid,
:bytes, :int, :pointer ], :int
attach_function 'ThashGenericIterator', [ :xid, :recordid ], :iterator
attach_function 'Titerator_next', [:xid, :iterator], :int
## Note: The pointer is an OUT param
attach_function 'Titerator_key', [:xid, :iterator, :pointer], :int
## Note: The pointer is an OUT param
attach_function 'Titerator_value', [:xid, :iterator, :pointer], :int
attach_function 'Titerator_tupleDone', [:xid, :iterator ], :void
attach_function 'Titerator_close', [:xid, :iterator], :void
end
end

View file

@ -0,0 +1,41 @@
require 'stasis/raw'
module Stasis
class String
def String.alloc(xid, str)
## str.length is apparently the size of the underlying buffer.
rid = Raw.Talloc(xid, str.length+1)
return rid
end
def String.put_new(xid, str)
rid = String.alloc(xid, str)
if(String.set(xid, rid, str))
return rid
else
return nil # This will surely crash the interpreter. Oh well.
end
end
def String.get(xid, rid)
objptr = FFI::MemoryPointer.new :char, Raw.TrecordSize(xid, rid);
Raw.Tread(xid, rid, objptr);
str = objptr.get_string(0)
objptr.free
return str
end
def String.set(xid, rid, str)
if(str.length + 1 > Raw.TrecordSize(xid, rid))
return false # throw exception?
else
objptr = FFI::MemoryPointer.new :char, str.length+1
objptr.put_string(0, str);
Raw.Tset(xid, rid, objptr)
objptr.free
return true
end
end
end
end

View file

@ -0,0 +1,30 @@
require 'stasis/raw'
module Stasis
class StringIterator
def StringIterator.value(xid, it)
objptrptr = FFI::MemoryPointer.new :pointer
len = Raw.Titerator_value(xid, it, objptrptr)
if(len == -1) then return nil end
objptr = objptrptr.get_pointer(0)
# The slice is for bounds checking.
str = objptr.null? ? nil : objptr.slice(0, len).get_string(0)
# The iterator frees objptr during tupleDone.
objptrptr.free
return str
end
def StringIterator.key(xid, it)
objptrptr = FFI::MemoryPointer.new :pointer
len = Raw.Titerator_key(xid, it, objptrptr)
if(len == -1) then return nil end
objptr = objptrptr.get_pointer(0)
# The slice is for bounds checking.
str = objptr.null? ? nil : objptr.slice(0, len).get_string(0)
# The iterator frees objptr during tupleDone.
objptrptr.free
return str
end
end
end

View file

@ -0,0 +1,39 @@
require 'stasis/hash'
require 'test/unit'
module Stasis
class TestHash < Test::Unit::TestCase
def setup
`rm -f storefile.txt logfile.txt`
end
def teardown
`rm -f storefile.txt logfile.txt`
end
def test_combined
assert_equal(0, Raw.Tinit)
xid = Raw.Tbegin
rid = Hash.alloc(xid)
assert_equal(0, Hash.insert(xid, rid, "foo", "bar"))
assert_equal("bar", Hash.lookup(xid, rid, "foo"))
Raw.Tcommit xid
xid = Raw.Tbegin
assert_equal(1, Hash.insert(xid, rid, "foo", "baz"))
assert_equal(0, Hash.insert(xid, rid, "bar", "bat"))
assert_equal("baz", Hash.lookup(xid, rid, "foo"))
assert_equal("bat", Hash.lookup(xid, rid, "bar"))
Raw.Tabort xid
assert_equal("bar", Hash.lookup( -1, rid, "foo"))
assert_equal(nil, Hash.lookup( -1, rid, "bar"))
assert_equal(0, Raw.Tdeinit)
assert_equal(0, Raw.Tinit)
assert_equal("bar", Hash.lookup( -1, rid, "foo"))
assert_equal(nil, Hash.lookup( -1, rid, "bar"))
xid = Raw.Tbegin
assert_equal(0, Hash.insert(xid, rid, "bar", "bat"))
assert_equal("bat", Hash.lookup(xid,rid,"bar"))
assert_equal(0, Raw.Tdeinit)
end
end
end

View file

@ -0,0 +1,39 @@
require 'stasis/hash'
require 'stasis/string_iterator'
require 'test/unit'
module Stasis
class TestIterator < Test::Unit::TestCase
def setup
`rm -f storefile.txt logfile.txt`
end
def teardown
`rm -f storefile.txt logfile.txt`
end
def test_hash_iterator
assert_equal(0, Raw.Tinit)
xid = Raw.Tbegin
rid = Hash.alloc(xid)
h = {}
(0..100).each {
|x|
assert_equal(0, Hash.insert(xid, rid, x.to_s, (x*10).to_s))
h[x.to_s] = (x*10).to_s
}
i = {}
it = Hash.iterator(xid, rid)
while(0 != Raw.Titerator_next(xid, it))
i[StringIterator.key(xid, it)] = StringIterator.value(xid, it)
end
Raw.Titerator_close(xid, it)
assert_equal(h, i)
Raw.Tcommit xid
Raw.Tdeinit
end
end
end

View file

@ -0,0 +1,52 @@
require 'stasis/string'
require 'test/unit'
class TestStrings < Test::Unit::TestCase
def setup
`rm -f storefile.txt logfile.txt`
end
def teardown
`rm -f storefile.txt logfile.txt`
end
def test_abort
assert_equal(0, Stasis::Raw.Tinit)
xid = Stasis::Raw.Tbegin
rid = Stasis::String.put_new xid, "Hello world"
str = Stasis::String.get xid, rid
assert_equal("Hello world", str)
Stasis::Raw.Tcommit xid;
xid = Stasis::Raw.Tbegin
assert(Stasis::String.set xid, rid, "G'bye world")
str = Stasis::String.get xid, rid
assert_equal("G'bye world", str)
Stasis::Raw.Tabort xid;
str = Stasis::String.get(-1, rid)
assert_equal("Hello world", str)
assert_equal(0, Stasis::Raw.Tdeinit)
end
def test_recover
assert_equal(0, Stasis::Raw.Tinit)
xid = Stasis::Raw.Tbegin
rid = Stasis::String.put_new xid, "Hello world"
str = Stasis::String.get xid, rid
assert_equal("Hello world", str)
Stasis::Raw.Tcommit xid;
xid = Stasis::Raw.Tbegin
assert(Stasis::String.set xid, rid, "G'bye world")
str = Stasis::String.get xid, rid
assert_equal("G'bye world", str)
assert_equal(0, Stasis::Raw.Tdeinit)
assert_equal(0, Stasis::Raw.Tinit)
str = Stasis::String.get(-1, rid)
assert_equal("Hello world", str)
assert_equal(0, Stasis::Raw.Tdeinit)
end
### XXX check for memory leaks...
end

View file

@ -0,0 +1,3 @@
require 'stasis/test/tc_string'
require 'stasis/test/tc_hash'
require 'stasis/test/tc_iterator'