rufus-tokyo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.txt +8 -0
- data/CREDITS.txt +8 -0
- data/README.txt +118 -0
- data/lib/rufus-tokyo.rb +3 -0
- data/lib/rufus/tokyo.rb +243 -0
- data/test/cabinet_0_test.rb +44 -0
- data/test/mem.rb +49 -0
- data/test/test.rb +5 -0
- data/test/test_base.rb +6 -0
- metadata +73 -0
data/CHANGELOG.txt
ADDED
data/CREDITS.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
|
2
|
+
= rufus-tokyo
|
3
|
+
|
4
|
+
ruby-ffi based interface to Tokyo Cabinet.
|
5
|
+
|
6
|
+
It focuses on the abstract API for now (http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi)
|
7
|
+
|
8
|
+
|
9
|
+
== installation
|
10
|
+
|
11
|
+
sudo gem install ffi rufus-tokyo
|
12
|
+
|
13
|
+
(see after 'usage' for how to install Tokyo Cabinet if required)
|
14
|
+
|
15
|
+
|
16
|
+
== usage
|
17
|
+
|
18
|
+
to create a hash (file named 'data.tch')
|
19
|
+
|
20
|
+
require 'rubygems'
|
21
|
+
require 'rufus/tokyo'
|
22
|
+
|
23
|
+
db = Rufus::Tokyo::Cabinet.new('data.tch')
|
24
|
+
|
25
|
+
db['nada'] = 'surf'
|
26
|
+
|
27
|
+
p db['nada'] # => 'surf'
|
28
|
+
p db['lost'] # => nil
|
29
|
+
|
30
|
+
5000.times { |i| db[i.to_s] = "x" }
|
31
|
+
|
32
|
+
p db.inject { |r, (k, v)| k } # => 4999
|
33
|
+
|
34
|
+
db.close
|
35
|
+
|
36
|
+
|
37
|
+
more in the rdoc
|
38
|
+
|
39
|
+
http://rufus.rubyforge.org/rufus-tokyo/
|
40
|
+
http://rufus.rubyforge.org/rufus-tokyo/classes/Rufus/Tokyo/Cabinet.html
|
41
|
+
|
42
|
+
or directly in the source
|
43
|
+
|
44
|
+
http://github.com/jmettraux/rufus-tokyo/blob/master/lib/rufus/tokyo.rb
|
45
|
+
|
46
|
+
|
47
|
+
== Tokyo Cabinet install
|
48
|
+
|
49
|
+
On a Mac, you would do
|
50
|
+
|
51
|
+
sudo port install tokyocabinet
|
52
|
+
|
53
|
+
|
54
|
+
If you don't have Tokyo Cabinet on your system, you can get it and compile it :
|
55
|
+
|
56
|
+
git clone git://github.com/etrepum/tokyo-cabinet.git
|
57
|
+
cd tokyo-cabinet
|
58
|
+
git checkout 1.4.1
|
59
|
+
./configure
|
60
|
+
make
|
61
|
+
|
62
|
+
eventually
|
63
|
+
|
64
|
+
sudo make install
|
65
|
+
|
66
|
+
|
67
|
+
== dependencies
|
68
|
+
|
69
|
+
the ruby gem ffi
|
70
|
+
|
71
|
+
|
72
|
+
== mailing list
|
73
|
+
|
74
|
+
On the rufus-ruby list[http://groups.google.com/group/rufus-ruby] :
|
75
|
+
|
76
|
+
http://groups.google.com/group/rufus-ruby
|
77
|
+
|
78
|
+
|
79
|
+
== issue tracker
|
80
|
+
|
81
|
+
http://rubyforge.org/tracker/?atid=18584&group_id=4812&func=browse
|
82
|
+
|
83
|
+
|
84
|
+
== irc
|
85
|
+
|
86
|
+
irc.freenode.net #ruote
|
87
|
+
|
88
|
+
|
89
|
+
== source
|
90
|
+
|
91
|
+
http://github.com/jmettraux/rufus-tokyo
|
92
|
+
|
93
|
+
git clone git://github.com/jmettraux/rufus-tokyo.git
|
94
|
+
|
95
|
+
|
96
|
+
== credits
|
97
|
+
|
98
|
+
many thanks to the authors of Tokyo Cabinet and to the authors of ruby-ffi
|
99
|
+
|
100
|
+
http://tokyocabinet.sourceforge.net
|
101
|
+
http://kenai.com/projects/ruby-ffi
|
102
|
+
|
103
|
+
|
104
|
+
== author
|
105
|
+
|
106
|
+
John Mettraux, jmettraux@gmail.com
|
107
|
+
http://jmettraux.wordpress.com
|
108
|
+
|
109
|
+
|
110
|
+
== the rest of Rufus
|
111
|
+
|
112
|
+
http://rufus.rubyforge.org
|
113
|
+
|
114
|
+
|
115
|
+
== license
|
116
|
+
|
117
|
+
MIT
|
118
|
+
|
data/lib/rufus-tokyo.rb
ADDED
data/lib/rufus/tokyo.rb
ADDED
@@ -0,0 +1,243 @@
|
|
1
|
+
#
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2009, John Mettraux, jmettraux@gmail.com
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
#
|
24
|
+
|
25
|
+
#
|
26
|
+
# "made in Japan"
|
27
|
+
#
|
28
|
+
# jmettraux@gmail.com
|
29
|
+
#
|
30
|
+
|
31
|
+
require 'rubygems'
|
32
|
+
require 'ffi'
|
33
|
+
|
34
|
+
module Rufus
|
35
|
+
module Tokyo
|
36
|
+
|
37
|
+
VERSION = '0.1.0'
|
38
|
+
|
39
|
+
module Func #:nodoc#
|
40
|
+
extend FFI::Library
|
41
|
+
|
42
|
+
#
|
43
|
+
# find Tokyo Cabinet lib
|
44
|
+
|
45
|
+
paths = Array(ENV['TOKYO_CABINET_LIB'] || %w{
|
46
|
+
/opt/local/lib/libtokyocabinet.dylib
|
47
|
+
/usr/local/lib/libtokyocabinet.dylib
|
48
|
+
/usr/local/lib/libtokyocabinet.so
|
49
|
+
})
|
50
|
+
|
51
|
+
paths.each do |path|
|
52
|
+
if File.exist?(path)
|
53
|
+
ffi_lib(path)
|
54
|
+
@lib = path
|
55
|
+
break
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
attach_function :tcadbnew, [], :pointer
|
60
|
+
|
61
|
+
attach_function :tcadbopen, [ :pointer, :string ], :int
|
62
|
+
attach_function :tcadbclose, [ :pointer ], :int
|
63
|
+
|
64
|
+
attach_function :tcadbdel, [ :pointer ], :void
|
65
|
+
|
66
|
+
attach_function :tcadbrnum, [ :pointer ], :uint64
|
67
|
+
attach_function :tcadbsize, [ :pointer ], :uint64
|
68
|
+
|
69
|
+
attach_function :tcadbput2, [ :pointer, :string, :string ], :int
|
70
|
+
attach_function :tcadbget2, [ :pointer, :string ], :string
|
71
|
+
attach_function :tcadbout2, [ :pointer, :string ], :int
|
72
|
+
|
73
|
+
attach_function :tcadbiterinit, [ :pointer ], :int
|
74
|
+
attach_function :tcadbiternext2, [ :pointer ], :string
|
75
|
+
|
76
|
+
#def self.method_missing (m, *args)
|
77
|
+
# mm = "tcadb#{m}"
|
78
|
+
# self.respond_to?(mm) ? self.send(mm, *args) : super
|
79
|
+
#end
|
80
|
+
#
|
81
|
+
# makes JRuby unhappy, forget it.
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Returns the path to the Tokyo Cabinet dynamic library currently in use
|
86
|
+
#
|
87
|
+
def self.lib
|
88
|
+
Rufus::Tokyo::Func.instance_variable_get(:@lib)
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# A 'cabinet', ie a Tokyo Cabinet database.
|
93
|
+
#
|
94
|
+
# Follows the abstract API described at :
|
95
|
+
#
|
96
|
+
# http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
|
97
|
+
#
|
98
|
+
# An usage example :
|
99
|
+
#
|
100
|
+
# db = Rufus::Tokyo::Cabinet.new('test_data.tch')
|
101
|
+
# db['pillow'] = 'Shonagon'
|
102
|
+
#
|
103
|
+
# db.size # => 1
|
104
|
+
# db['pillow'] # => 'Shonagon'
|
105
|
+
#
|
106
|
+
# db.delete('pillow') # => 'Shonagon'
|
107
|
+
# db.size # => 0
|
108
|
+
#
|
109
|
+
# db.close
|
110
|
+
#
|
111
|
+
class Cabinet
|
112
|
+
include Enumerable
|
113
|
+
|
114
|
+
#
|
115
|
+
# Creates/opens the cabinet, raises an exception in case of
|
116
|
+
# creation/opening failure.
|
117
|
+
#
|
118
|
+
# This method accepts a 'name' parameter and an optional 'params' hash
|
119
|
+
# parameter.
|
120
|
+
#
|
121
|
+
# 'name' follows the syntax described at
|
122
|
+
#
|
123
|
+
# http://tokyocabinet.sourceforge.net/spex-en.html#tcadbapi
|
124
|
+
#
|
125
|
+
# under tcadbopen(). For example :
|
126
|
+
#
|
127
|
+
# db = Rufus::Tokyo::Cabinet.new('casket.tch#bnum=100000#opts=ld')
|
128
|
+
#
|
129
|
+
# will open (eventually create) a hash database backed in the file
|
130
|
+
# 'casket.tch' with a bucket number of 100000 and the 'large' and
|
131
|
+
# 'deflate' options (opts) turned on.
|
132
|
+
#
|
133
|
+
# == :hash or :tree
|
134
|
+
#
|
135
|
+
# Setting the name to :hash or :tree simply will create a in-memory hash
|
136
|
+
# or tree respectively (see #new_tree and #new_hash).
|
137
|
+
#
|
138
|
+
# == tuning parameters
|
139
|
+
#
|
140
|
+
# It's ok to use the optional params hash to pass tuning parameters and
|
141
|
+
# options, thus
|
142
|
+
#
|
143
|
+
# db = Rufus::Tokyo::Cabinet.new('casket.tch#bnum=100000#opts=ld')
|
144
|
+
#
|
145
|
+
# and
|
146
|
+
#
|
147
|
+
# db = Rufus::Tokyo::Cabinet.new(
|
148
|
+
# 'casket.tch', :bnum => 100000, :opts => 'ld')
|
149
|
+
#
|
150
|
+
# are equivalent.
|
151
|
+
#
|
152
|
+
# == mode
|
153
|
+
#
|
154
|
+
# To open a db in read-only mode :
|
155
|
+
#
|
156
|
+
# db = Rufus::Tokyo::Cabinet.new('casket.tch#mode=r')
|
157
|
+
# db = Rufus::Tokyo::Cabinet.new('casket.tch', :mode => 'r')
|
158
|
+
#
|
159
|
+
def initialize (name, params={})
|
160
|
+
|
161
|
+
@db = Rufus::Tokyo::Func.tcadbnew
|
162
|
+
|
163
|
+
name = '*' if name == :hash # in memory hash database
|
164
|
+
name = '+' if name == :tree # in memory B+ tree database
|
165
|
+
|
166
|
+
name = name + params.collect { |k, v| "##{k}=#{v}" }.join('')
|
167
|
+
|
168
|
+
(Rufus::Tokyo::Func.tcadbopen(@db, name) == 1) ||
|
169
|
+
raise("failed to open/create db '#{name}'")
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# Returns a new in-memory hash. Accepts the same optional params hash
|
174
|
+
# as new().
|
175
|
+
#
|
176
|
+
def self.new_hash (params={})
|
177
|
+
self.new(:hash, params)
|
178
|
+
end
|
179
|
+
|
180
|
+
#
|
181
|
+
# Returns a new in-memory B+ tree. Accepts the same optional params hash
|
182
|
+
# as new().
|
183
|
+
#
|
184
|
+
def self.new_tree (params={})
|
185
|
+
self.new(:tree, params)
|
186
|
+
end
|
187
|
+
|
188
|
+
def []= (k, v)
|
189
|
+
Rufus::Tokyo::Func.tcadbput2(@db, k, v)
|
190
|
+
end
|
191
|
+
|
192
|
+
def [] (k)
|
193
|
+
Rufus::Tokyo::Func.tcadbget2(@db, k) rescue nil
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# Removes a record from the cabinet, returns the value if successful
|
198
|
+
# else nil.
|
199
|
+
#
|
200
|
+
def delete (k)
|
201
|
+
v = self[k]
|
202
|
+
(Rufus::Tokyo::Func.tcadbout2(@db, k) == 1) ? v : nil
|
203
|
+
end
|
204
|
+
|
205
|
+
#
|
206
|
+
# Returns the number of records in the 'cabinet'
|
207
|
+
#
|
208
|
+
def size
|
209
|
+
Rufus::Tokyo::Func.tcadbrnum(@db)
|
210
|
+
end
|
211
|
+
|
212
|
+
#
|
213
|
+
# Returns the 'weight' of the db (in bytes)
|
214
|
+
#
|
215
|
+
def weight
|
216
|
+
Rufus::Tokyo::Func.tcadbsize(@db)
|
217
|
+
end
|
218
|
+
|
219
|
+
#
|
220
|
+
# Closes the cabinet (and frees the datastructure allocated for it),
|
221
|
+
# returns true in case of success.
|
222
|
+
#
|
223
|
+
def close
|
224
|
+
result = Rufus::Tokyo::Func.tcadbclose(@db)
|
225
|
+
Rufus::Tokyo::Func.tcadbdel(@db)
|
226
|
+
(result == 1)
|
227
|
+
end
|
228
|
+
|
229
|
+
#
|
230
|
+
# The classical Ruby each (unlocks the power of the Enumerable mixin)
|
231
|
+
#
|
232
|
+
def each
|
233
|
+
|
234
|
+
Rufus::Tokyo::Func.tcadbiterinit(@db) # concurrent access ??
|
235
|
+
|
236
|
+
while (k = (Rufus::Tokyo::Func.tcadbiternext2(@db) rescue nil))
|
237
|
+
yield(k, self[k])
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Testing rufus-tokyo
|
4
|
+
#
|
5
|
+
# Fri Jan 23 08:31:01 JST 2009
|
6
|
+
#
|
7
|
+
|
8
|
+
require File.dirname(__FILE__) + '/test_base'
|
9
|
+
|
10
|
+
require 'test/unit'
|
11
|
+
|
12
|
+
require 'rufus/tokyo'
|
13
|
+
|
14
|
+
|
15
|
+
class CabinetZero < Test::Unit::TestCase
|
16
|
+
|
17
|
+
#def setup
|
18
|
+
#end
|
19
|
+
#def teardown
|
20
|
+
#end
|
21
|
+
|
22
|
+
def test_lib
|
23
|
+
|
24
|
+
assert_not_nil Rufus::Tokyo.lib
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_basic_workflow
|
28
|
+
|
29
|
+
db = Rufus::Tokyo::Cabinet.new('test_data.tch')
|
30
|
+
db['pillow'] = 'Shonagon'
|
31
|
+
|
32
|
+
assert_equal 1, db.size
|
33
|
+
assert_equal 'Shonagon', db['pillow']
|
34
|
+
|
35
|
+
assert_equal 'Shonagon', db.delete('pillow')
|
36
|
+
assert_equal 0, db.size
|
37
|
+
|
38
|
+
assert db.close
|
39
|
+
end
|
40
|
+
|
41
|
+
# TODO : memory tests
|
42
|
+
|
43
|
+
end
|
44
|
+
|
data/test/mem.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
%w{ lib test }.each do |path|
|
3
|
+
path = File.expand_path(File.dirname(__FILE__) + '/../' + path)
|
4
|
+
$: << path unless $:.include?(path)
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
|
10
|
+
def self_ps
|
11
|
+
ps = `ps -v #{$$}`.split("\n").last.split(' ')
|
12
|
+
%w{
|
13
|
+
pid stat time sl re pagein vsz rss lim tsiz pcpu pmem command
|
14
|
+
}.inject({}) { |h, k|
|
15
|
+
h[k.intern] = ps.shift; h
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def pmem (msg)
|
20
|
+
p [ msg, "#{self_ps[:vsz].to_i / 1024}k" ]
|
21
|
+
end
|
22
|
+
|
23
|
+
pmem 'starting'
|
24
|
+
|
25
|
+
require 'rubygems'
|
26
|
+
require 'rufus/tokyo'
|
27
|
+
|
28
|
+
FileUtils.rm('test_mem.tch')
|
29
|
+
|
30
|
+
db = Rufus::Tokyo::Cabinet.new('test_mem.tch')
|
31
|
+
|
32
|
+
pmem 'wired to db'
|
33
|
+
|
34
|
+
500_000.times { |i| db[i.to_s] = "value#{i}" }
|
35
|
+
|
36
|
+
pmem 'loaded 500_000 records'
|
37
|
+
|
38
|
+
db.each { |k, v| k }
|
39
|
+
|
40
|
+
pmem 'iterated 500_000 records'
|
41
|
+
|
42
|
+
a = db.collect { |k, v| k + v }
|
43
|
+
|
44
|
+
pmem 'collected 500_000 records'
|
45
|
+
|
46
|
+
db.close
|
47
|
+
|
48
|
+
pmem 'closed db'
|
49
|
+
|
data/test/test.rb
ADDED
data/test/test_base.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rufus-tokyo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- John Mettraux
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-01-23 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ruby-ffi
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: jmettraux@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.txt
|
33
|
+
- CHANGELOG.txt
|
34
|
+
- CREDITS.txt
|
35
|
+
files:
|
36
|
+
- lib/rufus
|
37
|
+
- lib/rufus/tokyo.rb
|
38
|
+
- lib/rufus-tokyo.rb
|
39
|
+
- test/cabinet_0_test.rb
|
40
|
+
- test/mem.rb
|
41
|
+
- test/test.rb
|
42
|
+
- test/test_base.rb
|
43
|
+
- README.txt
|
44
|
+
- CHANGELOG.txt
|
45
|
+
- CREDITS.txt
|
46
|
+
has_rdoc: true
|
47
|
+
homepage: http://rufus.rubyforge.org/
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: "0"
|
58
|
+
version:
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
requirements:
|
66
|
+
- ruby-ffi
|
67
|
+
rubyforge_project: rufus
|
68
|
+
rubygems_version: 1.3.1
|
69
|
+
signing_key:
|
70
|
+
specification_version: 2
|
71
|
+
summary: ruby-ffi based lib to access Tokyo Cabinet
|
72
|
+
test_files:
|
73
|
+
- test/test.rb
|