google_hash 0.4.0 → 0.5.1
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/VERSION +1 -1
- data/ext/extconf.rb +23 -21
- data/ext/go.bat +4 -1
- data/ext/template/google_hash.cpp.erb +45 -15
- data/ext/template/main.cpp.erb +0 -1
- data/spec/spec.google_hash.rb +73 -10
- metadata +3 -7
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.5.1
|
data/ext/extconf.rb
CHANGED
|
@@ -34,49 +34,51 @@ else
|
|
|
34
34
|
unreachable_int = 63
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
ruby_key = {:convert_keys_from_ruby => "", :convert_keys_to_ruby => "", :key_type => "VALUE", :
|
|
38
|
-
|
|
39
|
-
# i.e. long is basically broken till we do
|
|
40
|
-
#long_key = {:assert_key_type => 'T_FIXNUM', :convert_keys_from_ruby => "FIX2LONG",
|
|
41
|
-
#:convert_keys_to_ruby => "LONG2FIX", :key_type => "long", :unreachable_key => "1<<#{unreachable_int}"}
|
|
37
|
+
ruby_key = {:convert_keys_from_ruby => "", :convert_keys_to_ruby => "", :key_type => "VALUE", :english_key_type => "ruby",
|
|
38
|
+
:extra_hash_params => ", hashrb, eqrb", :unreachable_key => "current_instance"} # TODO NULL is false here?
|
|
42
39
|
int_key = {:assert_key_type => 'T_FIXNUM', :convert_keys_from_ruby => "FIX2INT",
|
|
43
|
-
:convert_keys_to_ruby => "INT2FIX", :key_type => "int", :unreachable_key => "1<<#{unreachable_int}"}
|
|
40
|
+
:convert_keys_to_ruby => "INT2FIX", :key_type => "int", :unreachable_key => "1<<#{unreachable_int}"}
|
|
41
|
+
# "long" is not useful to us since it's the same as int...
|
|
42
|
+
bignum_key = {:assert_key_type => ['T_BIGNUM', 'T_FIXNUM'], :convert_keys_from_ruby => "rb_big2dbl",
|
|
43
|
+
:convert_keys_to_ruby => "rb_dbl2big", :key_type => "double", :unreachable_key => "1<<#{unreachable_int}", # LODO is this a bignum value though?
|
|
44
|
+
:extra_hash_params => ", hashdouble, eqdouble",
|
|
45
|
+
:extra_set_code => "if(TYPE(set_this) == T_FIXNUM)\nset_this = rb_int2big(set_this);",
|
|
46
|
+
:extra_get_code => "if(TYPE(get_this) == T_FIXNUM) \n get_this = rb_int2big(get_this);"
|
|
47
|
+
} # fixnum's can never be zero...
|
|
44
48
|
|
|
45
49
|
|
|
46
|
-
ruby_value =
|
|
47
|
-
#long_value = {:assert_value_type => 'T_FIXNUM', :convert_values_from_ruby => "FIX2LONG",
|
|
48
|
-
#:convert_values_to_ruby => "LONG2FIX", :value_type => "long"}
|
|
50
|
+
ruby_value = {:value_type => "VALUE", :english_value_type => "ruby"}
|
|
49
51
|
int_value = {:assert_value_type => 'T_FIXNUM', :convert_values_from_ruby => "FIX2INT",
|
|
50
|
-
:convert_values_to_ruby => "INT2FIX", :value_type => "int"}
|
|
52
|
+
:convert_values_to_ruby => "INT2FIX", :value_type => "int"}
|
|
53
|
+
|
|
54
|
+
#bignum_value = {:assert_value_type => 'T_BIGNUM', :convert_values_from_ruby => "rb_big2dbl",
|
|
55
|
+
#:convert_values_to_ruby => "rb_dbl2big", :value_type => "double", }
|
|
51
56
|
|
|
52
57
|
init_funcs = []
|
|
53
58
|
|
|
54
|
-
for key in [ruby_key, int_key] do
|
|
59
|
+
for key in [ruby_key, int_key, bignum_key] do
|
|
55
60
|
for value in [ruby_value, int_value] do
|
|
56
61
|
options = key.merge(value)
|
|
57
62
|
for type in ['sparse', 'dense'] do
|
|
58
63
|
|
|
59
|
-
|
|
60
64
|
# create local variables so that the template can look cleaner
|
|
61
65
|
unreachable_key = options[:unreachable_key]
|
|
62
66
|
convert_keys_from_ruby = options[:convert_keys_from_ruby]
|
|
63
67
|
convert_keys_to_ruby = options[:convert_keys_to_ruby]
|
|
64
68
|
key_type = options[:key_type]
|
|
65
69
|
value_type = options[:value_type]
|
|
66
|
-
english_key_type = options[:
|
|
67
|
-
english_value_type = options[:
|
|
70
|
+
english_key_type = options[:english_key_type] || options[:key_type]
|
|
71
|
+
english_value_type = options[:english_value_type] || options[:value_type]
|
|
68
72
|
|
|
69
|
-
|
|
73
|
+
|
|
74
|
+
assert_key_type = [options[:assert_key_type]].flatten[0]
|
|
75
|
+
assert_key_type2 = [options[:assert_key_type]].flatten[1]
|
|
70
76
|
convert_values_from_ruby = options[:convert_values_from_ruby]
|
|
71
77
|
convert_values_to_ruby = options[:convert_values_to_ruby]
|
|
72
78
|
assert_value_type = options[:assert_value_type]
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
else
|
|
77
|
-
extra_hash_params = nil
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
+
extra_hash_params = options[:extra_hash_params]
|
|
81
|
+
|
|
80
82
|
template = ERB.new(File.read('template/google_hash.cpp.erb'))
|
|
81
83
|
descriptor = type + '_' + english_key_type + '_to_' + english_value_type;
|
|
82
84
|
File.write(descriptor + '.cpp', template.result(binding))
|
data/ext/go.bat
CHANGED
|
@@ -11,6 +11,7 @@ using std::endl;
|
|
|
11
11
|
#include <ext/hash_set>
|
|
12
12
|
<% end %>
|
|
13
13
|
using __gnu_cxx::hash; // or __gnu_cxx::hash, or maybe tr1::hash, depending on your OS
|
|
14
|
+
|
|
14
15
|
extern "C" {
|
|
15
16
|
|
|
16
17
|
// some helpers
|
|
@@ -40,6 +41,37 @@ struct eqint
|
|
|
40
41
|
|
|
41
42
|
static ID id_eql, id_hash;
|
|
42
43
|
|
|
44
|
+
<% if key_type == 'double' %>
|
|
45
|
+
|
|
46
|
+
struct eqdouble
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
|
|
50
|
+
bool operator()(const double &f1, const double &f2) const
|
|
51
|
+
|
|
52
|
+
{
|
|
53
|
+
|
|
54
|
+
return f1 == f2;
|
|
55
|
+
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
extern hash<int> inthash;
|
|
62
|
+
|
|
63
|
+
struct hashdouble {
|
|
64
|
+
|
|
65
|
+
size_t operator ()(const double& foo) const {
|
|
66
|
+
|
|
67
|
+
return inthash((int)foo);
|
|
68
|
+
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
<% end %>
|
|
74
|
+
|
|
43
75
|
<% if key_type == 'VALUE' %>
|
|
44
76
|
static hash<const char*> H;
|
|
45
77
|
|
|
@@ -103,33 +135,27 @@ struct hashrb
|
|
|
103
135
|
switch (TYPE(hval)) {
|
|
104
136
|
case T_FIXNUM:
|
|
105
137
|
return hval;
|
|
106
|
-
<% unless RUBY_VERSION < '1.9' %>
|
|
107
138
|
case T_BIGNUM:
|
|
108
139
|
return LONG2FIX(((long*)(RBIGNUM_DIGITS(hval)))[0]);
|
|
109
|
-
<% end %>
|
|
110
|
-
|
|
111
140
|
default:
|
|
112
141
|
hval = rb_to_int(hval);
|
|
113
142
|
goto retry;
|
|
114
|
-
|
|
115
|
-
|
|
143
|
+
}
|
|
116
144
|
}
|
|
117
145
|
};
|
|
118
146
|
|
|
119
147
|
<% end %>
|
|
120
148
|
|
|
121
|
-
/* we end up not needing this...or at least not using it, I don't know if it would be faster than using the default or not
|
|
122
|
-
*/
|
|
123
|
-
|
|
124
149
|
|
|
125
150
|
typedef struct {
|
|
126
151
|
<%= type %>_hash_map< <%= key_type %>, <%= value_type %> <%= extra_hash_params %> > *hash_map;
|
|
127
152
|
} RCallback;
|
|
128
153
|
|
|
129
|
-
|
|
130
154
|
static void mark_hash_map_values(RCallback *incoming) {
|
|
131
155
|
<% if value_type == 'VALUE' || key_type == 'VALUE' %>
|
|
132
|
-
|
|
156
|
+
|
|
157
|
+
// only need to iterate if we store *real* Ruby values
|
|
158
|
+
|
|
133
159
|
for(<%= type %>_hash_map< <%= key_type %>, <%= value_type %> <%= extra_hash_params %> >::iterator it = incoming->hash_map->begin(); it != incoming->hash_map->end(); ++it) {
|
|
134
160
|
|
|
135
161
|
<% if value_type == 'VALUE' %>
|
|
@@ -180,16 +206,18 @@ rb_mri_hash_new(VALUE freshly_created) {
|
|
|
180
206
|
static VALUE rb_ghash_set(VALUE cb, VALUE set_this, VALUE to_this) {
|
|
181
207
|
<% if assert_key_type %>
|
|
182
208
|
if(!(TYPE(set_this) == <%= assert_key_type %>)) {
|
|
183
|
-
|
|
209
|
+
<%= "if(!(TYPE(set_this) == #{assert_key_type2}))" if assert_key_type2 %>
|
|
210
|
+
rb_raise(rb_eTypeError, "not valid key (expected <%= assert_key_type %>)");
|
|
184
211
|
}
|
|
185
212
|
<% end %>
|
|
186
213
|
|
|
187
214
|
<% if assert_value_type %>
|
|
188
215
|
if(!(TYPE(to_this) == <%= assert_value_type %>)) {
|
|
189
|
-
rb_raise(rb_eTypeError, "not valid value
|
|
216
|
+
rb_raise(rb_eTypeError, "not valid value <%=assert_value_type%>");
|
|
190
217
|
}
|
|
191
218
|
<% end %>
|
|
192
219
|
|
|
220
|
+
<%= options[:extra_set_code] %>
|
|
193
221
|
|
|
194
222
|
RCallback* cbs = GetCallbackStruct(cb);
|
|
195
223
|
(*cbs->hash_map)[ <%= convert_keys_from_ruby %>(set_this)] = <%= convert_values_from_ruby %>(to_this);
|
|
@@ -197,13 +225,15 @@ static VALUE rb_ghash_set(VALUE cb, VALUE set_this, VALUE to_this) {
|
|
|
197
225
|
}
|
|
198
226
|
|
|
199
227
|
static VALUE rb_ghash_get(VALUE cb, VALUE get_this) {
|
|
200
|
-
// TODO optionally not assert [?]
|
|
228
|
+
// TODO optionally not type check assert anywhere [?]
|
|
201
229
|
<% if assert_key_type %>
|
|
202
230
|
if(!(TYPE(get_this) == <%= assert_key_type %>)) {
|
|
203
|
-
|
|
231
|
+
<%= "if(!(TYPE(get_this) == #{assert_key_type2}))" if assert_key_type2 %>
|
|
232
|
+
rb_raise(rb_eTypeError, "not valid get key (expected <%=assert_key_type%>)");
|
|
204
233
|
}
|
|
205
234
|
<% end %>
|
|
206
235
|
RCallback* cbs = GetCallbackStruct(cb);
|
|
236
|
+
<%= options[:extra_get_code] %>
|
|
207
237
|
|
|
208
238
|
<%= type %>_hash_map< <%= key_type %>, <%= value_type %> <%= extra_hash_params %> >::iterator out = cbs->hash_map->find(<%= convert_keys_from_ruby %>(get_this));
|
|
209
239
|
|
|
@@ -241,7 +271,7 @@ static VALUE rb_ghash_keys(VALUE cb) {
|
|
|
241
271
|
}
|
|
242
272
|
|
|
243
273
|
|
|
244
|
-
// only yields for now :)
|
|
274
|
+
// only does yields (blocks) for now :)
|
|
245
275
|
|
|
246
276
|
static VALUE rb_ghash_combination_2(VALUE cb) {
|
|
247
277
|
RCallback* incoming = GetCallbackStruct(cb);
|
data/ext/template/main.cpp.erb
CHANGED
data/spec/spec.google_hash.rb
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
require 'rubygems' if RUBY_VERSION < '1.9'
|
|
2
2
|
require 'sane'
|
|
3
3
|
require_relative '../ext/google_hash.so'
|
|
4
|
-
|
|
4
|
+
begin
|
|
5
|
+
require 'spec/autorun'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rspec' # rspec2
|
|
8
|
+
end
|
|
5
9
|
|
|
6
10
|
describe "google_hash" do
|
|
7
11
|
|
|
8
12
|
before do
|
|
9
|
-
@subject =
|
|
13
|
+
@subject = GoogleHashSparseIntToRuby.new
|
|
10
14
|
end
|
|
11
15
|
|
|
12
16
|
it "should be instantiable" do
|
|
@@ -141,7 +145,6 @@ describe "google_hash" do
|
|
|
141
145
|
assert sum == 1 + 2 + 1 + 3 + 2 + 3
|
|
142
146
|
end
|
|
143
147
|
|
|
144
|
-
|
|
145
148
|
it "should have an Array for values, keys" do
|
|
146
149
|
@subject[33] = 34
|
|
147
150
|
@subject.keys.should == [33]
|
|
@@ -149,22 +152,82 @@ describe "google_hash" do
|
|
|
149
152
|
end
|
|
150
153
|
|
|
151
154
|
it "should work with all Longs" do
|
|
152
|
-
a =
|
|
155
|
+
a = GoogleHashDenseIntToInt.new
|
|
153
156
|
a[3] = 4
|
|
154
157
|
a[3].should == 4
|
|
155
158
|
end
|
|
156
159
|
|
|
157
160
|
it "should raise on errant values" do
|
|
158
|
-
a =
|
|
161
|
+
a = GoogleHashDenseIntToInt.new
|
|
159
162
|
proc { a[3] = 'abc'}.should raise_error
|
|
160
|
-
end
|
|
163
|
+
end
|
|
161
164
|
|
|
162
|
-
it "should
|
|
165
|
+
it "should do bignum values as doubles" do
|
|
166
|
+
a = GoogleHashDenseDoubleToInt.new
|
|
167
|
+
a[10000000000000000000] = 1
|
|
168
|
+
a[10000000000000000000].should == 1
|
|
169
|
+
end
|
|
163
170
|
|
|
164
|
-
it "should
|
|
171
|
+
it "should not leak" do
|
|
172
|
+
a = GoogleHashDenseIntToInt.new
|
|
173
|
+
100_000.times {
|
|
174
|
+
a[1] = 1
|
|
175
|
+
a[1]
|
|
176
|
+
a.each{|k, v|}
|
|
177
|
+
a.delete(1) rescue nil
|
|
178
|
+
}
|
|
179
|
+
OS.rss_bytes.should be < 25_000_000
|
|
180
|
+
end
|
|
165
181
|
|
|
166
|
-
it "should
|
|
182
|
+
it "should do int values as doubles" do
|
|
183
|
+
a = GoogleHashDenseDoubleToInt.new
|
|
184
|
+
a[1] = 1
|
|
185
|
+
a[1].should == 1
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
it "should do float values as doubles" do
|
|
189
|
+
a = GoogleHashDenseDoubleToInt.new
|
|
190
|
+
a[1.0] = 1
|
|
191
|
+
a[1.0].should == 1
|
|
192
|
+
end
|
|
167
193
|
|
|
168
|
-
it "should
|
|
194
|
+
it "should do bignum to doubles et al" do
|
|
195
|
+
pending
|
|
196
|
+
a = GoogleHashDenseDoubleToDouble.new
|
|
197
|
+
a[10000000000000000000] = 1
|
|
198
|
+
a[10000000000000000000].should == 1
|
|
199
|
+
a[1] = 10000000000000000000
|
|
200
|
+
a[1].should == 10000000000000000000
|
|
201
|
+
a[10000000000000000000] = 10000000000000000000
|
|
202
|
+
a[10000000000000000000].should == 10000000000000000000
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
it "should have really real bignums" do
|
|
206
|
+
pending
|
|
207
|
+
fail 'same as above plus'
|
|
208
|
+
a = GoogleHashDenseBignumToRuby.new
|
|
209
|
+
a[10000000000000000000] = 'abc'
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
it 'should be able to delete bignums without leaking' do
|
|
213
|
+
pending
|
|
214
|
+
a = GoogleHashDenseBignumToBignum.new
|
|
215
|
+
100_000.times {
|
|
216
|
+
a[10000000000000000000] = 1
|
|
217
|
+
a.size.should == 1
|
|
218
|
+
a.delete[10000000000000000000]
|
|
219
|
+
a.size.should == 0
|
|
220
|
+
}
|
|
221
|
+
assert OS.rss_bytes < 100_000
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
it "should have an Enumerator for values, keys, an on demand, getNext enumerator object..."
|
|
225
|
+
|
|
226
|
+
it "should have a block access for values, keys"
|
|
227
|
+
|
|
228
|
+
it "should have sets, too, not just hashes"
|
|
229
|
+
|
|
230
|
+
it "should skip GC when native to native" do
|
|
231
|
+
end
|
|
169
232
|
|
|
170
233
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: google_hash
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
hash: 15
|
|
5
4
|
prerelease: false
|
|
6
5
|
segments:
|
|
7
6
|
- 0
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
version: 0.
|
|
7
|
+
- 5
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.5.1
|
|
11
10
|
platform: ruby
|
|
12
11
|
authors:
|
|
13
12
|
- rogerdpack
|
|
@@ -26,7 +25,6 @@ dependencies:
|
|
|
26
25
|
requirements:
|
|
27
26
|
- - ">="
|
|
28
27
|
- !ruby/object:Gem::Version
|
|
29
|
-
hash: 3
|
|
30
28
|
segments:
|
|
31
29
|
- 0
|
|
32
30
|
version: "0"
|
|
@@ -144,7 +142,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
144
142
|
requirements:
|
|
145
143
|
- - ">="
|
|
146
144
|
- !ruby/object:Gem::Version
|
|
147
|
-
hash: 3
|
|
148
145
|
segments:
|
|
149
146
|
- 0
|
|
150
147
|
version: "0"
|
|
@@ -153,7 +150,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
153
150
|
requirements:
|
|
154
151
|
- - ">="
|
|
155
152
|
- !ruby/object:Gem::Version
|
|
156
|
-
hash: 3
|
|
157
153
|
segments:
|
|
158
154
|
- 0
|
|
159
155
|
version: "0"
|