--
This commit is contained in:
parent
1063201b64
commit
0621370cec
103
lib/aodbm.rb
Normal file
103
lib/aodbm.rb
Normal 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
192
lib/plain.rb
Normal 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
|
Loading…
Reference in a new issue