Skip to content

Commit

Permalink
Adding the core database functionality. Queries are still needed and …
Browse files Browse the repository at this point in the history
…I'm punting on tctdbputproc() until I can compare if it could be better implemented with queries than the weak function supplied.
  • Loading branch information
JEG2 committed Jan 21, 2010
1 parent 51a7eaa commit ff5a2b8
Show file tree
Hide file tree
Showing 17 changed files with 714 additions and 134 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rdoc
Expand Up @@ -5,6 +5,7 @@ Below is a complete listing of changes for each revision of Oklahoma Mixer.
== 0.3.0

* Added the empty?() method
* Added the ability to set an Array of duplicates at once for B+Tree Databases

== 0.2.0

Expand Down
5 changes: 5 additions & 0 deletions lib/oklahoma_mixer.rb
Expand Up @@ -7,6 +7,8 @@
require "oklahoma_mixer/extensible_string"
require "oklahoma_mixer/array_list/c"
require "oklahoma_mixer/array_list"
require "oklahoma_mixer/hash_map/c"
require "oklahoma_mixer/hash_map"
require "oklahoma_mixer/cursor/c"
require "oklahoma_mixer/cursor"

Expand All @@ -16,6 +18,8 @@
require "oklahoma_mixer/b_tree_database"
require "oklahoma_mixer/fixed_length_database/c"
require "oklahoma_mixer/fixed_length_database"
require "oklahoma_mixer/table_database/c"
require "oklahoma_mixer/table_database"

module OklahomaMixer
VERSION = "0.3.0"
Expand All @@ -25,6 +29,7 @@ def self.open(path, *args)
when ".tch" then HashDatabase
when ".tcb" then BTreeDatabase
when ".tcf" then FixedLengthDatabase
when ".tct" then TableDatabase
else fail ArgumentError, "unsupported database type"
end
db = db_class.new(path, *args)
Expand Down
27 changes: 27 additions & 0 deletions lib/oklahoma_mixer/hash_map.rb
@@ -0,0 +1,27 @@
module OklahomaMixer
class HashMap # :nodoc:
def initialize(pointer = C.new)
@pointer = pointer
end

attr_reader :pointer

def update(pairs)
pairs.each do |key, value|
C.put(@pointer, *[yield(key), yield(value)].flatten)
end
end

def each
C.iterinit(@pointer)
loop do
return self unless key = C.read_from_func(:iternext, :no_free, @pointer)
yield [key, C.read_from_func(:get, :no_free, @pointer, key, key.size)]
end
end

def free
C.del(@pointer)
end
end
end
23 changes: 23 additions & 0 deletions lib/oklahoma_mixer/hash_map/c.rb
@@ -0,0 +1,23 @@
module OklahomaMixer
class HashMap
module C # :nodoc:
extend OklahomaMixer::Utilities::FFIDSL

prefix :tcmap

def_new_and_del_funcs

func :name => :put,
:args => [:pointer, :pointer, :int, :pointer, :int]
func :name => :get,
:args => [:pointer, :pointer, :int, :pointer],
:returns => :pointer

func :name => :iterinit,
:args => :pointer
func :name => :iternext,
:args => [:pointer, :pointer],
:returns => :pointer
end
end
end
103 changes: 103 additions & 0 deletions lib/oklahoma_mixer/table_database.rb
@@ -0,0 +1,103 @@
module OklahomaMixer
class TableDatabase < HashDatabase
################################
### Getting and Setting Keys ###
################################

def store(key, value, mode = nil, &dup_handler)
if mode == :add and dup_handler.nil?
super
else
Utilities.temp_map do |map|
map.update(value) { |key_or_value|
cast_to_bytes_and_length(key_or_value)
}
result = super(key, map, mode, &dup_handler)
result == map ? value : result
end
end
end
alias_method :[]=, :store

def fetch(key, *default)
if value = try( :get, cast_key_in(key),
:failure => lambda { |value| value.address.zero? },
:no_error => {22 => nil} )
cast_value_out(value)
else
if block_given?
warn "block supersedes default value argument" unless default.empty?
yield key
elsif not default.empty?
default.first
else
fail IndexError, "key not found"
end
end
end
#################
### Iteration ###
#################

def each
try(:iterinit)
loop do
begin
pointer = try( :iternext3,
:failure => lambda { |value| value.address.zero? },
:no_error => {22 => nil} )
return self unless pointer
map = HashMap.new(pointer)
key, value = nil, { }
map.each do |k, v|
if k.empty?
key = v
else
value[k] = v
end
end
yield [key, value]
ensure
map.free if map
end
end
end
alias_method :each_pair, :each

#######
private
#######

def tune(options)
super
if options.values_at(:bnum, :apow, :fpow, :opts).any?
optimize(options.merge(:tune => true))
end
if options.values_at(:rcnum, :lcnum, :ncnum).any?
setcache(options)
end
end

def setcache(options)
try( :setcache,
options.fetch(:rcnum, 0).to_i,
options.fetch(:lcnum, 0).to_i,
options.fetch(:ncnum, 0).to_i )
end

def cast_value_in(value)
value.pointer
end

def cast_value_out(pointer)
map = HashMap.new(pointer)
hash = { }
map.each do |key, value|
hash[cast_to_encoded_string(key)] = cast_to_encoded_string(value)
end
hash
ensure
map.free if map
end
end
end
85 changes: 85 additions & 0 deletions lib/oklahoma_mixer/table_database/c.rb
@@ -0,0 +1,85 @@
module OklahomaMixer
class TableDatabase < HashDatabase
module C # :nodoc:
extend OklahomaMixer::Utilities::FFIDSL

prefix :tctdb

const :OPTS, %w[TLARGE TDEFLATE TBZIP TTCBS]
INDEXES = enum :TDBITLEXICAL, 0,
:TDBITDECIMAL, 1,
:TDBITTOKEN, 2,
:TDBITQGRAM, 3,
:TDBITOPT, 9998,
:TDBITVOID, 9999,
:TDBITKEEP, 1 << 24

def_core_database_consts_and_funcs

func :name => :tune,
:args => [:pointer, :int64, :int8, :int8, OPTS],
:returns => :bool
func :name => :setcache,
:args => [:pointer, :int32, :int32, :int32],
:returns => :bool
func :name => :setxmsiz,
:args => [:pointer, :int64],
:returns => :bool
func :name => :setdfunit,
:args => [:pointer, :int32],
:returns => :bool
func :name => :optimize,
:args => [:pointer, :int64, :int8, :int8, OPTS],
:returns => :bool

func :name => :put,
:args => [:pointer, :pointer, :int, :pointer],
:returns => :bool
func :name => :putkeep,
:args => [:pointer, :pointer, :int, :pointer],
:returns => :bool
func :name => :putcat,
:args => [:pointer, :pointer, :int, :pointer],
:returns => :bool
call :name => :TCPDPROC,
:args => [:pointer, :int, :pointer, :pointer],
:returns => :pointer
func :name => :putproc,
:args => [:pointer, :pointer, :int, :pointer, :int, :TCPDPROC,
:pointer],
:returns => :bool
func :name => :addint,
:args => [:pointer, :pointer, :int, :int],
:returns => :int
func :name => :adddouble,
:args => [:pointer, :pointer, :int, :double],
:returns => :double
func :name => :out,
:args => [:pointer, :pointer, :int],
:returns => :bool
func :name => :get,
:args => [:pointer, :pointer, :int],
:returns => :pointer
func :name => :fwmkeys,
:args => [:pointer, :pointer, :int, :int],
:returns => :pointer
func :name => :genuid,
:args => :pointer,
:returns => :int64

func :name => :iterinit,
:args => :pointer,
:returns => :bool
func :name => :iternext,
:args => [:pointer, :pointer],
:returns => :pointer
func :name => :iternext3,
:args => :pointer,
:returns => :pointer

func :name => :setindex,
:args => [:pointer, :string, INDEXES],
:returns => :bool
end
end
end
11 changes: 10 additions & 1 deletion lib/oklahoma_mixer/utilities.rb
Expand Up @@ -38,13 +38,15 @@ def func(details)
end

def read_from_func(func, *args)
no_free = args.shift if args.first.is_a?(Symbol) and
args.first == :no_free
Utilities.temp_int do |size|
begin
args << size
pointer = send(func, *args)
pointer.address.zero? ? nil : pointer.get_bytes(0, size.get_int(0))
ensure
Utilities.free(pointer) if pointer
Utilities.free(pointer) if pointer and not no_free
end
end
end
Expand Down Expand Up @@ -186,6 +188,13 @@ def self.temp_list(size)
ensure
list.free if list
end

def self.temp_map
map = HashMap.new
yield map
ensure
map.free if map
end

extend FFIDSL

Expand Down
22 changes: 22 additions & 0 deletions test/document_iteration_test.rb
@@ -0,0 +1,22 @@
require "test_helper"
require "shared_iteration"

class TestDocumentIteration < Test::Unit::TestCase
def setup
@db = tdb
@keys = %w[a b c]
@values = @keys.map { |key|
Hash[*@keys.map { |k| [key + k, "abc".index(k).to_s] }.flatten]
}
@keys.zip(@values) do |key, value|
@db[key] = value
end
end

def teardown
@db.close
remove_db_files
end

include SharedIteration
end

0 comments on commit ff5a2b8

Please sign in to comment.