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 +4 -0
- data/Rakefile +68 -0
- data/bench/as_hwia.rb +137 -0
- data/bench/bench.rb +40 -0
- data/ext/hwia/extconf.rb +12 -0
- data/ext/hwia/hwia.c +524 -0
- data/hwia.gemspec +25 -0
- data/lib/hwia_rails.rb +20 -0
- metadata +62 -0
data/README
ADDED
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
|
data/ext/hwia/extconf.rb
ADDED
@@ -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
|
+
|