google_hash 0.4.0 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|