initial checkin of ruby bindings
This commit is contained in:
parent
aa8142d708
commit
c3b9345bd2
10 changed files with 455 additions and 0 deletions
22
lang/ruby/stasis.rb
Normal file
22
lang/ruby/stasis.rb
Normal 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
99
lang/ruby/stasis/ffi.rb
Normal 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
53
lang/ruby/stasis/hash.rb
Normal 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
77
lang/ruby/stasis/raw.rb
Normal 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
|
41
lang/ruby/stasis/string.rb
Normal file
41
lang/ruby/stasis/string.rb
Normal 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
|
30
lang/ruby/stasis/string_iterator.rb
Normal file
30
lang/ruby/stasis/string_iterator.rb
Normal 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
|
39
lang/ruby/stasis/test/tc_hash.rb
Normal file
39
lang/ruby/stasis/test/tc_hash.rb
Normal 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
|
39
lang/ruby/stasis/test/tc_iterator.rb
Normal file
39
lang/ruby/stasis/test/tc_iterator.rb
Normal 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
|
52
lang/ruby/stasis/test/tc_string.rb
Normal file
52
lang/ruby/stasis/test/tc_string.rb
Normal 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
|
||||
|
3
lang/ruby/stasis/test/ts_stasis.rb
Normal file
3
lang/ruby/stasis/test/ts_stasis.rb
Normal file
|
@ -0,0 +1,3 @@
|
|||
require 'stasis/test/tc_string'
|
||||
require 'stasis/test/tc_hash'
|
||||
require 'stasis/test/tc_iterator'
|
Loading…
Reference in a new issue