--
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