methodmissing-hwia 1.0.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/README ADDED
@@ -0,0 +1,4 @@
1
+ Attempt at a zippier HashWithIndifferentAccess for Ruby MRI
2
+ (c) 2009 Lourens Naudé (methodmissing), James Tucker (raggi)
3
+
4
+ Work in progress
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+
5
+ HWIA_ROOT = 'ext/hwia'
6
+
7
+ desc 'Default: test'
8
+ task :default => :test
9
+
10
+ desc 'Run hwia tests.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs = [HWIA_ROOT]
13
+ t.pattern = 'test/test_*.rb'
14
+ t.verbose = true
15
+ end
16
+ task :test => :build
17
+
18
+ namespace :build do
19
+ file "#{HWIA_ROOT}/hwia.c"
20
+ file "#{HWIA_ROOT}/extconf.rb"
21
+ file "#{HWIA_ROOT}/Makefile" => %W(#{HWIA_ROOT}/hwia.c #{HWIA_ROOT}/extconf.rb) do
22
+ Dir.chdir(HWIA_ROOT) do
23
+ ruby 'extconf.rb'
24
+ end
25
+ end
26
+
27
+ desc "generate makefile"
28
+ task :makefile => %W(#{HWIA_ROOT}/Makefile #{HWIA_ROOT}/hwia.c)
29
+
30
+ dlext = Config::CONFIG['DLEXT']
31
+ file "#{HWIA_ROOT}/hwia.#{dlext}" => %W(#{HWIA_ROOT}/Makefile #{HWIA_ROOT}/hwia.c) do
32
+ Dir.chdir(HWIA_ROOT) do
33
+ sh 'make' # TODO - is there a config for which make somewhere?
34
+ end
35
+ end
36
+
37
+ desc "compile hwia extension"
38
+ task :compile => "#{HWIA_ROOT}/hwia.#{dlext}"
39
+
40
+ task :clean do
41
+ Dir.chdir(HWIA_ROOT) do
42
+ sh 'make clean'
43
+ end if File.exists?("#{HWIA_ROOT}/Makefile")
44
+ end
45
+
46
+ CLEAN.include("#{HWIA_ROOT}/Makefile")
47
+ CLEAN.include("#{HWIA_ROOT}/hwia.#{dlext}")
48
+ end
49
+
50
+ task :clean => %w(build:clean)
51
+
52
+ desc "compile"
53
+ task :build => %w(build:compile)
54
+
55
+ task :install do |t|
56
+ Dir.chdir(HWIA_ROOT) do
57
+ sh 'sudo make install'
58
+ end
59
+ end
60
+
61
+ desc "clean build install"
62
+ task :setup => %w(clean build install)
63
+
64
+ desc "run benchmarks"
65
+ task :bench do
66
+ ruby "bench/bench.rb"
67
+ end
68
+ task :bench => :build
data/bench/as_hwia.rb ADDED
@@ -0,0 +1,137 @@
1
+ # This class has dubious semantics and we only have it so that
2
+ # people can write params[:key] instead of params['key']
3
+ # and they get the same value for both keys.
4
+
5
+ module ActiveSupport
6
+ class HashWithIndifferentAccess < Hash
7
+ def initialize(constructor = {})
8
+ if constructor.is_a?(Hash)
9
+ super()
10
+ update(constructor)
11
+ else
12
+ super(constructor)
13
+ end
14
+ end
15
+
16
+ def default(key = nil)
17
+ if key.is_a?(Symbol) && include?(key = key.to_s)
18
+ self[key]
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
25
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
26
+
27
+ # Assigns a new value to the hash:
28
+ #
29
+ # hash = HashWithIndifferentAccess.new
30
+ # hash[:key] = "value"
31
+ #
32
+ def []=(key, value)
33
+ regular_writer(convert_key(key), convert_value(value))
34
+ end
35
+
36
+ # Updates the instantized hash with values from the second:
37
+ #
38
+ # hash_1 = HashWithIndifferentAccess.new
39
+ # hash_1[:key] = "value"
40
+ #
41
+ # hash_2 = HashWithIndifferentAccess.new
42
+ # hash_2[:key] = "New Value!"
43
+ #
44
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
45
+ #
46
+ def update(other_hash)
47
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
48
+ self
49
+ end
50
+
51
+ alias_method :merge!, :update
52
+
53
+ # Checks the hash for a key matching the argument passed in:
54
+ #
55
+ # hash = HashWithIndifferentAccess.new
56
+ # hash["key"] = "value"
57
+ # hash.key? :key # => true
58
+ # hash.key? "key" # => true
59
+ #
60
+ def key?(key)
61
+ super(convert_key(key))
62
+ end
63
+
64
+ alias_method :include?, :key?
65
+ alias_method :has_key?, :key?
66
+ alias_method :member?, :key?
67
+
68
+ # Fetches the value for the specified key, same as doing hash[key]
69
+ def fetch(key, *extras)
70
+ super(convert_key(key), *extras)
71
+ end
72
+
73
+ # Returns an array of the values at the specified indices:
74
+ #
75
+ # hash = HashWithIndifferentAccess.new
76
+ # hash[:a] = "x"
77
+ # hash[:b] = "y"
78
+ # hash.values_at("a", "b") # => ["x", "y"]
79
+ #
80
+ def values_at(*indices)
81
+ indices.collect {|key| self[convert_key(key)]}
82
+ end
83
+
84
+ # Returns an exact copy of the hash.
85
+ def dup
86
+ HashWithIndifferentAccess.new(self)
87
+ end
88
+
89
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
90
+ # Does not overwrite the existing hash.
91
+ def merge(hash)
92
+ self.dup.update(hash)
93
+ end
94
+
95
+ # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
96
+ # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a HashWithDifferentAccess.
97
+ def reverse_merge(other_hash)
98
+ super other_hash.with_indifferent_access
99
+ end
100
+
101
+ def reverse_merge!(other_hash)
102
+ replace(reverse_merge( other_hash ))
103
+ end
104
+
105
+ # Removes a specified key from the hash.
106
+ def delete(key)
107
+ super(convert_key(key))
108
+ end
109
+
110
+ def stringify_keys!; self end
111
+ def symbolize_keys!; self end
112
+ def to_options!; self end
113
+
114
+ # Convert to a Hash with String keys.
115
+ def to_hash
116
+ Hash.new(default).merge!(self)
117
+ end
118
+
119
+ protected
120
+ def convert_key(key)
121
+ key.kind_of?(Symbol) ? key.to_s : key
122
+ end
123
+
124
+ def convert_value(value)
125
+ case value
126
+ when Hash
127
+ value.with_indifferent_access
128
+ when Array
129
+ value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
130
+ else
131
+ value
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
data/bench/bench.rb ADDED
@@ -0,0 +1,40 @@
1
+ require "benchmark"
2
+ $:.unshift "." #ruby 1.9.2
3
+ require File.dirname(__FILE__) + "/../ext/hwia/hwia"
4
+ require File.dirname(__FILE__) + "/as_hwia"
5
+
6
+ STR_HASH = { :a => 1, 'b' => 2 }.strhash
7
+ HWIA_HASH = HashWithIndifferentAccess.new({ :a => 1, 'b' => 2 })
8
+ HASH = { :d => :d, 'e' => :e }
9
+
10
+ TESTS = 10_000
11
+ Benchmark.bmbm do |results|
12
+ results.report("StrHash#[:sym]") { TESTS.times { STR_HASH[:a] } }
13
+ results.report("HashWithIndifferentAccess#[:sym]") { TESTS.times { HWIA_HASH[:a] } }
14
+ results.report("StrHash#['str']") { TESTS.times { STR_HASH['b'] } }
15
+ results.report("HashWithIndifferentAccess#['str]") { TESTS.times { HWIA_HASH['b'] } }
16
+ results.report("StrHash#key?(:sym)") { TESTS.times { STR_HASH.key?(:a) } }
17
+ results.report("HashWithIndifferentAccess#key?(:sym)") { TESTS.times { HWIA_HASH.key?(:a) } }
18
+ results.report("StrHash#key?('str')") { TESTS.times { STR_HASH.key?('a') } }
19
+ results.report("HashWithIndifferentAccess#key?('str')") { TESTS.times { HWIA_HASH.key?('a') } }
20
+ results.report("StrHash#fetch(:sym)") { TESTS.times { STR_HASH.fetch(:a) } }
21
+ results.report("HashWithIndifferentAccess#fetch(:sym)") { TESTS.times { HWIA_HASH.fetch(:a) } }
22
+ results.report("StrHash#fetch('str')") { TESTS.times { STR_HASH.fetch('a') } }
23
+ results.report("HashWithIndifferentAccess#fetch('str')") { TESTS.times { HWIA_HASH.fetch('a') } }
24
+ results.report("StrHash#values_at(:sym)") { TESTS.times { STR_HASH.values_at(:a) } }
25
+ results.report("HashWithIndifferentAccess#values_at(:sym)") { TESTS.times { HWIA_HASH.values_at(:a) } }
26
+ results.report("StrHash#values_at('str')") { TESTS.times { STR_HASH.values_at('a') } }
27
+ results.report("HashWithIndifferentAccess#values_at('str')") { TESTS.times { HWIA_HASH.values_at('a') } }
28
+ results.report("StrHash#['str']=") { TESTS.times { STR_HASH['c'] = :c } }
29
+ results.report("HashWithIndifferentAccess#['str]=") { TESTS.times { HWIA_HASH['c'] = :c } }
30
+ results.report("StrHash#[:sym]=") { TESTS.times { STR_HASH[:c] = :c } }
31
+ results.report("HashWithIndifferentAccess#[:sym]=") { TESTS.times { HWIA_HASH[:c] = :c } }
32
+ results.report("StrHash#update") { TESTS.times { STR_HASH.update(HASH) } }
33
+ results.report("HashWithIndifferentAccess#update") { TESTS.times { HWIA_HASH.update(HASH) } }
34
+ results.report("StrHash#dup") { TESTS.times { STR_HASH.dup } }
35
+ results.report("HashWithIndifferentAccess#dup") { TESTS.times { HWIA_HASH.dup } }
36
+ results.report("StrHash#merge") { TESTS.times { STR_HASH.merge(HASH) } }
37
+ results.report("HashWithIndifferentAccess#merge") { TESTS.times { HWIA_HASH.merge(HASH) } }
38
+ results.report("StrHash#to_hash") { TESTS.times { STR_HASH.to_hash } }
39
+ results.report("HashWithIndifferentAccess#to_hash") { TESTS.times { HWIA_HASH.to_hash } }
40
+ end
@@ -0,0 +1,12 @@
1
+ require 'mkmf'
2
+
3
+ def add_define(name)
4
+ $defs.push("-D#{name}")
5
+ end
6
+
7
+ dir_config('hwia')
8
+
9
+ add_define 'RUBY19' if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
10
+ add_define 'RUBY18' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
11
+
12
+ create_makefile('hwia')
data/ext/hwia/hwia.c ADDED
@@ -0,0 +1,524 @@
1
+ #include "ruby.h"
2
+ #ifdef RUBY19
3
+ #include "ruby/st.h"
4
+ #else
5
+ #include "st.h"
6
+ #endif
7
+
8
+ VALUE rb_cStrHash;
9
+
10
+ static ID id_strhash, id_hash;
11
+ static VALUE hash_format;
12
+
13
+ #ifndef RSTRING_PTR
14
+ #define RSTRING_PTR(obj) RSTRING(obj)->ptr
15
+ #endif
16
+
17
+ #ifndef RSTRING_LEN
18
+ #define RSTRING_LEN(obj) RSTRING(obj)->len
19
+ #endif
20
+
21
+ #ifndef RARRAY_PTR
22
+ #define RARRAY_PTR(obj) RARRAY(obj)->heap.ptr
23
+ #endif
24
+
25
+ #ifndef RARRAY_LEN
26
+ #define RARRAY_LEN(obj) RARRAY(obj)->heap.len
27
+ #endif
28
+
29
+ #ifdef RUBY19
30
+ #define HASH_TBL(obj) RHASH(obj)->ntbl
31
+ #else
32
+ #define HASH_TBL(obj) RHASH(obj)->tbl
33
+ #endif
34
+
35
+ static int
36
+ strhash(register const char *string)
37
+ {
38
+ register int c;
39
+ register int val = 0;
40
+ while ((c = *string++) != '\0') {
41
+ val = val*997 + c;
42
+ }
43
+ return val + (val>>5);
44
+ }
45
+
46
+ int
47
+ rb_sym_strhash(VALUE sym)
48
+ {
49
+ ID id = SYM2ID(sym);
50
+ return strhash((char*)rb_id2name(id));
51
+ }
52
+
53
+ static VALUE
54
+ rb_sym_strhash_m(VALUE sym)
55
+ {
56
+ return INT2FIX(rb_sym_strhash(sym));
57
+ }
58
+
59
+ int
60
+ rb_str_strhash(VALUE str)
61
+ {
62
+ return strhash((char*)RSTRING_PTR(str));
63
+ }
64
+
65
+ static VALUE
66
+ rb_str_strhash_m(VALUE str)
67
+ {
68
+ return INT2FIX(rb_str_strhash(str));
69
+ }
70
+
71
+ int
72
+ rb_strhash_cmp(VALUE s1,VALUE s2)
73
+ {
74
+ int s1_hash = SYMBOL_P(s1) ? rb_sym_strhash(s1) : rb_str_strhash(s1);
75
+ int s2_hash = SYMBOL_P(s2) ? rb_sym_strhash(s2) : rb_str_strhash(s2);
76
+ if (s1_hash == s2_hash) return 0;
77
+ if (s1_hash > s2_hash) return 1;
78
+ return -1;
79
+ }
80
+
81
+ /* hash.c */
82
+ static VALUE
83
+ rb_hash_has_key(hash, key)
84
+ VALUE hash;
85
+ VALUE key;
86
+ {
87
+ #ifdef RUBY19
88
+ if (!RHASH(hash)->ntbl)
89
+ return Qfalse;
90
+ #endif
91
+ if (st_lookup(HASH_TBL(hash), key, 0)) {
92
+ return Qtrue;
93
+ }
94
+ return Qfalse;
95
+ }
96
+
97
+ /* hash.c */
98
+ #ifdef RUBY18
99
+ static VALUE
100
+ eql(VALUE *args)
101
+ {
102
+ return (VALUE)rb_eql(args[0], args[1]);
103
+ }
104
+ #endif
105
+
106
+ /* hash.c */
107
+ static int
108
+ rb_strhash_hash_cmp(VALUE a, VALUE b)
109
+ {
110
+ #ifdef RUBY18
111
+ VALUE args[2];
112
+ #endif
113
+ if (a == b) return 0;
114
+ if (FIXNUM_P(a) && FIXNUM_P(b)) {
115
+ return a != b;
116
+ }
117
+ if (TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString &&
118
+ TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString) {
119
+ return rb_str_cmp(a, b);
120
+ }
121
+ if ((TYPE(a) == T_STRING && RBASIC(a)->klass == rb_cString && SYMBOL_P(b)) || (TYPE(b) == T_STRING && RBASIC(b)->klass == rb_cString && SYMBOL_P(a))) {
122
+ return rb_strhash_cmp(a, b);
123
+ }
124
+ if (a == Qundef || b == Qundef) return -1;
125
+ if (SYMBOL_P(a) && SYMBOL_P(b)) {
126
+ return a != b;
127
+ }
128
+ #ifdef RUBY18
129
+ args[0] = a;
130
+ args[1] = b;
131
+ return !rb_with_disable_interrupt(eql, (VALUE)args);
132
+ #else
133
+ return !rb_eql(a, b);
134
+ #endif
135
+ }
136
+
137
+ VALUE
138
+ rb_strhash(VALUE obj)
139
+ {
140
+ VALUE hval = rb_funcall(obj, id_hash, 0);
141
+ #ifdef RUBY18
142
+ return hval;
143
+ #else
144
+ retry:
145
+ switch (TYPE(hval)) {
146
+ case T_FIXNUM:
147
+ return hval;
148
+
149
+ case T_BIGNUM:
150
+ return LONG2FIX(((long*)(RBIGNUM_DIGITS(hval)))[0]);
151
+
152
+ default:
153
+ hval = rb_to_int(hval);
154
+ goto retry;
155
+ }
156
+ #endif
157
+ }
158
+
159
+ /* hash.c */
160
+ static int
161
+ rb_strhash_hash(VALUE a)
162
+ {
163
+ VALUE hval, hnum;
164
+
165
+ switch (TYPE(a)) {
166
+ case T_FIXNUM:
167
+ #ifdef RUBY18
168
+ return (int)a;
169
+ #else
170
+ hnum = a;
171
+ break;
172
+ #endif
173
+ case T_SYMBOL:
174
+ hnum = rb_sym_strhash_m(a);
175
+ break;
176
+ case T_STRING:
177
+ hnum = rb_str_strhash_m(a);
178
+ break;
179
+
180
+ default:
181
+ hval = rb_hash(a);
182
+ #ifdef RUBY18
183
+ if (!FIXNUM_P(hval)) {
184
+ hval = rb_funcall(hval, '%', 1, hash_format);
185
+ }
186
+ #endif
187
+ hnum = FIX2LONG(hval);
188
+ }
189
+ #ifdef RUBY18
190
+ return (int)hnum;
191
+ #else
192
+ hnum <<= 1;
193
+ return (int)RSHIFT(hnum, 1);
194
+ #endif
195
+ }
196
+
197
+ static struct st_hash_type objstrhash = {
198
+ rb_strhash_hash_cmp,
199
+ rb_strhash_hash,
200
+ };
201
+
202
+ static void
203
+ rb_hash_modify_check(VALUE hash){
204
+ if (OBJ_FROZEN(hash)) rb_error_frozen("hash");
205
+ if (!OBJ_TAINTED(hash) && rb_safe_level() >= 4)
206
+ rb_raise(rb_eSecurityError, "Insecure: can't modify hash");
207
+ }
208
+
209
+ /* hash.c */
210
+ static void
211
+ rb_hash_modify(VALUE hash)
212
+ {
213
+ #ifdef RUBY18
214
+ if (!HASH_TBL(hash)) rb_raise(rb_eTypeError, "uninitialized Hash");
215
+ #endif
216
+ rb_hash_modify_check(hash);
217
+ #ifdef RUBY19
218
+ if (!HASH_TBL(hash)) HASH_TBL(hash) = st_init_table(&objstrhash);
219
+ #endif
220
+ }
221
+
222
+ static VALUE strhash_alloc0 _((VALUE));
223
+ static VALUE strhash_alloc _((VALUE));
224
+ /* hash.c */
225
+ static VALUE
226
+ strhash_alloc0(VALUE klass)
227
+ {
228
+ NEWOBJ(hash, struct RHash);
229
+ OBJSETUP(hash, klass, T_HASH);
230
+
231
+ hash->ifnone = Qnil;
232
+
233
+ return (VALUE)hash;
234
+ }
235
+
236
+ static VALUE
237
+ strhash_alloc(VALUE klass)
238
+ {
239
+ VALUE hash = strhash_alloc0(klass);
240
+
241
+ HASH_TBL(hash) = st_init_table(&objstrhash);
242
+
243
+ return hash;
244
+ }
245
+
246
+ VALUE
247
+ rb_strhash_new()
248
+ {
249
+ return strhash_alloc(rb_cStrHash);
250
+ }
251
+
252
+ static VALUE rb_hash_strhash(VALUE hash);
253
+ static VALUE rb_strhash_convert(VALUE hash, VALUE value);
254
+
255
+ /* hash.c */
256
+ static int
257
+ rb_hash_rehash_i(VALUE key, VALUE value, st_table *tbl)
258
+ {
259
+ if (key != Qundef) st_insert(tbl, key, rb_strhash_convert(Qnil, value));
260
+ return ST_CONTINUE;
261
+ }
262
+
263
+ /* hash.c */
264
+ static VALUE
265
+ rb_strhash_rehash(VALUE hash)
266
+ {
267
+ st_table *tbl;
268
+ #ifdef RUBY19
269
+ if (RHASH(hash)->iter_lev > 0) {
270
+ rb_raise(rb_eRuntimeError, "rehash during iteration");
271
+ }
272
+ rb_hash_modify_check(hash);
273
+ if (!RHASH(hash)->ntbl)
274
+ return hash;
275
+ #endif
276
+ rb_hash_modify(hash);
277
+ tbl = st_init_table_with_size(&objstrhash, HASH_TBL(hash)->num_entries);
278
+ rb_hash_foreach(hash, rb_hash_rehash_i, (st_data_t)tbl);
279
+ st_free_table(HASH_TBL(hash));
280
+ HASH_TBL(hash) = tbl;
281
+
282
+ return hash;
283
+ }
284
+
285
+ /* temp. public API */
286
+ static VALUE
287
+ rb_strhash_convert(VALUE hash, VALUE val)
288
+ {
289
+ int i;
290
+ VALUE values;
291
+
292
+ switch (TYPE(val)) {
293
+ case T_HASH:
294
+ return rb_hash_strhash(val);
295
+ case T_ARRAY:
296
+ values = rb_ary_new2(RARRAY_LEN(val));
297
+ for (i = 0; i < RARRAY_LEN(val); i++) {
298
+ VALUE el = RARRAY_PTR(val)[i];
299
+ rb_ary_push(values, (TYPE(el) == T_HASH) ? rb_hash_strhash(el) : el);
300
+ }
301
+ return values;
302
+ default:
303
+ return val;
304
+ }
305
+ }
306
+
307
+ static VALUE
308
+ rb_strhash_aset(VALUE hash, VALUE key, VALUE val){
309
+ VALUE converted = rb_strhash_convert(hash,val);
310
+ rb_hash_aset(hash, key, converted);
311
+ return converted;
312
+ }
313
+
314
+ /* hash.c */
315
+ static VALUE
316
+ rb_strhash_s_create(int argc, VALUE *argv, VALUE klass)
317
+ {
318
+ VALUE hash;
319
+ int i;
320
+
321
+ if (argc == 1 && TYPE(argv[0]) == T_HASH) {
322
+ hash = strhash_alloc0(klass);
323
+ HASH_TBL(hash) = st_copy(HASH_TBL(argv[0]));
324
+ HASH_TBL(hash)->type = &objstrhash;
325
+ RHASH(hash)->ifnone = RHASH(argv[0])->ifnone;
326
+ return rb_strhash_rehash(hash);
327
+ }
328
+
329
+ if (argc % 2 != 0) {
330
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
331
+ }
332
+
333
+ hash = strhash_alloc(klass);
334
+ for (i=0; i<argc; i+=2) {
335
+ rb_strhash_aset(hash, argv[i], argv[i + 1]);
336
+ }
337
+
338
+ return hash;
339
+ }
340
+
341
+ static VALUE
342
+ rb_strhash_strhash(VALUE hash)
343
+ {
344
+ return hash;
345
+ }
346
+
347
+ static VALUE
348
+ rb_hash_strhash(VALUE hash)
349
+ {
350
+ VALUE args[1];
351
+ args[0] = hash;
352
+ return rb_strhash_s_create(1, (VALUE *)args, rb_cStrHash);
353
+ }
354
+
355
+ /* hash.c */
356
+ static VALUE
357
+ to_strhash(hash)
358
+ VALUE hash;
359
+ {
360
+ return rb_convert_type(hash, T_HASH, "StrHash", "to_hash");
361
+ }
362
+
363
+ /* hash.c */
364
+ static int
365
+ rb_strhash_update_i(VALUE key, VALUE value, VALUE hash)
366
+ {
367
+ if (key == Qundef) return ST_CONTINUE;
368
+ st_insert(HASH_TBL(hash), key, rb_strhash_convert(hash,value));
369
+ return ST_CONTINUE;
370
+ }
371
+
372
+ /* hash.c */
373
+ static int
374
+ rb_strhash_update_block_i(VALUE key, VALUE value, VALUE hash)
375
+ {
376
+ if (key == Qundef) return ST_CONTINUE;
377
+ if (rb_hash_has_key(hash, key)) {
378
+ value = rb_yield_values(3, key, rb_hash_aref(hash, key), value);
379
+ }
380
+ st_insert(HASH_TBL(hash), key, rb_strhash_convert(hash,value));
381
+ return ST_CONTINUE;
382
+ }
383
+
384
+ /* hash.c */
385
+ static VALUE
386
+ rb_strhash_update(VALUE hash1, VALUE hash2)
387
+ {
388
+ #ifdef RUBY19
389
+ rb_hash_modify(hash1);
390
+ #endif
391
+ hash2 = to_strhash(hash2);
392
+ if (rb_block_given_p()) {
393
+ rb_hash_foreach(hash2, rb_strhash_update_block_i, hash1);
394
+ }
395
+ else {
396
+ rb_hash_foreach(hash2, rb_strhash_update_i, hash1);
397
+ }
398
+ return hash1;
399
+ }
400
+
401
+ static VALUE
402
+ rb_strhash_initialize(int argc, VALUE *argv, VALUE hash){
403
+ VALUE constructor;
404
+ rb_scan_args(argc, argv, "01", &constructor);
405
+ if(TYPE(constructor) == T_HASH){
406
+ return rb_strhash_update(hash,constructor);
407
+ }else{
408
+ return rb_call_super(argc,argv);
409
+ }
410
+ }
411
+
412
+ static VALUE
413
+ rb_strhash_merge(VALUE hash1, VALUE hash2){
414
+ /* see note in Init */
415
+ VALUE duped = rb_hash_strhash(hash1);
416
+ return rb_strhash_update(hash1,hash2);
417
+ }
418
+
419
+ /*hash.c, see rb_strhash_to_hash*/
420
+ static VALUE
421
+ to_hash(hash)
422
+ VALUE hash;
423
+ {
424
+ return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
425
+ }
426
+
427
+ /*hash.c, see rb_strhash_to_hash*/
428
+ static int
429
+ rb_hash_update_i(key, value, hash)
430
+ VALUE key, value;
431
+ VALUE hash;
432
+ {
433
+ if (key == Qundef) return ST_CONTINUE;
434
+ #ifdef RUBY19
435
+ st_insert(RHASH(hash)->ntbl, key, value);
436
+ #else
437
+ rb_hash_aset(hash, key, value);
438
+ #endif
439
+ return ST_CONTINUE;
440
+ }
441
+
442
+ /*hash.c, see rb_strhash_to_hash*/
443
+ static int
444
+ rb_hash_update_block_i(key, value, hash)
445
+ VALUE key, value;
446
+ VALUE hash;
447
+ {
448
+ if (key == Qundef) return ST_CONTINUE;
449
+ if (rb_hash_has_key(hash, key)) {
450
+ value = rb_yield_values(3, key, rb_hash_aref(hash, key), value);
451
+ }
452
+ #ifdef RUBY19
453
+ st_insert(RHASH(hash)->ntbl, key, value);
454
+ #else
455
+ rb_hash_aset(hash, key, value);
456
+ #endif
457
+ return ST_CONTINUE;
458
+ }
459
+
460
+ /*hash.c, see rb_strhash_to_hash*/
461
+ static VALUE
462
+ rb_hash_update(hash1, hash2)
463
+ VALUE hash1, hash2;
464
+ {
465
+ #ifdef RUBY19
466
+ rb_hash_modify(hash1);
467
+ #endif
468
+ hash2 = to_hash(hash2);
469
+ if (rb_block_given_p()) {
470
+ rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
471
+ }
472
+ else {
473
+ rb_hash_foreach(hash2, rb_hash_update_i, hash1);
474
+ }
475
+ return hash1;
476
+ }
477
+
478
+ static int
479
+ rb_strhash_to_hash_i(VALUE key, VALUE value, VALUE hash){
480
+ if (SYMBOL_P(key)){
481
+ rb_hash_delete(hash,key);
482
+ rb_hash_aset(hash, rb_obj_as_string(key), value);
483
+ }
484
+ return ST_CONTINUE;
485
+ }
486
+
487
+ static VALUE
488
+ rb_strhash_to_hash(VALUE hash){
489
+ VALUE hsh = rb_hash_update(rb_hash_new(), hash);
490
+ RHASH(hsh)->ifnone = RHASH(hash)->ifnone;
491
+ rb_hash_foreach(hsh, rb_strhash_to_hash_i, hsh);
492
+ return hsh;
493
+ }
494
+
495
+ void
496
+ Init_hwia()
497
+ {
498
+ id_hash = rb_intern("hash");
499
+ id_strhash = rb_intern("strhash");
500
+ hash_format = INT2FIX(536870923);
501
+
502
+ rb_cStrHash = rb_define_class("StrHash", rb_cHash);
503
+
504
+ rb_undef_alloc_func(rb_cStrHash);
505
+ rb_define_alloc_func(rb_cStrHash, strhash_alloc);
506
+ rb_define_singleton_method(rb_cStrHash, "[]", rb_strhash_s_create, -1);
507
+
508
+ rb_define_method(rb_cString, "strhash", rb_str_strhash_m, 0);
509
+ rb_define_method(rb_cSymbol, "strhash", rb_sym_strhash_m, 0);
510
+
511
+ rb_define_method(rb_cStrHash,"initialize", rb_strhash_initialize, -1);
512
+ rb_define_method(rb_cStrHash, "rehash", rb_strhash_rehash, 0);
513
+ /* revist, same API, but may be clobbered */
514
+ rb_define_method(rb_cStrHash, "dup", rb_hash_strhash, 0);
515
+ rb_define_method(rb_cStrHash, "strhash", rb_strhash_strhash, 0);
516
+ rb_define_method(rb_cStrHash, "convert", rb_strhash_convert, 1);
517
+ rb_define_method(rb_cStrHash, "[]=", rb_strhash_aset, 2);
518
+ rb_define_method(rb_cStrHash, "store", rb_strhash_aset, 2);
519
+ rb_define_method(rb_cStrHash, "update", rb_strhash_update, 1);
520
+ rb_define_method(rb_cStrHash, "merge!", rb_strhash_update, 1);
521
+ rb_define_method(rb_cStrHash, "merge", rb_strhash_merge, 1);
522
+ rb_define_method(rb_cStrHash, "to_hash", rb_strhash_to_hash, 0);
523
+ rb_define_method(rb_cHash, "strhash", rb_hash_strhash, 0);
524
+ }
data/hwia.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "hwia"
3
+ s.version = "1.0.0"
4
+ s.date = "2009-08-23"
5
+ s.summary = "A faster HashWithIndifferentAccess (hwia) for MRI"
6
+ s.email = "lourens@methodmissing.com"
7
+ s.homepage = "http://github.com/methodmissing/hwia"
8
+ s.description = "A faster HashWithIndifferentAccess (hwia) for MRI (1.8.{6,7} and 1.9.2)"
9
+ s.has_rdoc = true
10
+ s.authors = ["Lourens Naudé (methodmissing)"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w[
13
+ README
14
+ Rakefile
15
+ bench/bench.rb
16
+ bench/as_hwia.rb
17
+ ext/hwia/extconf.rb
18
+ ext/hwia/hwia.c
19
+ lib/hwia_rails.rb
20
+ hwia.gemspec
21
+ ] + Dir.glob('test/*')
22
+ s.rdoc_options = ["--main", "README"]
23
+ s.extra_rdoc_files = ["README"]
24
+ s.extensions << "ext/hwia/extconf.rb"
25
+ end
data/lib/hwia_rails.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'hwia'
2
+ raise LoadError.new("Rails environment required!") unless Object.const_defined?(:ActiveSupport)
3
+
4
+ class Hash
5
+ alias hash_with_indifferent_access strhash
6
+ end
7
+
8
+ class StrHash
9
+ def stringify_keys!; self end
10
+ def symbolize_keys!; self end
11
+ def to_options!; self end
12
+ end
13
+
14
+ begin
15
+ old, $VERBOSE = $VERBOSE, nil
16
+ ActiveSupport::HashWithIndifferentAccess = StrHash
17
+ HashWithIndifferentAccess = ActiveSupport::HashWithIndifferentAccess
18
+ ensure
19
+ $VERBOSE = old
20
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: methodmissing-hwia
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - "Lourens Naud\xC3\xA9 (methodmissing)"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-23 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A faster HashWithIndifferentAccess (hwia) for MRI (1.8.{6,7} and 1.9.2)
17
+ email: lourens@methodmissing.com
18
+ executables: []
19
+
20
+ extensions:
21
+ - ext/hwia/extconf.rb
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - README
26
+ - Rakefile
27
+ - bench/bench.rb
28
+ - bench/as_hwia.rb
29
+ - ext/hwia/extconf.rb
30
+ - ext/hwia/hwia.c
31
+ - lib/hwia_rails.rb
32
+ - hwia.gemspec
33
+ has_rdoc: true
34
+ homepage: http://github.com/methodmissing/hwia
35
+ licenses:
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --main
39
+ - README
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.3.5
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: A faster HashWithIndifferentAccess (hwia) for MRI
61
+ test_files: []
62
+