class LibCDB::CDB
See README.
Constants
- LIBCDB_VERSION
The TinyCDB library version.
- VERSION
Attributes
The underlying IO object.
The current IO mode, either r
or w
.
Public Class Methods
Opens path
for reading and shovels each record dump into
target
(followed by separator
, if present).
Returns target
.
# File lib/libcdb.rb, line 106 def dump(path, target = '', separator = $\ || $/) open(path) { |cdb| cdb.each_dump { |dump| target << dump target << separator if separator } target } end
Opens path
for reading and iterates over each key/value pair,
or, if key
is given, each value for key
.
# File lib/libcdb.rb, line 97 def foreach(path, *key) open(path) { |cdb| cdb.each(*key) { |val| yield val } } end
Opens path
for writing and loads dump
into the
database. dump
may be a string or an IO object. Returns the
(unclosed) CDB object.
# File lib/libcdb.rb, line 122 def load(path, dump) require 'strscan' s, n, e = nil, 0, lambda { |m| s.eos? ? raise("Unexpected end of input (#{m} at #{n}).") : raise("#{m} at #{n}:#{s.pos}: #{s.peek(16).inspect}") } cdb = open(path, 'w+') dump.each_line { |line| n += 1 s = StringScanner.new(line) e['Record identifier expected'] unless s.scan(/\+/) e['Key length expected'] unless s.scan(/\d+/) klen = s.matched.to_i e['Length separator expected'] unless s.scan(/,/) e['Value length expected'] unless s.scan(/\d+/) vlen = s.matched.to_i e['Key separator expected'] unless s.scan(/:/) key = '' klen.times { key << s.get_byte } e['Value separator expected'] unless s.scan(/->/) value = '' vlen.times { value << s.get_byte } e['Record terminator expected'] unless s.scan(/\n/) e['Unexpected data'] unless s.eos? cdb.store(key, value) } cdb end
Loads the dump at file
into the database at path
(see load).
# File lib/libcdb.rb, line 169 def load_file(path, file) File.open(file, 'rb') { |f| self.load(path, f) } end
Creates a new CDB object to interface with
io
. mode
must be the same mode io
was opened in, either r
or w
. Responds to both Reader and Writer
methods interchangeably by reopening io
in the corresponding
mode and instantiating a new Reader or Writer object with it. Note that io
will be truncated each time it's opened for writing.
# File lib/libcdb.rb, line 256 def initialize(io, mode = MODE_WRITE) @io, @mode = io, mode case mode when MODE_READ then open_read when MODE_WRITE then open_write else raise ArgumentError, "illegal access mode #{mode.inspect}" end end
Opens path
with mode
. If a block is given, yields
a cdb object according to mode
(see below) and
returns the return value of the block; the object is closed afterwards.
Otherwise just returns the object.
# File lib/libcdb.rb, line 62 def open(path, mode = MODE_READ) klass, args = _open_args(path, mode) cdb = begin klass.new(*args) rescue args.first.close raise end if block_given? begin yield cdb ensure err = $! begin cdb.close rescue raise unless err end raise err if err end else cdb end end
Prints the stats on path
.
# File lib/libcdb.rb, line 207 def print_stats(path) stats(path).tap { |s| r, k, v, h = s.values_at(:records, :keys, :values, :hash) v1, v2 = [:min, :avg, :max], [:tables, :entries, :collisions] puts 'number of records: %d' % r puts 'key min/avg/max length: %d/%d/%d' % k.values_at(*v1) puts 'val min/avg/max length: %d/%d/%d' % v.values_at(*v1) # TODO: hash table stats puts 'hash tables/entries/collisions: %d/%d/%d' % h.values_at(*v2) puts 'hash table min/avg/max length: %d/%d/%d' % h.values_at(*v1) puts 'hash table distances:' d = h[:distances] 0.upto(9) { |i| puts ' d%d: %6d %2d%%' % [i, *d[i]] } puts ' >9: %6d %2d%%' % d[-1] } end
Returns a hash with the stats on path
.
# File lib/libcdb.rb, line 177 def stats(path) {}.tap { |stats| open(path) { |cdb| stats[:records] = cnt = cdb.total stats[:keys] = khash = { min: Float::INFINITY, avg: 0, max: 0 } stats[:values] = vhash = khash.dup stats[:hash] = Hash.new(0).update(distances: Hash.new([0, 0])) khash[:min] = vhash[:min] = 0 and break if cnt.zero? ktot, vtot, update = 0, 0, lambda { |h, s| s.bytesize.tap { |l| h[:min] = l if l < h[:min] h[:max] = l if l > h[:max] } } cdb.each_key { |k| ktot += update[khash, k] } cdb.each_value { |v| vtot += update[vhash, v] } khash[:avg] = (ktot + cnt / 2) / cnt vhash[:avg] = (vtot + cnt / 2) / cnt # TODO: hash table stats } } end
Private Class Methods
# File lib/libcdb.rb, line 232 def _open_args(path, mode) klass, args = self, [] case mode when 'r+' then args = [mode = MODE_READ] when 'w+' then args = [ MODE_WRITE] when MODE_READ then klass = Reader when MODE_WRITE then klass, mode = Writer, 'w+' else raise ArgumentError, "illegal access mode #{mode.inspect}" end [klass, args.unshift(File.open(path, mode + 'b'))] end
Public Instance Methods
Whether cdb is closed. See closed_read? and closed_write?.
# File lib/libcdb.rb, line 392 def closed? read? ? closed_read? : write? ? closed_write? : nil end
Whether reader is closed if cdb is currently opened for reading.
# File lib/libcdb.rb, line 376 def closed_read? reader.closed? if read? end
Whether writer is closed if cdb is currently opened for writing.
# File lib/libcdb.rb, line 384 def closed_write? writer.closed? if write? end
Whether cdb is currently opened for reading.
# File lib/libcdb.rb, line 301 def read? !!@reader end
The Reader object associated with cdb.
# File lib/libcdb.rb, line 276 def reader @reader ||= open_read end
Whether cdb is currently opened for writing.
# File lib/libcdb.rb, line 309 def write? !!@writer end
The Writer object associated with cdb.
# File lib/libcdb.rb, line 284 def writer @writer ||= open_write end
Private Instance Methods
Reopens io in mode
and
returns it.
# File lib/libcdb.rb, line 402 def reopen(new_mode = MODE_READ) return io if mode == new_mode @mode = new_mode new_mode += '+' if new_mode == MODE_WRITE io.reopen(io.path, new_mode + 'b') end