Denis Knauf 2011-07-15 10:03:38 +02:00
parent 1063201b64
commit 0621370cec
2 changed files with 295 additions and 0 deletions

103
lib/aodbm.rb Normal file
View File

@ -0,0 +1,103 @@
require 'ffi'
module AODBM
class DB
attr_reader :c
def initialize name, flags = nil
flags ||= 0
@c = Lib::open name, flags
end
def open name, flags = nil
db = new name, flags
block_given? ? yield( db) : db
end
def close
Lib::close @c
end
def current
Version.new @c, Lib::current( @c)
end
def commit version
Lib::commit @c, version
end
end
class Version
attr_reader :db, :c
def initialize db, c
@db, @c = db, c
end
def previous
Version.new @db, Lib::previous( @db.c, @c)
end
def has? data
Lib::has @db.c, @c, Lib::Data.new( data)
end
def set key, val
Lib::set @db.c, @c, Lib::Data.new( key), Lib::Data.new( val)
end
end
module Lib
extend FFI::Library
ffi_lib "libaodbm.so"
typedef :pointer, :aodbm
typedef :uint64, :version
class Data < FFI::Struct
def initialilze data
end
layout :dat, :buffer, :sz, :size_t
end
typedef :pointer, :data
attach_function :open, :aodbm_open, [:string, :int], :aodbm
attach_function :close, :aodbm_close, [:aodbm], :void
attach_function :current, :aodbm_current, [:aodbm], :version
attach_function :commit, :aodbm_commit, [:aodbm, :version], :bool
attach_function :has, :aodbm_has, [:aodbm, :version, :data], :bool
attach_function :set, :aodbm_set, [:aodbm, :version, :data, :data], :version
attach_function :get, :aodbm_get, [:aodbm, :version, :data], :data
attach_function :del, :aodbm_del, [:aodbm, :version, :data], :version
attach_function :is_based_on, :aodbm_is_based_on, [:aodbm, :version, :data], :bool
attach_function :previous, :aodbm_previous_version, [:aodbm, :version], :version
attach_function :common_ancestor, :aodbm_common_ancestor, [:aodbm, :version, :version], :version
#typedef :changeset, :pointer
#attach_function :aodbm_diff_prev, [:aodbm, :version], :changeset
#attach_function :aodbm_diff_prev_rev, [:aodbm, :version], :changeset
#attach_function :aodbm_diff, [:aodbm, :version, :version], :changeset
#attach_function :aodbm_apply, [:aodbm, :version, :changeset], :version
#attach_function :aodbm_apply_di, [:aodbm, :version, :changeset], :version
#attach_function :aodbm_merge, [:aodbm, :version, :version], :version
typedef :pointer, :iterator
class Record < FFI::Struct
layout :key, :data, :val, :data
end
typedef :pointer, :record
attach_function :new_iterator, :aodbm_new_iterator, [:aodbm, :version], :iterator
attach_function :iterator_from, :aodbm_iterate_from, [:aodbm, :version, :data], :iterator
attach_function :iterate_next, :aodbm_iterator_next, [:aodbm, :iterator], :record
attach_function :iterate_goto, :aodbm_iterator_goto, [:aodbm, :iterator, :data], :void
attach_function :free_iterator, :aodbm_free_iterator, [:iterator], :void
attach_function :free_data, :aodbm_free_data, [:data], :void
end
end

192
lib/plain.rb Normal file
View File

@ -0,0 +1,192 @@
require 'zlib' # Zlib::crc32
require 'uuidtools'
# Maximum Filesize: 4GB because #pack("N")
class Plain < File
UUID_SIZE = 16 # Size of an key (UUID as raw 16byte string)
INT_SIZE = 4
CRCR32_SIZE = INT_SIZE # Size of CRC32 (32Bit)
KEY_SIZE = UUID_SIZE
IDX_SIZE = INT_SIZE # Size of an index (big-endian 32bit integer)
SIZE_SIZE = INT_SIZE # Size of size_t
CHK_SIZE = CRCR32_SIZE
IDXE_SIZE = KEY_SIZE + IDX_SIZE # Size of an entry in index-file
DBP_SIZE = KEY_SIZE + SIZE_SIZE + CHK_SIZE
UUID_PACK = "a16"
INT_PACK = "N"
STR_PACK = "a*"
CRC32_PACK = INT_PACK
KEY_PACK = UUID_PACK
IDX_PACK = INT_PACK
SIZE_PACK = INT_PACK
CHK_PACK = CRC32_PACK
IDXE_PACK = KEY_PACK + IDX_PACK
DBP_PACK = KEY_PACK + SIZE_PACK + CHK_PACK
VAL_PACK = STR_PACK
ENTRY_PACK = DBP_PACK + VAL_PACK
class Corrupt < Exception
end
module UUIDConv
class <<self
# Converts a UUIDTools::UUID to raw-String: UUIDTools::UUID#raw
def cto_s x
x.raw
end
# Converts a String to UUIDTools::UUID: UUIDTools::UUID.parse_raw
def cto_key x
UUIDTools::UUID.parse_raw x
end
end
end
# Converting-helper.
# To methods are required:
# #cto_str # obj => 16-byte-String
# #cto_key # 16-byte-String => obj
attr_accessor :conv
attr_reader :idx
def initialize *a
super *a
@conv = @@conv
@idx = File.open "#{path}.idx", a[1]
end
class <<self
# Default we use UUIDTool for keys.
@@conv = UUIDConv
def new *a
a[1] ||= 'a'
r = super *a
block_given? ? yield(r) : r
end
def open *a, &e
a[1] ||= 'r'
new *a, &e
end
end
def sync
super
@idx.sync
end
def close
super
@idx.close
end
def gen_entry key, val
[key, val.length, Zlib::crc32( val), val].pack( ENTRY_PACK)
end
def gen_idx key, pos = self.pos
[key, pos].pack IDXE_PACK
end
# Store an entry in DB
def push key, val
key = @conv.cto_s key
idx = gen_idx key
print gen_entry( key, val)
@idx.print idx
sync
@idx.sync
true
end
def put val
push UUIDTools::UUID.timestamp_create, val
end
# Read the entry.
# First you should seek, if you won't read sequential
def get
if m = read( DBP_SIZE)
key, length, chk = m.unpack DBP_PACK
val = read length
valchk = Zlib::crc32 val
raise Corrupt, "#{path} is corrupt: #{pos-length-CHK_SIZE-DBP_SIZE} #{chk} -- #{valchk}" unless chk == valchk
[@conv.cto_key( key), length, val, chk]
end
end
alias next get
# yield(idx): must return:
# -1: to small
# 0: found! return true
# 1: to big
# else: like you use break nil
# yield is allowed to use break
def self.binary_search size
idx = 1
begin
i = size
while 0 < i
idx <<= 1
i >>= 1
end
end
idx >>= 1
t = idx
p idx
while t > 0
t >>= 1
i = yield idx
case i
when -1 then idx -= t
when 1 then idx += t
when 0 then return true
else return nil
end
end
end
# Get the value.
# key must provide: #<=> uuid
def search_ key
idx = self.length
position = nil
self.class.binary_search( idx) do |idx|
p idx
idx *= IDXE_SIZE
if idx > size
size = @idx.size
break if idx > size
end
# Read entry i. p is position and k is key
@idx.seek idx
k, position = @idx.read( IDXE_SIZE).unpack IDXE_PACK
p pos: position, key: k
key <=> @conv.cto_key( k)
end ? position : nil
end
def [] key
if position = search_( key)
self.pos = position+KEY_SIZE
length,_ = read( SIZE_SIZE).unpack( SIZE_PACK)
read( length)[2]
end
end
def each
return Enumerator.new( self) unless block_given?
yield *get while !eof?
end
# counts entries (size of the idx): O(1)
def length
@idx.size/IDXE_SIZE
end
end