localmemcache 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,61 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['.', '..'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'bacon'
5
+ require 'lmctestapi'
6
+ $a = Alloc.new(1000)
7
+
8
+ Bacon.summary_on_exit
9
+
10
+ describe 'Alloc' do
11
+
12
+ it 'should support basic allocation and freeing of memory' do
13
+ $a.dispose($a.get(100))
14
+ end
15
+
16
+ it 'should merge free chunks' do
17
+ free = $a.free_mem
18
+ v1 = $a.get(100)
19
+ $a.free_mem.should.equal(free - 108)
20
+ v2 = $a.get(100)
21
+ $a.free_mem.should.equal(free - 216)
22
+ $a.free_chunks.should.equal 1
23
+ $a.dispose(v1)
24
+ $a.free_mem.should.equal(free - 108)
25
+ $a.free_chunks.should.equal 2
26
+ $a.dispose(v2)
27
+ $a.free_mem.should.equal free
28
+ $a.free_chunks.should.equal 1
29
+ end
30
+
31
+ it 'should handle memory exhaustion' do
32
+ v1 = $a.get($a.free_mem)
33
+ $a.free_mem.should.equal 0
34
+ $a.free_mem.should.equal 0
35
+ $a.free_mem.should.equal 0
36
+ should.raise(OutOfMemoryError) { $a.get(1) }
37
+ $a.dispose(v1)
38
+ end
39
+
40
+ it 'should manage free memory only if there is enough mem left to hold the struct ' do
41
+ $a.free_chunks.should.equal 1
42
+ v1 = $a.get($a.free_mem - 100)
43
+ $a.free_chunks.should.equal 1
44
+ v2 = $a.get(90)
45
+ $a.dump
46
+ $a.free_chunks.should.equal 0
47
+ $a.dispose(v1)
48
+ $a.dispose(v2)
49
+ end
50
+
51
+ it 'should deal with calling free for already freed items' do
52
+ v = $a.get(90)
53
+ $a.dispose(v)
54
+ $a.dispose(v)
55
+ end
56
+
57
+ it 'should deal with calling free with nasty parameters' do
58
+ $a.dispose(0)
59
+ end
60
+
61
+ end
@@ -0,0 +1,301 @@
1
+ # Bacon -- small RSpec clone.
2
+ #
3
+ # "Truth will sooner come out from error than from confusion." ---Francis Bacon
4
+
5
+ # Copyright (C) 2007, 2008 Christian Neukirchen <purl.org/net/chneukirchen>
6
+ #
7
+ # Bacon is freely distributable under the terms of an MIT-style license.
8
+ # See COPYING or http://www.opensource.org/licenses/mit-license.php.
9
+
10
+ module Bacon
11
+ VERSION = "0.9"
12
+
13
+ Counter = Hash.new(0)
14
+ ErrorLog = ""
15
+ Shared = Hash.new { |_, name|
16
+ raise NameError, "no such context: #{name.inspect}"
17
+ }
18
+
19
+ RestrictName = // unless defined? RestrictName
20
+ RestrictContext = // unless defined? RestrictContext
21
+
22
+ def self.summary_on_exit
23
+ return if Counter[:installed_summary] > 0
24
+ at_exit {
25
+ handle_summary
26
+ if $!
27
+ raise $!
28
+ elsif Counter[:errors] + Counter[:failed] > 0
29
+ exit 1
30
+ end
31
+ }
32
+ Counter[:installed_summary] += 1
33
+ end
34
+
35
+ module SpecDoxOutput
36
+ def handle_specification(name)
37
+ puts name
38
+ yield
39
+ puts
40
+ end
41
+
42
+ def handle_requirement(description)
43
+ print "- #{description}"
44
+ error = yield
45
+ puts error.empty? ? "" : " [#{error}]"
46
+ end
47
+
48
+ def handle_summary
49
+ print ErrorLog
50
+ puts "%d specifications (%d requirements), %d failures, %d errors" %
51
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
52
+ end
53
+ end
54
+
55
+ module TestUnitOutput
56
+ def handle_specification(name) yield end
57
+
58
+ def handle_requirement(description)
59
+ error = yield
60
+ if error.empty?
61
+ print "."
62
+ else
63
+ print error[0..0]
64
+ end
65
+ end
66
+
67
+ def handle_summary
68
+ puts "", ErrorLog
69
+ puts "%d tests, %d assertions, %d failures, %d errors" %
70
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
71
+ end
72
+ end
73
+
74
+ module TapOutput
75
+ def handle_specification(name) yield end
76
+
77
+ def handle_requirement(description)
78
+ ErrorLog.replace ""
79
+ error = yield
80
+ if error.empty?
81
+ printf "ok %-3d - %s\n" % [Counter[:specifications], description]
82
+ else
83
+ printf "not ok %d - %s: %s\n" %
84
+ [Counter[:specifications], description, error]
85
+ puts ErrorLog.strip.gsub(/^/, '# ')
86
+ end
87
+ end
88
+
89
+ def handle_summary
90
+ puts "1..#{Counter[:specifications]}"
91
+ puts "# %d tests, %d assertions, %d failures, %d errors" %
92
+ Counter.values_at(:specifications, :requirements, :failed, :errors)
93
+ end
94
+ end
95
+
96
+ extend SpecDoxOutput # default
97
+
98
+ class Error < RuntimeError
99
+ attr_accessor :count_as
100
+
101
+ def initialize(count_as, message)
102
+ @count_as = count_as
103
+ super message
104
+ end
105
+ end
106
+
107
+ class Context
108
+ def initialize(name, &block)
109
+ @name = name
110
+ @before, @after = [], []
111
+
112
+ return unless name =~ RestrictContext
113
+ Bacon.handle_specification(name) { instance_eval(&block) }
114
+ end
115
+
116
+ def before(&block); @before << block; end
117
+ def after(&block); @after << block; end
118
+
119
+ def behaves_like(*names)
120
+ names.each { |name| instance_eval(&Shared[name]) }
121
+ end
122
+
123
+ def it(description, &block)
124
+ return unless description =~ RestrictName
125
+ Counter[:specifications] += 1
126
+ run_requirement description, block
127
+ end
128
+
129
+ def run_requirement(description, spec)
130
+ Bacon.handle_requirement description do
131
+ begin
132
+ Counter[:depth] += 1
133
+ @before.each { |block| instance_eval(&block) }
134
+ instance_eval(&spec)
135
+ rescue Object => e
136
+ ErrorLog << "#{e.class}: #{e.message}\n"
137
+ e.backtrace.find_all { |line| line !~ /bin\/bacon|\/bacon\.rb:\d+/ }.
138
+ each_with_index { |line, i|
139
+ ErrorLog << "\t#{line}#{i==0 ? ": #@name - #{description}" : ""}\n"
140
+ }
141
+ ErrorLog << "\n"
142
+
143
+ if e.kind_of? Error
144
+ Counter[e.count_as] += 1
145
+ e.count_as.to_s.upcase
146
+ else
147
+ Counter[:errors] += 1
148
+ "ERROR: #{e.class}"
149
+ end
150
+ else
151
+ ""
152
+ ensure
153
+ begin
154
+ @after.each { |block| instance_eval(&block) }
155
+ rescue
156
+ end
157
+ Counter[:depth] -= 1
158
+ end
159
+ end
160
+ end
161
+
162
+ def raise?(*args, &block); block.raise?(*args); end
163
+ def throw?(*args, &block); block.throw?(*args); end
164
+ def change?(*args, &block); block.change?(*args); end
165
+ end
166
+ end
167
+
168
+
169
+ class Object
170
+ def true?; false; end
171
+ def false?; false; end
172
+ end
173
+
174
+ class TrueClass
175
+ def true?; true; end
176
+ end
177
+
178
+ class FalseClass
179
+ def false?; true; end
180
+ end
181
+
182
+ class Proc
183
+ def raise?(*exceptions)
184
+ exceptions << RuntimeError if exceptions.empty?
185
+ call
186
+
187
+ # Only to work in 1.9.0, rescue with splat doesn't work there right now
188
+ rescue Object => e
189
+ case e
190
+ when *exceptions
191
+ e
192
+ else
193
+ raise e
194
+ end
195
+ else
196
+ false
197
+ end
198
+
199
+ def throw?(sym)
200
+ catch(sym) {
201
+ call
202
+ return false
203
+ }
204
+ return true
205
+ end
206
+
207
+ def change?
208
+ pre_result = yield
209
+ called = call
210
+ post_result = yield
211
+ pre_result != post_result
212
+ end
213
+ end
214
+
215
+ class Numeric
216
+ def close?(to, delta)
217
+ (to.to_f - self).abs <= delta.to_f rescue false
218
+ end
219
+ end
220
+
221
+
222
+ class Object
223
+ def should(*args, &block) Should.new(self).be(*args, &block) end
224
+ end
225
+
226
+ module Kernel
227
+ private
228
+
229
+ def describe(name, &block) Bacon::Context.new(name, &block) end
230
+ def shared(name, &block) Bacon::Shared[name] = block end
231
+ end
232
+
233
+
234
+ class Should
235
+ # Kills ==, ===, =~, eql?, equal?, frozen?, instance_of?, is_a?,
236
+ # kind_of?, nil?, respond_to?, tainted?
237
+ instance_methods.each { |method|
238
+ undef_method method if method =~ /\?|^\W+$/
239
+ }
240
+
241
+ def initialize(object)
242
+ @object = object
243
+ @negated = false
244
+ end
245
+
246
+ def not(*args, &block)
247
+ @negated = !@negated
248
+
249
+ if args.empty?
250
+ self
251
+ else
252
+ be(*args, &block)
253
+ end
254
+ end
255
+
256
+ def be(*args, &block)
257
+ if args.empty?
258
+ self
259
+ else
260
+ block = args.shift unless block_given?
261
+ satisfy(*args, &block)
262
+ end
263
+ end
264
+
265
+ alias a be
266
+ alias an be
267
+
268
+ def satisfy(*args, &block)
269
+ if args.size == 1 && String === args.first
270
+ description = args.shift
271
+ else
272
+ description = ""
273
+ end
274
+
275
+ r = yield(@object, *args)
276
+ if Bacon::Counter[:depth] > 0
277
+ raise Bacon::Error.new(:failed, description) unless @negated ^ r
278
+ Bacon::Counter[:requirements] += 1
279
+ end
280
+ @negated ^ r ? r : false
281
+ end
282
+
283
+ def method_missing(name, *args, &block)
284
+ name = "#{name}?" if name.to_s =~ /\w[^?]\z/
285
+
286
+ desc = @negated ? "not " : ""
287
+ desc << @object.inspect << "." << name.to_s
288
+ desc << "(" << args.map{|x|x.inspect}.join(", ") << ") failed"
289
+
290
+ satisfy(desc) { |x| x.__send__(name, *args, &block) }
291
+ end
292
+
293
+ def equal(value) self == value end
294
+ def match(value) self =~ value end
295
+ def identical_to(value) self.equal? value end
296
+ alias same_as identical_to
297
+
298
+ def flunk(reason="Flunked")
299
+ raise Bacon::Error.new(:failed, reason)
300
+ end
301
+ end
data/src/tests/bench ADDED
@@ -0,0 +1,11 @@
1
+ #! /bin/sh
2
+ D=`dirname $0`
3
+ DIR=`cd $D; pwd`
4
+ script=$DIR/bench.rb
5
+
6
+ if test "x$1" = "x-d"; then
7
+ irb -r $script
8
+ else
9
+ #valgrind --leak-check=full --tool=memcheck ruby $script
10
+ ruby $script
11
+ fi
@@ -0,0 +1,46 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'bacon'
5
+ require 'localmemcache'
6
+
7
+ Bacon.summary_on_exit
8
+
9
+ LocalMemCache.clear_namespace("speed-comparison");
10
+ $lm2 = LocalMemCache.new :namespace=>"speed-comparison"
11
+
12
+ def compare_speed(n)
13
+
14
+ puts "LocalMemCache"
15
+ measure_time(n) {
16
+ r = rand(10000).to_s
17
+ $lm2.set(r, r)
18
+ $lm2.get(r)
19
+ }
20
+
21
+ puts "builtin"
22
+ $hh = {}
23
+ measure_time(n) {
24
+ r = rand(10000).to_s
25
+ $hh[r] = r
26
+ $hh[r]
27
+ }
28
+ end
29
+
30
+ def measure_time(c, &block)
31
+ _then = Time.now
32
+ c.times { block.call }
33
+ now = Time.now
34
+ puts "#{(now - _then)*1000} ms"
35
+ end
36
+
37
+ compare_speed(2_000_000)
38
+
39
+ #$stdout.write "ht shm setting x 20000: "
40
+ #tmeasure (2_000_000) {
41
+ # v = $lm2.get("f").to_i + 1
42
+ # #puts "v:#{v}"
43
+ # $lm2.set("f", v)
44
+ #}
45
+ #puts "foo: #{$lm2.get("f")}"
46
+
@@ -0,0 +1,14 @@
1
+ require 'mkmf'
2
+
3
+ dir = File.dirname(__FILE__)
4
+
5
+ $defs << "-DRUBY_VERSION_CODE=#{RUBY_VERSION.gsub(/\D/, '')}"
6
+
7
+ $srcs = ['lmctestapi.c']
8
+ $objs = ['lmctestapi.o']
9
+
10
+ $CFLAGS << " -D_REENTRANT -g -I .."
11
+ $LDFLAGS << " ../liblmc.a -lpthread -lrt"
12
+
13
+ dir_config('lmctestapi')
14
+ create_makefile('lmctestapi')
data/src/tests/lmc ADDED
@@ -0,0 +1,11 @@
1
+ #! /bin/sh
2
+ D=`dirname $0`
3
+ DIR=`cd $D; pwd`
4
+ script=$DIR/lmc.rb
5
+
6
+ if test "x$1" = "x-d"; then
7
+ irb -r $script
8
+ else
9
+ #valgrind --leak-check=full --tool=memcheck ruby $script
10
+ ruby $script
11
+ fi
data/src/tests/lmc.rb ADDED
@@ -0,0 +1,85 @@
1
+ $DIR=File.dirname(__FILE__)
2
+ ['.', '..', '../ruby-binding/'].each {|p| $:.unshift File.join($DIR, p) }
3
+
4
+ require 'bacon'
5
+ require 'localmemcache'
6
+
7
+ Bacon.summary_on_exit
8
+
9
+ $lm = LocalMemCache.new :namespace=>"testing"
10
+
11
+ describe 'LocalMemCache' do
12
+
13
+ it 'should allow to set and query keys' do
14
+ $lm.get("non-existant").should.be.nil
15
+ $lm.set("foo", "1")
16
+ $lm.get("foo").should.equal "1"
17
+ end
18
+
19
+ it 'should support the [] and []= operators' do
20
+ $lm["boo"] = "2"
21
+ $lm["boo"].should.equal "2"
22
+ end
23
+
24
+ it 'should allow deletion of keys' do
25
+ $lm["deleteme"] = "blah"
26
+ $lm["deleteme"].should.not.be.nil
27
+ $lm.delete("deleteme")
28
+ $lm["deleteme"].should.be.nil
29
+ $lm.delete("non-existant")
30
+ end
31
+
32
+ #it 'should support iteration' do
33
+ # puts "TBD"
34
+ #end
35
+
36
+ it 'should support clearing of namespaces' do
37
+ LocalMemCache.clear_namespace("testing");
38
+ end
39
+
40
+ end
41
+
42
+ def tmeasure(c, &block)
43
+ _then = Time.now
44
+ c.times { block.call }
45
+ now = Time.now
46
+ puts "#{(now - _then)*1000} ms"
47
+ end
48
+
49
+ $lm2 = LocalMemCache.new :namespace=>"speed-comparison"
50
+
51
+ def compare_speed(n)
52
+
53
+ puts "LocalMemCache"
54
+ tmeasure(n) {
55
+ r = rand(10000).to_s
56
+ # $lm2.get(r)
57
+ $lm2.set(r, r)
58
+ # nr = $lm2.get(r)
59
+ # if nr != r
60
+ # $stderr.puts "FAILED: #{nr.inspect} != #{r.inspect}"
61
+ # end
62
+ }
63
+
64
+ puts "builtin"
65
+ $hh = {}
66
+ tmeasure(n) {
67
+ r = rand(10000).to_s
68
+ # $hh[r]
69
+ $hh[r] = r
70
+ # if $hh[r] != r
71
+ # $stderr.puts "FAILED!"
72
+ # end
73
+ }
74
+ end
75
+
76
+ compare_speed(2_000_000)
77
+
78
+ #$stdout.write "ht shm setting x 20000: "
79
+ #tmeasure (2_000_000) {
80
+ # v = $lm2.get("f").to_i + 1
81
+ # #puts "v:#{v}"
82
+ # $lm2.set("f", v)
83
+ #}
84
+ #puts "foo: #{$lm2.get("f")}"
85
+
@@ -0,0 +1,162 @@
1
+ /*
2
+ * Copyright (C) 2009, Sven C. Koehler
3
+ */
4
+
5
+ #include <ruby.h>
6
+ #include "lmc_shm.h"
7
+ #include "lmc_hashtable.h"
8
+ #include "lmc_valloc.h"
9
+
10
+ void *memp = NULL;
11
+ static VALUE OutOfMemoryError;
12
+
13
+ long long_value(VALUE i) { return NUM2LONG(rb_Integer(i)); }
14
+ VALUE num2string(long i) { return rb_big2str(rb_int2big(i), 10); }
15
+ char *rstring_ptr(VALUE s) {
16
+ char* r = NIL_P(s) ? "nil" : RSTRING_PTR(rb_String(s));
17
+ return r ? r : "nil";
18
+ }
19
+ static VALUE ruby_string(char *s) { return s ? rb_str_new2(s) : Qnil; }
20
+
21
+ static VALUE Alloc__new(VALUE klass, VALUE size) {
22
+ size_t s = long_value(size);
23
+ memp = malloc(s);
24
+ #ifdef DEBUG_ALLOC
25
+ printf("memp: %0x, end: %0x\n", memp, memp+s);
26
+ memset(memp, 0xF0, s);
27
+ #endif
28
+ lmc_init_memory(memp, s);
29
+ return rb_class_new_instance(0, NULL, klass);
30
+ }
31
+
32
+ static VALUE Alloc__get(VALUE obj, VALUE size) {
33
+ size_t va = lmc_valloc(memp, long_value(size));
34
+ if (!va) { rb_raise(OutOfMemoryError, "Out of memory"); }
35
+ size_t v = long_value(size);
36
+ #ifdef DEBUG_ALLOC
37
+ memset(memp + va, va, v);
38
+ #endif
39
+ return rb_int2big(va);
40
+ }
41
+
42
+ static VALUE Alloc__dispose(VALUE obj, VALUE adr) {
43
+ lmc_free(memp, long_value(adr));
44
+ return Qnil;
45
+ }
46
+
47
+ static VALUE Alloc__dump(VALUE obj) {
48
+ lmc_dump(memp);
49
+ return Qnil;
50
+ }
51
+
52
+ static VALUE Alloc__free_mem(VALUE obj) {
53
+ return rb_int2inum(lmc_status(memp, "f").free_mem);
54
+ }
55
+
56
+ static VALUE Alloc__largest_chunk(VALUE obj) {
57
+ return rb_int2inum(lmc_status(memp, "lc").largest_chunk);
58
+ }
59
+
60
+ static VALUE Alloc__free_chunks(VALUE obj) {
61
+ return rb_int2inum(lmc_status(memp, "fc").free_chunks);
62
+ }
63
+
64
+ static VALUE SHMError;
65
+
66
+ static lmc_shm_t* get_Shm(VALUE obj) {
67
+ lmc_shm_t *ls;
68
+ Data_Get_Struct(obj, lmc_shm_t, ls);
69
+ if (ls == NULL) { rb_raise(SHMError, "Not active"); }
70
+ return ls;
71
+ }
72
+
73
+ static VALUE SHM__new(VALUE klass, VALUE namespace) {
74
+ Check_Type(namespace, T_STRING);
75
+ lmc_shm_t *ls = lmc_shm_create("test2", 2000000, 0, 0);
76
+ return Data_Wrap_Struct(klass, NULL, lmc_shm_destroy, ls);
77
+ }
78
+
79
+ static VALUE SHM__close(VALUE obj) {
80
+ lmc_shm_destroy(get_Shm(obj), 0);
81
+ DATA_PTR(obj) = NULL;
82
+ return Qnil;
83
+ }
84
+
85
+ static VALUE SHM__get(VALUE obj, VALUE id) {
86
+ return num2string(((int *)get_Shm(obj)->base)[long_value(id)]);
87
+ }
88
+
89
+ static VALUE SHM__set(VALUE obj, VALUE id, VALUE value) {
90
+ ((int *)get_Shm(obj)->base)[long_value(id)] = long_value(value);
91
+ return Qnil;
92
+ }
93
+
94
+ //typedef struct {
95
+ // va_ht_hash_t va;
96
+ // mem_cache_t *mc;
97
+ // lmc_lock_t *ll;
98
+ //} ht_desc_t;
99
+ //
100
+ //static VALUE Hashtable__new(VALUE klass) {
101
+ // lmc_lock_obtain("Hashtable__new");
102
+ // size_t s = 20401094656;
103
+ // lmc_lock_t *ll = lmc_lock_init("test11");
104
+ // mem_cache_t *mc = local_mem_cache_create(s); // namespace?
105
+ // lmc_init_memory(mc->shm, s);
106
+ // va_ht_hash_t va_ht = ht_hash_create(mc->shm);
107
+ // ht_desc_t* ht = malloc(sizeof(ht_desc_t));
108
+ // ht->va = va_ht;
109
+ // ht->mc = mc;
110
+ // ht->ll = ll;
111
+ // lmc_lock_release("Hashtable__new");
112
+ // return Data_Wrap_Struct(klass, NULL, NULL, ht);
113
+ //}
114
+ //
115
+ //ht_desc_t *get_Hashtable(VALUE obj) {
116
+ // ht_desc_t *ht;
117
+ // Data_Get_Struct(obj, ht_desc_t, ht);
118
+ // return ht;
119
+ //}
120
+ //
121
+ //static VALUE Hashtable__get(VALUE obj, VALUE key) {
122
+ // ht_desc_t *ht = get_Hashtable(obj);
123
+ // return ruby_string(ht_get(ht->mc->shm, ht->va, rstring_ptr(key)));
124
+ //}
125
+ //
126
+ //static VALUE Hashtable__set(VALUE obj, VALUE key, VALUE value) {
127
+ // ht_desc_t *ht = get_Hashtable(obj);
128
+ // ht_set(ht->mc->shm, ht->va, rstring_ptr(key), rstring_ptr(value));
129
+ // return Qnil;
130
+ //}
131
+
132
+ static VALUE Alloc;
133
+ static VALUE SHM;
134
+ //static VALUE Hashtable;
135
+
136
+ #include <stdio.h>
137
+ #include <fcntl.h>
138
+ #include <sys/stat.h>
139
+ #include <sys/mman.h>
140
+
141
+ void Init_lmctestapi() {
142
+ OutOfMemoryError = rb_define_class("OutOfMemoryError", rb_eStandardError);
143
+ Alloc = rb_define_class("Alloc", rb_cObject);
144
+ rb_define_singleton_method(Alloc, "new", Alloc__new, 1);
145
+ rb_define_method(Alloc, "get", Alloc__get, 1);
146
+ rb_define_method(Alloc, "dispose", Alloc__dispose, 1);
147
+ rb_define_method(Alloc, "dump", Alloc__dump, 0);
148
+ rb_define_method(Alloc, "free_mem", Alloc__free_mem, 0);
149
+ rb_define_method(Alloc, "largest_chunk", Alloc__largest_chunk, 0);
150
+ rb_define_method(Alloc, "free_chunks", Alloc__free_chunks, 0);
151
+
152
+ SHM = rb_define_class("SHM", rb_cObject);
153
+ rb_define_singleton_method(SHM, "new", SHM__new, 1);
154
+ rb_define_method(SHM, "get", SHM__get, 1);
155
+ rb_define_method(SHM, "set", SHM__set, 2);
156
+ rb_define_method(SHM, "close", SHM__close, 0);
157
+
158
+ //Hashtable = rb_define_class("Hashtable", rb_cObject);
159
+ //rb_define_singleton_method(Hashtable, "new", Hashtable__new, 0);
160
+ //rb_define_method(Hashtable, "get", Hashtable__get, 1);
161
+ //rb_define_method(Hashtable, "set", Hashtable__set, 2);
162
+ }
@@ -0,0 +1,9 @@
1
+ #! /bin/sh
2
+ cd `dirname $0`
3
+
4
+ test -f Makefile || ruby extconf.rb
5
+ test -f mctestapi.so || make
6
+ make clean
7
+ make
8
+ ./hashtable
9
+