add simple_test and enumerable range/dup queries. improve absolute sort

This commit is contained in:
Justin Balthrop 2009-08-07 13:18:56 -07:00
parent 5592d5b746
commit af7cb910ac

View file

@ -1,4 +1,4 @@
require 'bdb' require 'bdb' if not defined?(Bdb)
class Bdb::Simple class Bdb::Simple
include Enumerable include Enumerable
@ -19,7 +19,7 @@ class Bdb::Simple
@db = Bdb::Db.new @db = Bdb::Db.new
@db.flags = Bdb::DB_DUPSORT if dup? @db.flags = Bdb::DB_DUPSORT if dup?
@db.btree_compare = lambda do |db, key1, key2| @db.btree_compare = lambda do |db, key1, key2|
compare_absolute(Marshal.load(key1), Marshal.load(key2)) self.class.compare_absolute(Marshal.load(key1), Marshal.load(key2))
end end
@db.open(nil, file, nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0) @db.open(nil, file, nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
end end
@ -30,33 +30,16 @@ class Bdb::Simple
db[Marshal.dump(key)] = Marshal.dump(value) db[Marshal.dump(key)] = Marshal.dump(value)
end end
def delete(key)
db.del(nil, Marshal.dump(key), 0)
end
def [](key) def [](key)
if key.kind_of?(Range) if key.kind_of?(Range) or dup?
values = [] Bdb::SimpleSet.new(db, key)
cursor = db.cursor(nil, 0)
k,v = cursor.get(Marshal.dump(key.first), nil, Bdb::DB_SET_RANGE)
while k and key.include?(Marshal.load(k))
values << Marshal.load(v)
k, v = cursor.get(nil, nil, Bdb::DB_NEXT)
end
cursor.close
values
else else
key = Marshal.dump(key) v = db.get(nil, Marshal.dump(key), nil, 0)
if dup? Marshal.load(v) if v
values = []
cursor = db.cursor(nil, 0)
k,v = cursor.get(key, nil, Bdb::DB_SET)
while data
values << Marshal.load(v)
k,v = cursor.get(nil, nil, Bdb::DB_NEXT_DUP)
end
cursor.close
values
else
v = db.get(nil, key, nil, 0)
Marshal.load(v) if v
end
end end
end end
@ -86,20 +69,66 @@ class Bdb::Simple
end end
def self.compare_absolute(left, right) def self.compare_absolute(left, right)
if left.is_a?(Array) and right.is_a?(Array) if left.class == right.class
left.zip(right) do |l,r| if left.is_a?(Array) and right.is_a?(Array)
comp = compare_absolute(l, r) # Arrays: compare one element at a time.
return comp unless comp == 0 left.zip(right) do |l,r|
comp = compare_absolute(l, r)
return comp unless comp == 0
end
left.size == right.size ? 0 : -1
elsif left.is_a?(Hash) and right.is_a?(Hash)
# Hashes: sort the keys and compare as an array of arrays.
left = left.to_a.sort {|a,b| compare_absolute(a[0],b[0])}
right = right.to_a.sort {|a,b| compare_absolute(a[0],b[0])}
compare_absolute(left, right)
else
begin
# Try to use the spaceship operator.
left <=> right
rescue NoMethodError => e
left.hash <=> right.hash
end
end end
left.size == right.size ? 0 : -1
elsif left.kind_of?(right.class) or right.kind_of?(left.class)
left <=> right rescue 0
elsif left.is_a?(NilClass)
-1
elsif right.is_a?(NilClass)
1
else else
# Nil is the smallest. Hash is the largest. All other objects are sorted by class name.
return -1 if left.is_a?(NilClass)
return 1 if right.is_a?(NilClass)
return 1 if left.is_a?(Hash)
return -1 if right.is_a?(Hash)
# Compare class names and hashes as a last resort if that fails.
right.class.name <=> left.class.name right.class.name <=> left.class.name
end end
end end
end end
class Bdb::SimpleSet
include Enumerable
def initialize(db, key)
@db = db
@key = key
end
attr_reader :db, :key
def each
if key.kind_of?(Range)
cursor = db.cursor(nil, 0)
k,v = cursor.get(Marshal.dump(key.first), nil, Bdb::DB_SET_RANGE)
while k and key.include?(Marshal.load(k))
yield Marshal.load(v)
k, v = cursor.get(nil, nil, Bdb::DB_NEXT)
end
cursor.close
else
cursor = db.cursor(nil, 0)
k,v = cursor.get(Marshal.dump(key), nil, Bdb::DB_SET)
while k
yield Marshal.load(v)
k,v = cursor.get(nil, nil, Bdb::DB_NEXT_DUP)
end
cursor.close
end
end
end