From: Yusuke ENDOH Date: 2009-11-04T12:50:12+09:00 Subject: [ruby-core:26522] Re: HashWithIndifferentAccess to core Hi, 2009/11/4 "Martin J. Durst" : > Just a thought: What about implementing this with an option on Hash:new, > rather than as a separate class? That at least would solve the problem with > the class name. This sounds good if HWIA is really imported. How about "Hash#compare_as_string_key" following in "Hash#compare_by_indentity"? h = {} h.compare_as_string_key h[:a] = :foo h["b"] = :bar p h["a"] #=> :foo p h[:b] #=> :bar p h #=> {:a=>:foo, "b"=>:bar} h[1] = 1 #=> neither string nor symbol key in strhash (RuntimeError) # (should it be assumed as "1" by using to_s?) Here is a patch (I didn't refer the original patch, sorry): diff --git a/hash.c b/hash.c index ef8b9a8..e26312c 100644 --- a/hash.c +++ b/hash.c @@ -99,6 +99,44 @@ rb_any_hash(VALUE a) return (st_index_t)RSHIFT(hnum, 1); } +static int +rb_strsym_cmp(VALUE a, VALUE b) +{ + if (a == b) return 0; + switch (TYPE(a)) { + case T_SYMBOL: + a = rb_id2str(SYM2ID(a)); + case T_STRING: + switch (TYPE(b)) { + case T_SYMBOL: + b = rb_id2str(SYM2ID(b)); + case T_STRING: + return rb_str_hash_cmp(a, b); + } + } + rb_raise(rb_eRuntimeError, "not string nor symbol key in strhash"); +} + +static st_index_t +rb_strsym_hash(VALUE a) +{ + VALUE hval; + st_index_t hnum; + + switch (TYPE(a)) { + case T_SYMBOL: + a = rb_id2str(SYM2ID(a)); + case T_STRING: + hnum = rb_str_hash(a); + break; + + default: + rb_raise(rb_eRuntimeError, "neither string nor symbol key in strhash"); + } + hnum <<= 1; + return (st_index_t)RSHIFT(hnum, 1); +} + static const struct st_hash_type objhash = { rb_any_cmp, rb_any_hash, @@ -109,6 +147,11 @@ static const struct st_hash_type identhash = { st_numhash, }; +static const struct st_hash_type strsymhash = { + rb_strsym_cmp, + rb_strsym_hash, +}; + typedef int st_foreach_func(st_data_t, st_data_t, st_data_t); struct foreach_safe_arg { @@ -1852,6 +1895,40 @@ rb_hash_compare_by_id_p(VALUE hash) return Qfalse; } +/* + * call-seq: + * hsh.compare_as_string_key => hsh + * + * TBD + */ + +static VALUE +rb_hash_compare_as_string_key(VALUE hash) +{ + rb_hash_modify(hash); + RHASH(hash)->ntbl->type = &strsymhash; + rb_hash_rehash(hash); + return hash; +} + +/* + * call-seq: + * hsh.compare_as_string_key? => true or false + * + * TBD + */ + +static VALUE +rb_hash_compare_as_string_key_p(VALUE hash) +{ + if (!RHASH(hash)->ntbl) + return Qfalse; + if (RHASH(hash)->ntbl->type == &strsymhash) { + return Qtrue; + } + return Qfalse; +} + static int path_tainted = -1; static char **origenviron; @@ -2715,6 +2792,8 @@ Init_Hash(void) rb_define_method(rb_cHash,"compare_by_identity", rb_hash_compare_by_id, 0); rb_define_method(rb_cHash,"compare_by_identity?", rb_hash_compare_by_id_p, 0); + rb_define_method(rb_cHash,"compare_as_string_key", rb_hash_compare_as_string_key, 0); + rb_define_method(rb_cHash,"compare_as_string_key?", rb_hash_compare_as_string_key_p, 0); origenviron = environ; envtbl = rb_obj_alloc(rb_cObject); -- Yusuke ENDOH