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

master
Justin Balthrop 2009-08-07 13:18:56 -07:00
parent 5592d5b746
commit af7cb910ac
1 changed files with 67 additions and 38 deletions

View File

@ -1,4 +1,4 @@
require 'bdb'
require 'bdb' if not defined?(Bdb)
class Bdb::Simple
include Enumerable
@ -19,7 +19,7 @@ class Bdb::Simple
@db = Bdb::Db.new
@db.flags = Bdb::DB_DUPSORT if dup?
@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
@db.open(nil, file, nil, Bdb::Db::BTREE, Bdb::DB_CREATE, 0)
end
@ -30,33 +30,16 @@ class Bdb::Simple
db[Marshal.dump(key)] = Marshal.dump(value)
end
def delete(key)
db.del(nil, Marshal.dump(key), 0)
end
def [](key)
if key.kind_of?(Range)
values = []
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
if key.kind_of?(Range) or dup?
Bdb::SimpleSet.new(db, key)
else
key = Marshal.dump(key)
if dup?
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
v = db.get(nil, Marshal.dump(key), nil, 0)
Marshal.load(v) if v
end
end
@ -86,20 +69,66 @@ class Bdb::Simple
end
def self.compare_absolute(left, right)
if left.is_a?(Array) and right.is_a?(Array)
left.zip(right) do |l,r|
comp = compare_absolute(l, r)
return comp unless comp == 0
if left.class == right.class
if left.is_a?(Array) and right.is_a?(Array)
# Arrays: compare one element at a time.
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
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
# 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
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