/hash.c
C | 3560 lines | 2137 code | 369 blank | 1054 comment | 333 complexity | ca299d6b3ccf4cf2bf95884dc79a8cff MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, AGPL-3.0, 0BSD
Large files files are truncated, but you can click here to view the full file
- /**********************************************************************
- hash.c -
- $Author$
- created at: Mon Nov 22 18:51:18 JST 1993
- Copyright (C) 1993-2007 Yukihiro Matsumoto
- Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
- Copyright (C) 2000 Information-technology Promotion Agency, Japan
- **********************************************************************/
- #include "ruby/ruby.h"
- #include "ruby/st.h"
- #include "ruby/util.h"
- #include "ruby/encoding.h"
- #include "internal.h"
- #include <errno.h>
- #include "probes.h"
- #ifdef __APPLE__
- # ifdef HAVE_CRT_EXTERNS_H
- # include <crt_externs.h>
- # else
- # include "missing/crt_externs.h"
- # endif
- #endif
- static VALUE rb_hash_s_try_convert(VALUE, VALUE);
- #define HASH_DELETED FL_USER1
- #define HASH_PROC_DEFAULT FL_USER2
- VALUE
- rb_hash_freeze(VALUE hash)
- {
- return rb_obj_freeze(hash);
- }
- VALUE rb_cHash;
- static VALUE envtbl;
- static ID id_hash, id_yield, id_default;
- static int
- rb_any_cmp(VALUE a, VALUE b)
- {
- if (a == b) return 0;
- if (FIXNUM_P(a) && FIXNUM_P(b)) {
- return a != b;
- }
- if (RB_TYPE_P(a, T_STRING) && RBASIC(a)->klass == rb_cString &&
- RB_TYPE_P(b, T_STRING) && RBASIC(b)->klass == rb_cString) {
- return rb_str_hash_cmp(a, b);
- }
- if (a == Qundef || b == Qundef) return -1;
- if (SYMBOL_P(a) && SYMBOL_P(b)) {
- return a != b;
- }
- return !rb_eql(a, b);
- }
- VALUE
- rb_hash(VALUE obj)
- {
- VALUE hval = rb_funcall(obj, id_hash, 0);
- retry:
- switch (TYPE(hval)) {
- case T_FIXNUM:
- return hval;
- case T_BIGNUM:
- return LONG2FIX(((long*)(RBIGNUM_DIGITS(hval)))[0]);
- default:
- hval = rb_to_int(hval);
- goto retry;
- }
- }
- static st_index_t
- rb_any_hash(VALUE a)
- {
- VALUE hval;
- st_index_t hnum;
- if (SPECIAL_CONST_P(a)) {
- if (a == Qundef) return 0;
- hnum = rb_hash_end(rb_hash_start((st_index_t)a));
- }
- else if (BUILTIN_TYPE(a) == T_STRING) {
- hnum = rb_str_hash(a);
- }
- else {
- hval = rb_hash(a);
- hnum = FIX2LONG(hval);
- }
- hnum <<= 1;
- return (st_index_t)RSHIFT(hnum, 1);
- }
- static const struct st_hash_type objhash = {
- rb_any_cmp,
- rb_any_hash,
- };
- extern const struct st_hash_type st_hashtype_num;
- #define identhash st_hashtype_num
- typedef int st_foreach_func(st_data_t, st_data_t, st_data_t);
- struct foreach_safe_arg {
- st_table *tbl;
- st_foreach_func *func;
- st_data_t arg;
- };
- static int
- foreach_safe_i(st_data_t key, st_data_t value, struct foreach_safe_arg *arg)
- {
- int status;
- status = (*arg->func)(key, value, arg->arg);
- if (status == ST_CONTINUE) {
- return ST_CHECK;
- }
- return status;
- }
- void
- st_foreach_safe(st_table *table, int (*func)(ANYARGS), st_data_t a)
- {
- struct foreach_safe_arg arg;
- arg.tbl = table;
- arg.func = (st_foreach_func *)func;
- arg.arg = a;
- if (st_foreach_check(table, foreach_safe_i, (st_data_t)&arg, 0)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
- }
- }
- typedef int rb_foreach_func(VALUE, VALUE, VALUE);
- struct hash_foreach_arg {
- VALUE hash;
- rb_foreach_func *func;
- VALUE arg;
- };
- static int
- hash_foreach_iter(st_data_t key, st_data_t value, st_data_t argp)
- {
- struct hash_foreach_arg *arg = (struct hash_foreach_arg *)argp;
- int status;
- st_table *tbl;
- tbl = RHASH(arg->hash)->ntbl;
- status = (*arg->func)((VALUE)key, (VALUE)value, arg->arg);
- if (RHASH(arg->hash)->ntbl != tbl) {
- rb_raise(rb_eRuntimeError, "rehash occurred during iteration");
- }
- switch (status) {
- case ST_DELETE:
- FL_SET(arg->hash, HASH_DELETED);
- return ST_DELETE;
- case ST_CONTINUE:
- break;
- case ST_STOP:
- return ST_STOP;
- }
- return ST_CHECK;
- }
- static VALUE
- hash_foreach_ensure(VALUE hash)
- {
- if (--RHASH_ITER_LEV(hash) == 0) {
- if (FL_TEST(hash, HASH_DELETED)) {
- st_cleanup_safe(RHASH(hash)->ntbl, (st_data_t)Qundef);
- FL_UNSET(hash, HASH_DELETED);
- }
- }
- return 0;
- }
- static VALUE
- hash_foreach_call(VALUE arg)
- {
- VALUE hash = ((struct hash_foreach_arg *)arg)->hash;
- if (st_foreach_check(RHASH(hash)->ntbl, hash_foreach_iter, (st_data_t)arg, (st_data_t)Qundef)) {
- rb_raise(rb_eRuntimeError, "hash modified during iteration");
- }
- return Qnil;
- }
- void
- rb_hash_foreach(VALUE hash, int (*func)(ANYARGS), VALUE farg)
- {
- struct hash_foreach_arg arg;
- if (!RHASH(hash)->ntbl)
- return;
- RHASH_ITER_LEV(hash)++;
- arg.hash = hash;
- arg.func = (rb_foreach_func *)func;
- arg.arg = farg;
- rb_ensure(hash_foreach_call, (VALUE)&arg, hash_foreach_ensure, hash);
- }
- static VALUE
- hash_alloc(VALUE klass)
- {
- NEWOBJ_OF(hash, struct RHash, klass, T_HASH);
- RHASH_IFNONE(hash) = Qnil;
- return (VALUE)hash;
- }
- static VALUE
- empty_hash_alloc(VALUE klass)
- {
- if (RUBY_DTRACE_HASH_CREATE_ENABLED()) {
- RUBY_DTRACE_HASH_CREATE(0, rb_sourcefile(), rb_sourceline());
- }
- return hash_alloc(klass);
- }
- VALUE
- rb_hash_new(void)
- {
- return hash_alloc(rb_cHash);
- }
- VALUE
- rb_hash_dup(VALUE hash)
- {
- NEWOBJ_OF(ret, struct RHash,
- rb_obj_class(hash),
- (RBASIC(hash)->flags)&(T_MASK|FL_EXIVAR|FL_TAINT|FL_UNTRUSTED));
- if (FL_TEST((hash), FL_EXIVAR))
- rb_copy_generic_ivar((VALUE)(ret),(VALUE)(hash));
- if (!RHASH_EMPTY_P(hash))
- ret->ntbl = st_copy(RHASH(hash)->ntbl);
- if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- FL_SET(ret, HASH_PROC_DEFAULT);
- }
- RHASH_IFNONE(ret) = RHASH_IFNONE(hash);
- return (VALUE)ret;
- }
- static void
- rb_hash_modify_check(VALUE hash)
- {
- rb_check_frozen(hash);
- if (!OBJ_UNTRUSTED(hash) && rb_safe_level() >= 4)
- rb_raise(rb_eSecurityError, "Insecure: can't modify hash");
- }
- struct st_table *
- rb_hash_tbl(VALUE hash)
- {
- if (!RHASH(hash)->ntbl) {
- RHASH(hash)->ntbl = st_init_table(&objhash);
- }
- return RHASH(hash)->ntbl;
- }
- static void
- rb_hash_modify(VALUE hash)
- {
- rb_hash_modify_check(hash);
- rb_hash_tbl(hash);
- }
- NORETURN(static void no_new_key(void));
- static void
- no_new_key(void)
- {
- rb_raise(rb_eRuntimeError, "can't add a new key into hash during iteration");
- }
- #define NOINSERT_UPDATE_CALLBACK(func) \
- int \
- func##_noinsert(st_data_t *key, st_data_t *val, st_data_t arg, int existing) \
- { \
- if (!existing) no_new_key(); \
- return func(key, val, arg, existing); \
- }
- #define UPDATE_CALLBACK(iter_lev, func) ((iter_lev) > 0 ? func##_noinsert : func)
- #define RHASH_UPDATE_ITER(hash, iter_lev, key, func, arg) \
- st_update(RHASH(hash)->ntbl, (st_data_t)(key), \
- UPDATE_CALLBACK((iter_lev), func), \
- (st_data_t)(arg))
- #define RHASH_UPDATE(hash, key, func, arg) \
- RHASH_UPDATE_ITER(hash, RHASH_ITER_LEV(hash), key, func, arg)
- static void
- default_proc_arity_check(VALUE proc)
- {
- int n = rb_proc_arity(proc);
- if (rb_proc_lambda_p(proc) && n != 2 && (n >= 0 || n < -3)) {
- if (n < 0) n = -n-1;
- rb_raise(rb_eTypeError, "default_proc takes two arguments (2 for %d)", n);
- }
- }
- /*
- * call-seq:
- * Hash.new -> new_hash
- * Hash.new(obj) -> new_hash
- * Hash.new {|hash, key| block } -> new_hash
- *
- * Returns a new, empty hash. If this hash is subsequently accessed by
- * a key that doesn't correspond to a hash entry, the value returned
- * depends on the style of <code>new</code> used to create the hash. In
- * the first form, the access returns <code>nil</code>. If
- * <i>obj</i> is specified, this single object will be used for
- * all <em>default values</em>. If a block is specified, it will be
- * called with the hash object and the key, and should return the
- * default value. It is the block's responsibility to store the value
- * in the hash if required.
- *
- * h = Hash.new("Go Fish")
- * h["a"] = 100
- * h["b"] = 200
- * h["a"] #=> 100
- * h["c"] #=> "Go Fish"
- * # The following alters the single default object
- * h["c"].upcase! #=> "GO FISH"
- * h["d"] #=> "GO FISH"
- * h.keys #=> ["a", "b"]
- *
- * # While this creates a new default object each time
- * h = Hash.new { |hash, key| hash[key] = "Go Fish: #{key}" }
- * h["c"] #=> "Go Fish: c"
- * h["c"].upcase! #=> "GO FISH: C"
- * h["d"] #=> "Go Fish: d"
- * h.keys #=> ["c", "d"]
- *
- */
- static VALUE
- rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
- {
- VALUE ifnone;
- rb_hash_modify(hash);
- if (rb_block_given_p()) {
- rb_check_arity(argc, 0, 0);
- ifnone = rb_block_proc();
- default_proc_arity_check(ifnone);
- RHASH_IFNONE(hash) = ifnone;
- FL_SET(hash, HASH_PROC_DEFAULT);
- }
- else {
- rb_scan_args(argc, argv, "01", &ifnone);
- RHASH_IFNONE(hash) = ifnone;
- }
- return hash;
- }
- /*
- * call-seq:
- * Hash[ key, value, ... ] -> new_hash
- * Hash[ [ [key, value], ... ] ] -> new_hash
- * Hash[ object ] -> new_hash
- *
- * Creates a new hash populated with the given objects. Equivalent to
- * the literal <code>{ <i>key</i> => <i>value</i>, ... }</code>. In the first
- * form, keys and values occur in pairs, so there must be an even number of arguments.
- * The second and third form take a single argument which is either
- * an array of key-value pairs or an object convertible to a hash.
- *
- * Hash["a", 100, "b", 200] #=> {"a"=>100, "b"=>200}
- * Hash[ [ ["a", 100], ["b", 200] ] ] #=> {"a"=>100, "b"=>200}
- * Hash["a" => 100, "b" => 200] #=> {"a"=>100, "b"=>200}
- */
- static VALUE
- rb_hash_s_create(int argc, VALUE *argv, VALUE klass)
- {
- VALUE hash, tmp;
- int i;
- if (argc == 1) {
- tmp = rb_hash_s_try_convert(Qnil, argv[0]);
- if (!NIL_P(tmp)) {
- hash = hash_alloc(klass);
- if (RHASH(tmp)->ntbl) {
- RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl);
- }
- return hash;
- }
- tmp = rb_check_array_type(argv[0]);
- if (!NIL_P(tmp)) {
- long i;
- hash = hash_alloc(klass);
- for (i = 0; i < RARRAY_LEN(tmp); ++i) {
- VALUE e = RARRAY_PTR(tmp)[i];
- VALUE v = rb_check_array_type(e);
- VALUE key, val = Qnil;
- if (NIL_P(v)) {
- #if 0 /* refix in the next release */
- rb_raise(rb_eArgError, "wrong element type %s at %ld (expected array)",
- rb_builtin_class_name(e), i);
- #else
- rb_warn("wrong element type %s at %ld (expected array)",
- rb_builtin_class_name(e), i);
- rb_warn("ignoring wrong elements is deprecated, remove them explicitly");
- rb_warn("this causes ArgumentError in the next release");
- continue;
- #endif
- }
- switch (RARRAY_LEN(v)) {
- default:
- rb_raise(rb_eArgError, "invalid number of elements (%ld for 1..2)",
- RARRAY_LEN(v));
- case 2:
- val = RARRAY_PTR(v)[1];
- case 1:
- key = RARRAY_PTR(v)[0];
- rb_hash_aset(hash, key, val);
- }
- }
- return hash;
- }
- }
- if (argc % 2 != 0) {
- rb_raise(rb_eArgError, "odd number of arguments for Hash");
- }
- hash = hash_alloc(klass);
- for (i=0; i<argc; i+=2) {
- rb_hash_aset(hash, argv[i], argv[i + 1]);
- }
- return hash;
- }
- static VALUE
- to_hash(VALUE hash)
- {
- return rb_convert_type(hash, T_HASH, "Hash", "to_hash");
- }
- VALUE
- rb_check_hash_type(VALUE hash)
- {
- return rb_check_convert_type(hash, T_HASH, "Hash", "to_hash");
- }
- /*
- * call-seq:
- * Hash.try_convert(obj) -> hash or nil
- *
- * Try to convert <i>obj</i> into a hash, using to_hash method.
- * Returns converted hash or nil if <i>obj</i> cannot be converted
- * for any reason.
- *
- * Hash.try_convert({1=>2}) # => {1=>2}
- * Hash.try_convert("1=>2") # => nil
- */
- static VALUE
- rb_hash_s_try_convert(VALUE dummy, VALUE hash)
- {
- return rb_check_hash_type(hash);
- }
- static int
- rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg)
- {
- st_table *tbl = (st_table *)arg;
- st_insert(tbl, (st_data_t)key, (st_data_t)value);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.rehash -> hsh
- *
- * Rebuilds the hash based on the current hash values for each key. If
- * values of key objects have changed since they were inserted, this
- * method will reindex <i>hsh</i>. If <code>Hash#rehash</code> is
- * called while an iterator is traversing the hash, an
- * <code>RuntimeError</code> will be raised in the iterator.
- *
- * a = [ "a", "b" ]
- * c = [ "c", "d" ]
- * h = { a => 100, c => 300 }
- * h[a] #=> 100
- * a[0] = "z"
- * h[a] #=> nil
- * h.rehash #=> {["z", "b"]=>100, ["c", "d"]=>300}
- * h[a] #=> 100
- */
- static VALUE
- rb_hash_rehash(VALUE hash)
- {
- st_table *tbl;
- if (RHASH_ITER_LEV(hash) > 0) {
- rb_raise(rb_eRuntimeError, "rehash during iteration");
- }
- rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return hash;
- tbl = st_init_table_with_size(RHASH(hash)->ntbl->type, RHASH(hash)->ntbl->num_entries);
- rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tbl);
- st_free_table(RHASH(hash)->ntbl);
- RHASH(hash)->ntbl = tbl;
- return hash;
- }
- static VALUE
- hash_default_value(VALUE hash, VALUE key)
- {
- if (rb_method_basic_definition_p(CLASS_OF(hash), id_default)) {
- VALUE ifnone = RHASH_IFNONE(hash);
- if (!FL_TEST(hash, HASH_PROC_DEFAULT)) return ifnone;
- if (key == Qundef) return Qnil;
- return rb_funcall(ifnone, id_yield, 2, hash, key);
- }
- else {
- return rb_funcall(hash, id_default, 1, key);
- }
- }
- /*
- * call-seq:
- * hsh[key] -> value
- *
- * Element Reference---Retrieves the <i>value</i> object corresponding
- * to the <i>key</i> object. If not found, returns the default value (see
- * <code>Hash::new</code> for details).
- *
- * h = { "a" => 100, "b" => 200 }
- * h["a"] #=> 100
- * h["c"] #=> nil
- *
- */
- VALUE
- rb_hash_aref(VALUE hash, VALUE key)
- {
- st_data_t val;
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- return hash_default_value(hash, key);
- }
- return (VALUE)val;
- }
- VALUE
- rb_hash_lookup2(VALUE hash, VALUE key, VALUE def)
- {
- st_data_t val;
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- return def; /* without Hash#default */
- }
- return (VALUE)val;
- }
- VALUE
- rb_hash_lookup(VALUE hash, VALUE key)
- {
- return rb_hash_lookup2(hash, key, Qnil);
- }
- /*
- * call-seq:
- * hsh.fetch(key [, default] ) -> obj
- * hsh.fetch(key) {| key | block } -> obj
- *
- * Returns a value from the hash for the given key. If the key can't be
- * found, there are several options: With no other arguments, it will
- * raise an <code>KeyError</code> exception; if <i>default</i> is
- * given, then that will be returned; if the optional code block is
- * specified, then that will be run and its result returned.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.fetch("a") #=> 100
- * h.fetch("z", "go fish") #=> "go fish"
- * h.fetch("z") { |el| "go fish, #{el}"} #=> "go fish, z"
- *
- * The following example shows that an exception is raised if the key
- * is not found and a default value is not supplied.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.fetch("z")
- *
- * <em>produces:</em>
- *
- * prog.rb:2:in `fetch': key not found (KeyError)
- * from prog.rb:2
- *
- */
- static VALUE
- rb_hash_fetch_m(int argc, VALUE *argv, VALUE hash)
- {
- VALUE key, if_none;
- st_data_t val;
- long block_given;
- rb_scan_args(argc, argv, "11", &key, &if_none);
- block_given = rb_block_given_p();
- if (block_given && argc == 2) {
- rb_warn("block supersedes default value argument");
- }
- if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
- if (block_given) return rb_yield(key);
- if (argc == 1) {
- volatile VALUE desc = rb_protect(rb_inspect, key, 0);
- if (NIL_P(desc)) {
- desc = rb_any_to_s(key);
- }
- desc = rb_str_ellipsize(desc, 65);
- rb_raise(rb_eKeyError, "key not found: %s", RSTRING_PTR(desc));
- }
- return if_none;
- }
- return (VALUE)val;
- }
- VALUE
- rb_hash_fetch(VALUE hash, VALUE key)
- {
- return rb_hash_fetch_m(1, &key, hash);
- }
- /*
- * call-seq:
- * hsh.default(key=nil) -> obj
- *
- * Returns the default value, the value that would be returned by
- * <i>hsh</i>[<i>key</i>] if <i>key</i> did not exist in <i>hsh</i>.
- * See also <code>Hash::new</code> and <code>Hash#default=</code>.
- *
- * h = Hash.new #=> {}
- * h.default #=> nil
- * h.default(2) #=> nil
- *
- * h = Hash.new("cat") #=> {}
- * h.default #=> "cat"
- * h.default(2) #=> "cat"
- *
- * h = Hash.new {|h,k| h[k] = k.to_i*10} #=> {}
- * h.default #=> nil
- * h.default(2) #=> 20
- */
- static VALUE
- rb_hash_default(int argc, VALUE *argv, VALUE hash)
- {
- VALUE key, ifnone;
- rb_scan_args(argc, argv, "01", &key);
- ifnone = RHASH_IFNONE(hash);
- if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- if (argc == 0) return Qnil;
- return rb_funcall(ifnone, id_yield, 2, hash, key);
- }
- return ifnone;
- }
- /*
- * call-seq:
- * hsh.default = obj -> obj
- *
- * Sets the default value, the value returned for a key that does not
- * exist in the hash. It is not possible to set the default to a
- * <code>Proc</code> that will be executed on each key lookup.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.default = "Go fish"
- * h["a"] #=> 100
- * h["z"] #=> "Go fish"
- * # This doesn't do what you might hope...
- * h.default = proc do |hash, key|
- * hash[key] = key + key
- * end
- * h[2] #=> #<Proc:0x401b3948@-:6>
- * h["cat"] #=> #<Proc:0x401b3948@-:6>
- */
- static VALUE
- rb_hash_set_default(VALUE hash, VALUE ifnone)
- {
- rb_hash_modify_check(hash);
- RHASH_IFNONE(hash) = ifnone;
- FL_UNSET(hash, HASH_PROC_DEFAULT);
- return ifnone;
- }
- /*
- * call-seq:
- * hsh.default_proc -> anObject
- *
- * If <code>Hash::new</code> was invoked with a block, return that
- * block, otherwise return <code>nil</code>.
- *
- * h = Hash.new {|h,k| h[k] = k*k } #=> {}
- * p = h.default_proc #=> #<Proc:0x401b3d08@-:1>
- * a = [] #=> []
- * p.call(a, 2)
- * a #=> [nil, nil, 4]
- */
- static VALUE
- rb_hash_default_proc(VALUE hash)
- {
- if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- return RHASH_IFNONE(hash);
- }
- return Qnil;
- }
- /*
- * call-seq:
- * hsh.default_proc = proc_obj or nil
- *
- * Sets the default proc to be executed on each failed key lookup.
- *
- * h.default_proc = proc do |hash, key|
- * hash[key] = key + key
- * end
- * h[2] #=> 4
- * h["cat"] #=> "catcat"
- */
- static VALUE
- rb_hash_set_default_proc(VALUE hash, VALUE proc)
- {
- VALUE b;
- rb_hash_modify_check(hash);
- if (NIL_P(proc)) {
- FL_UNSET(hash, HASH_PROC_DEFAULT);
- RHASH_IFNONE(hash) = proc;
- return proc;
- }
- b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
- if (NIL_P(b) || !rb_obj_is_proc(b)) {
- rb_raise(rb_eTypeError,
- "wrong default_proc type %s (expected Proc)",
- rb_obj_classname(proc));
- }
- proc = b;
- default_proc_arity_check(proc);
- RHASH_IFNONE(hash) = proc;
- FL_SET(hash, HASH_PROC_DEFAULT);
- return proc;
- }
- static int
- key_i(VALUE key, VALUE value, VALUE arg)
- {
- VALUE *args = (VALUE *)arg;
- if (rb_equal(value, args[0])) {
- args[1] = key;
- return ST_STOP;
- }
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.key(value) -> key
- *
- * Returns the key of an occurrence of a given value. If the value is
- * not found, returns <code>nil</code>.
- *
- * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 300 }
- * h.key(200) #=> "b"
- * h.key(300) #=> "c"
- * h.key(999) #=> nil
- *
- */
- static VALUE
- rb_hash_key(VALUE hash, VALUE value)
- {
- VALUE args[2];
- args[0] = value;
- args[1] = Qnil;
- rb_hash_foreach(hash, key_i, (VALUE)args);
- return args[1];
- }
- /* :nodoc: */
- static VALUE
- rb_hash_index(VALUE hash, VALUE value)
- {
- rb_warn("Hash#index is deprecated; use Hash#key");
- return rb_hash_key(hash, value);
- }
- static VALUE
- rb_hash_delete_key(VALUE hash, VALUE key)
- {
- st_data_t ktmp = (st_data_t)key, val;
- if (!RHASH(hash)->ntbl)
- return Qundef;
- if (RHASH_ITER_LEV(hash) > 0) {
- if (st_delete_safe(RHASH(hash)->ntbl, &ktmp, &val, (st_data_t)Qundef)) {
- FL_SET(hash, HASH_DELETED);
- return (VALUE)val;
- }
- }
- else if (st_delete(RHASH(hash)->ntbl, &ktmp, &val))
- return (VALUE)val;
- return Qundef;
- }
- /*
- * call-seq:
- * hsh.delete(key) -> value
- * hsh.delete(key) {| key | block } -> value
- *
- * Deletes the key-value pair and returns the value from <i>hsh</i> whose
- * key is equal to <i>key</i>. If the key is not found, returns the
- * <em>default value</em>. If the optional code block is given and the
- * key is not found, pass in the key and return the result of
- * <i>block</i>.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.delete("a") #=> 100
- * h.delete("z") #=> nil
- * h.delete("z") { |el| "#{el} not found" } #=> "z not found"
- *
- */
- VALUE
- rb_hash_delete(VALUE hash, VALUE key)
- {
- VALUE val;
- rb_hash_modify_check(hash);
- val = rb_hash_delete_key(hash, key);
- if (val != Qundef) return val;
- if (rb_block_given_p()) {
- return rb_yield(key);
- }
- return Qnil;
- }
- struct shift_var {
- VALUE key;
- VALUE val;
- };
- static int
- shift_i(VALUE key, VALUE value, VALUE arg)
- {
- struct shift_var *var = (struct shift_var *)arg;
- if (var->key != Qundef) return ST_STOP;
- var->key = key;
- var->val = value;
- return ST_DELETE;
- }
- static int
- shift_i_safe(VALUE key, VALUE value, VALUE arg)
- {
- struct shift_var *var = (struct shift_var *)arg;
- var->key = key;
- var->val = value;
- return ST_STOP;
- }
- /*
- * call-seq:
- * hsh.shift -> anArray or obj
- *
- * Removes a key-value pair from <i>hsh</i> and returns it as the
- * two-item array <code>[</code> <i>key, value</i> <code>]</code>, or
- * the hash's default value if the hash is empty.
- *
- * h = { 1 => "a", 2 => "b", 3 => "c" }
- * h.shift #=> [1, "a"]
- * h #=> {2=>"b", 3=>"c"}
- */
- static VALUE
- rb_hash_shift(VALUE hash)
- {
- struct shift_var var;
- rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl) {
- var.key = Qundef;
- rb_hash_foreach(hash, RHASH_ITER_LEV(hash) > 0 ? shift_i_safe : shift_i,
- (VALUE)&var);
- if (var.key != Qundef) {
- if (RHASH_ITER_LEV(hash) > 0) {
- rb_hash_delete_key(hash, var.key);
- }
- return rb_assoc_new(var.key, var.val);
- }
- }
- return hash_default_value(hash, Qnil);
- }
- static int
- delete_if_i(VALUE key, VALUE value, VALUE hash)
- {
- if (RTEST(rb_yield_values(2, key, value))) {
- rb_hash_delete_key(hash, key);
- }
- return ST_CONTINUE;
- }
- static VALUE rb_hash_size(VALUE hash);
- /*
- * call-seq:
- * hsh.delete_if {| key, value | block } -> hsh
- * hsh.delete_if -> an_enumerator
- *
- * Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
- * evaluates to <code>true</code>.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * h = { "a" => 100, "b" => 200, "c" => 300 }
- * h.delete_if {|key, value| key >= "b" } #=> {"a"=>100}
- *
- */
- VALUE
- rb_hash_delete_if(VALUE hash)
- {
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl)
- rb_hash_foreach(hash, delete_if_i, hash);
- return hash;
- }
- /*
- * call-seq:
- * hsh.reject! {| key, value | block } -> hsh or nil
- * hsh.reject! -> an_enumerator
- *
- * Equivalent to <code>Hash#delete_if</code>, but returns
- * <code>nil</code> if no changes were made.
- */
- VALUE
- rb_hash_reject_bang(VALUE hash)
- {
- st_index_t n;
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_modify(hash);
- if (!RHASH(hash)->ntbl)
- return Qnil;
- n = RHASH(hash)->ntbl->num_entries;
- rb_hash_foreach(hash, delete_if_i, hash);
- if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
- return hash;
- }
- /*
- * call-seq:
- * hsh.reject {| key, value | block } -> a_hash
- * hsh.reject -> an_enumerator
- *
- * Same as <code>Hash#delete_if</code>, but works on (and returns) a
- * copy of the <i>hsh</i>. Equivalent to
- * <code><i>hsh</i>.dup.delete_if</code>.
- *
- */
- static VALUE
- rb_hash_reject(VALUE hash)
- {
- return rb_hash_delete_if(rb_obj_dup(hash));
- }
- /*
- * call-seq:
- * hsh.values_at(key, ...) -> array
- *
- * Return an array containing the values associated with the given keys.
- * Also see <code>Hash.select</code>.
- *
- * h = { "cat" => "feline", "dog" => "canine", "cow" => "bovine" }
- * h.values_at("cow", "cat") #=> ["bovine", "feline"]
- */
- VALUE
- rb_hash_values_at(int argc, VALUE *argv, VALUE hash)
- {
- VALUE result = rb_ary_new2(argc);
- long i;
- for (i=0; i<argc; i++) {
- rb_ary_push(result, rb_hash_aref(hash, argv[i]));
- }
- return result;
- }
- static int
- select_i(VALUE key, VALUE value, VALUE result)
- {
- if (RTEST(rb_yield_values(2, key, value)))
- rb_hash_aset(result, key, value);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.select {|key, value| block} -> a_hash
- * hsh.select -> an_enumerator
- *
- * Returns a new hash consisting of entries for which the block returns true.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * h = { "a" => 100, "b" => 200, "c" => 300 }
- * h.select {|k,v| k > "a"} #=> {"b" => 200, "c" => 300}
- * h.select {|k,v| v < 200} #=> {"a" => 100}
- */
- VALUE
- rb_hash_select(VALUE hash)
- {
- VALUE result;
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- result = rb_hash_new();
- rb_hash_foreach(hash, select_i, result);
- return result;
- }
- static int
- keep_if_i(VALUE key, VALUE value, VALUE hash)
- {
- if (!RTEST(rb_yield_values(2, key, value))) {
- return ST_DELETE;
- }
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.select! {| key, value | block } -> hsh or nil
- * hsh.select! -> an_enumerator
- *
- * Equivalent to <code>Hash#keep_if</code>, but returns
- * <code>nil</code> if no changes were made.
- */
- VALUE
- rb_hash_select_bang(VALUE hash)
- {
- st_index_t n;
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return Qnil;
- n = RHASH(hash)->ntbl->num_entries;
- rb_hash_foreach(hash, keep_if_i, hash);
- if (n == RHASH(hash)->ntbl->num_entries) return Qnil;
- return hash;
- }
- /*
- * call-seq:
- * hsh.keep_if {| key, value | block } -> hsh
- * hsh.keep_if -> an_enumerator
- *
- * Deletes every key-value pair from <i>hsh</i> for which <i>block</i>
- * evaluates to false.
- *
- * If no block is given, an enumerator is returned instead.
- *
- */
- VALUE
- rb_hash_keep_if(VALUE hash)
- {
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_modify_check(hash);
- if (RHASH(hash)->ntbl)
- rb_hash_foreach(hash, keep_if_i, hash);
- return hash;
- }
- static int
- clear_i(VALUE key, VALUE value, VALUE dummy)
- {
- return ST_DELETE;
- }
- /*
- * call-seq:
- * hsh.clear -> hsh
- *
- * Removes all key-value pairs from <i>hsh</i>.
- *
- * h = { "a" => 100, "b" => 200 } #=> {"a"=>100, "b"=>200}
- * h.clear #=> {}
- *
- */
- VALUE
- rb_hash_clear(VALUE hash)
- {
- rb_hash_modify_check(hash);
- if (!RHASH(hash)->ntbl)
- return hash;
- if (RHASH(hash)->ntbl->num_entries > 0) {
- if (RHASH_ITER_LEV(hash) > 0)
- rb_hash_foreach(hash, clear_i, 0);
- else
- st_clear(RHASH(hash)->ntbl);
- }
- return hash;
- }
- static int
- hash_aset(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
- {
- *val = arg;
- return ST_CONTINUE;
- }
- static int
- hash_aset_str(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
- {
- *key = (st_data_t)rb_str_new_frozen((VALUE)*key);
- return hash_aset(key, val, arg, existing);
- }
- static NOINSERT_UPDATE_CALLBACK(hash_aset)
- static NOINSERT_UPDATE_CALLBACK(hash_aset_str)
- /*
- * call-seq:
- * hsh[key] = value -> value
- * hsh.store(key, value) -> value
- *
- * Element Assignment---Associates the value given by
- * <i>value</i> with the key given by <i>key</i>.
- * <i>key</i> should not have its value changed while it is in
- * use as a key (a <code>String</code> passed as a key will be
- * duplicated and frozen).
- *
- * h = { "a" => 100, "b" => 200 }
- * h["a"] = 9
- * h["c"] = 4
- * h #=> {"a"=>9, "b"=>200, "c"=>4}
- *
- */
- VALUE
- rb_hash_aset(VALUE hash, VALUE key, VALUE val)
- {
- int iter_lev = RHASH_ITER_LEV(hash);
- st_table *tbl = RHASH(hash)->ntbl;
- rb_hash_modify(hash);
- if (!tbl) {
- if (iter_lev > 0) no_new_key();
- tbl = RHASH_TBL(hash);
- }
- if (tbl->type == &identhash || rb_obj_class(key) != rb_cString) {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset, val);
- }
- else {
- RHASH_UPDATE_ITER(hash, iter_lev, key, hash_aset_str, val);
- }
- return val;
- }
- static int
- replace_i(VALUE key, VALUE val, VALUE hash)
- {
- rb_hash_aset(hash, key, val);
- return ST_CONTINUE;
- }
- static VALUE
- rb_hash_initialize_copy(VALUE hash, VALUE hash2)
- {
- rb_hash_modify_check(hash);
- hash2 = to_hash(hash2);
- Check_Type(hash2, T_HASH);
- if (!RHASH_EMPTY_P(hash2)) {
- RHASH(hash)->ntbl = st_copy(RHASH(hash2)->ntbl);
- rb_hash_rehash(hash);
- }
- if (FL_TEST(hash2, HASH_PROC_DEFAULT)) {
- FL_SET(hash, HASH_PROC_DEFAULT);
- }
- else {
- FL_UNSET(hash, HASH_PROC_DEFAULT);
- }
- RHASH_IFNONE(hash) = RHASH_IFNONE(hash2);
- return hash;
- }
- /*
- * call-seq:
- * hsh.replace(other_hash) -> hsh
- *
- * Replaces the contents of <i>hsh</i> with the contents of
- * <i>other_hash</i>.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
- *
- */
- static VALUE
- rb_hash_replace(VALUE hash, VALUE hash2)
- {
- rb_hash_modify_check(hash);
- hash2 = to_hash(hash2);
- if (hash == hash2) return hash;
- rb_hash_clear(hash);
- if (RHASH(hash2)->ntbl) {
- rb_hash_tbl(hash);
- RHASH(hash)->ntbl->type = RHASH(hash2)->ntbl->type;
- }
- rb_hash_foreach(hash2, replace_i, hash);
- RHASH_IFNONE(hash) = RHASH_IFNONE(hash2);
- if (FL_TEST(hash2, HASH_PROC_DEFAULT)) {
- FL_SET(hash, HASH_PROC_DEFAULT);
- }
- else {
- FL_UNSET(hash, HASH_PROC_DEFAULT);
- }
- return hash;
- }
- /*
- * call-seq:
- * hsh.length -> fixnum
- * hsh.size -> fixnum
- *
- * Returns the number of key-value pairs in the hash.
- *
- * h = { "d" => 100, "a" => 200, "v" => 300, "e" => 400 }
- * h.length #=> 4
- * h.delete("a") #=> 200
- * h.length #=> 3
- */
- static VALUE
- rb_hash_size(VALUE hash)
- {
- if (!RHASH(hash)->ntbl)
- return INT2FIX(0);
- return INT2FIX(RHASH(hash)->ntbl->num_entries);
- }
- /*
- * call-seq:
- * hsh.empty? -> true or false
- *
- * Returns <code>true</code> if <i>hsh</i> contains no key-value pairs.
- *
- * {}.empty? #=> true
- *
- */
- static VALUE
- rb_hash_empty_p(VALUE hash)
- {
- return RHASH_EMPTY_P(hash) ? Qtrue : Qfalse;
- }
- static int
- each_value_i(VALUE key, VALUE value)
- {
- rb_yield(value);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.each_value {| value | block } -> hsh
- * hsh.each_value -> an_enumerator
- *
- * Calls <i>block</i> once for each key in <i>hsh</i>, passing the
- * value as a parameter.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.each_value {|value| puts value }
- *
- * <em>produces:</em>
- *
- * 100
- * 200
- */
- static VALUE
- rb_hash_each_value(VALUE hash)
- {
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_foreach(hash, each_value_i, 0);
- return hash;
- }
- static int
- each_key_i(VALUE key, VALUE value)
- {
- rb_yield(key);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.each_key {| key | block } -> hsh
- * hsh.each_key -> an_enumerator
- *
- * Calls <i>block</i> once for each key in <i>hsh</i>, passing the key
- * as a parameter.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.each_key {|key| puts key }
- *
- * <em>produces:</em>
- *
- * a
- * b
- */
- static VALUE
- rb_hash_each_key(VALUE hash)
- {
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_foreach(hash, each_key_i, 0);
- return hash;
- }
- static int
- each_pair_i(VALUE key, VALUE value)
- {
- rb_yield(rb_assoc_new(key, value));
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.each {| key, value | block } -> hsh
- * hsh.each_pair {| key, value | block } -> hsh
- * hsh.each -> an_enumerator
- * hsh.each_pair -> an_enumerator
- *
- * Calls <i>block</i> once for each key in <i>hsh</i>, passing the key-value
- * pair as parameters.
- *
- * If no block is given, an enumerator is returned instead.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.each {|key, value| puts "#{key} is #{value}" }
- *
- * <em>produces:</em>
- *
- * a is 100
- * b is 200
- *
- */
- static VALUE
- rb_hash_each_pair(VALUE hash)
- {
- RETURN_SIZED_ENUMERATOR(hash, 0, 0, rb_hash_size);
- rb_hash_foreach(hash, each_pair_i, 0);
- return hash;
- }
- static int
- to_a_i(VALUE key, VALUE value, VALUE ary)
- {
- rb_ary_push(ary, rb_assoc_new(key, value));
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.to_a -> array
- *
- * Converts <i>hsh</i> to a nested array of <code>[</code> <i>key,
- * value</i> <code>]</code> arrays.
- *
- * h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
- * h.to_a #=> [["c", 300], ["a", 100], ["d", 400]]
- */
- static VALUE
- rb_hash_to_a(VALUE hash)
- {
- VALUE ary;
- ary = rb_ary_new();
- rb_hash_foreach(hash, to_a_i, ary);
- OBJ_INFECT(ary, hash);
- return ary;
- }
- static int
- inspect_i(VALUE key, VALUE value, VALUE str)
- {
- VALUE str2;
- str2 = rb_inspect(key);
- if (RSTRING_LEN(str) > 1) {
- rb_str_buf_cat_ascii(str, ", ");
- }
- else {
- rb_enc_copy(str, str2);
- }
- rb_str_buf_append(str, str2);
- OBJ_INFECT(str, str2);
- rb_str_buf_cat_ascii(str, "=>");
- str2 = rb_inspect(value);
- rb_str_buf_append(str, str2);
- OBJ_INFECT(str, str2);
- return ST_CONTINUE;
- }
- static VALUE
- inspect_hash(VALUE hash, VALUE dummy, int recur)
- {
- VALUE str;
- if (recur) return rb_usascii_str_new2("{...}");
- str = rb_str_buf_new2("{");
- rb_hash_foreach(hash, inspect_i, str);
- rb_str_buf_cat2(str, "}");
- OBJ_INFECT(str, hash);
- return str;
- }
- /*
- * call-seq:
- * hsh.to_s -> string
- * hsh.inspect -> string
- *
- * Return the contents of this hash as a string.
- *
- * h = { "c" => 300, "a" => 100, "d" => 400, "c" => 300 }
- * h.to_s #=> "{\"c\"=>300, \"a\"=>100, \"d\"=>400}"
- */
- static VALUE
- rb_hash_inspect(VALUE hash)
- {
- if (RHASH_EMPTY_P(hash))
- return rb_usascii_str_new2("{}");
- return rb_exec_recursive(inspect_hash, hash, 0);
- }
- /*
- * call-seq:
- * hsh.to_hash => hsh
- *
- * Returns +self+.
- */
- static VALUE
- rb_hash_to_hash(VALUE hash)
- {
- return hash;
- }
- /*
- * call-seq:
- * hsh.to_h -> hsh or new_hash
- *
- * Returns +self+. If called on a subclass of Hash, converts
- * the receiver to a Hash object.
- */
- static VALUE
- rb_hash_to_h(VALUE hash)
- {
- if (rb_obj_class(hash) != rb_cHash) {
- VALUE ret = rb_hash_new();
- if (!RHASH_EMPTY_P(hash))
- RHASH(ret)->ntbl = st_copy(RHASH(hash)->ntbl);
- if (FL_TEST(hash, HASH_PROC_DEFAULT)) {
- FL_SET(ret, HASH_PROC_DEFAULT);
- }
- RHASH_IFNONE(ret) = RHASH_IFNONE(hash);
- return ret;
- }
- return hash;
- }
- static int
- keys_i(VALUE key, VALUE value, VALUE ary)
- {
- rb_ary_push(ary, key);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.keys -> array
- *
- * Returns a new array populated with the keys from this hash. See also
- * <code>Hash#values</code>.
- *
- * h = { "a" => 100, "b" => 200, "c" => 300, "d" => 400 }
- * h.keys #=> ["a", "b", "c", "d"]
- *
- */
- static VALUE
- rb_hash_keys(VALUE hash)
- {
- VALUE ary;
- ary = rb_ary_new();
- rb_hash_foreach(hash, keys_i, ary);
- return ary;
- }
- static int
- values_i(VALUE key, VALUE value, VALUE ary)
- {
- rb_ary_push(ary, value);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.values -> array
- *
- * Returns a new array populated with the values from <i>hsh</i>. See
- * also <code>Hash#keys</code>.
- *
- * h = { "a" => 100, "b" => 200, "c" => 300 }
- * h.values #=> [100, 200, 300]
- *
- */
- static VALUE
- rb_hash_values(VALUE hash)
- {
- VALUE ary;
- ary = rb_ary_new();
- rb_hash_foreach(hash, values_i, ary);
- return ary;
- }
- /*
- * call-seq:
- * hsh.has_key?(key) -> true or false
- * hsh.include?(key) -> true or false
- * hsh.key?(key) -> true or false
- * hsh.member?(key) -> true or false
- *
- * Returns <code>true</code> if the given key is present in <i>hsh</i>.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.has_key?("a") #=> true
- * h.has_key?("z") #=> false
- *
- */
- static VALUE
- rb_hash_has_key(VALUE hash, VALUE key)
- {
- if (!RHASH(hash)->ntbl)
- return Qfalse;
- if (st_lookup(RHASH(hash)->ntbl, key, 0)) {
- return Qtrue;
- }
- return Qfalse;
- }
- static int
- rb_hash_search_value(VALUE key, VALUE value, VALUE arg)
- {
- VALUE *data = (VALUE *)arg;
- if (rb_equal(value, data[1])) {
- data[0] = Qtrue;
- return ST_STOP;
- }
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.has_value?(value) -> true or false
- * hsh.value?(value) -> true or false
- *
- * Returns <code>true</code> if the given value is present for some key
- * in <i>hsh</i>.
- *
- * h = { "a" => 100, "b" => 200 }
- * h.has_value?(100) #=> true
- * h.has_value?(999) #=> false
- */
- static VALUE
- rb_hash_has_value(VALUE hash, VALUE val)
- {
- VALUE data[2];
- data[0] = Qfalse;
- data[1] = val;
- rb_hash_foreach(hash, rb_hash_search_value, (VALUE)data);
- return data[0];
- }
- struct equal_data {
- VALUE result;
- st_table *tbl;
- int eql;
- };
- static int
- eql_i(VALUE key, VALUE val1, VALUE arg)
- {
- struct equal_data *data = (struct equal_data *)arg;
- st_data_t val2;
- if (!st_lookup(data->tbl, key, &val2)) {
- data->result = Qfalse;
- return ST_STOP;
- }
- if (!(data->eql ? rb_eql(val1, (VALUE)val2) : (int)rb_equal(val1, (VALUE)val2))) {
- data->result = Qfalse;
- return ST_STOP;
- }
- return ST_CONTINUE;
- }
- static VALUE
- recursive_eql(VALUE hash, VALUE dt, int recur)
- {
- struct equal_data *data;
- if (recur) return Qtrue; /* Subtle! */
- data = (struct equal_data*)dt;
- data->result = Qtrue;
- rb_hash_foreach(hash, eql_i, dt);
- return data->result;
- }
- static VALUE
- hash_equal(VALUE hash1, VALUE hash2, int eql)
- {
- struct equal_data data;
- if (hash1 == hash2) return Qtrue;
- if (!RB_TYPE_P(hash2, T_HASH)) {
- if (!rb_respond_to(hash2, rb_intern("to_hash"))) {
- return Qfalse;
- }
- if (eql)
- return rb_eql(hash2, hash1);
- else
- return rb_equal(hash2, hash1);
- }
- if (RHASH_SIZE(hash1) != RHASH_SIZE(hash2))
- return Qfalse;
- if (!RHASH(hash1)->ntbl || !RHASH(hash2)->ntbl)
- return Qtrue;
- if (RHASH(hash1)->ntbl->type != RHASH(hash2)->ntbl->type)
- return Qfalse;
- #if 0
- if (!(rb_equal(RHASH_IFNONE(hash1), RHASH_IFNONE(hash2)) &&
- FL_TEST(hash1, HASH_PROC_DEFAULT) == FL_TEST(hash2, HASH_PROC_DEFAULT)))
- return Qfalse;
- #endif
- data.tbl = RHASH(hash2)->ntbl;
- data.eql = eql;
- return rb_exec_recursive_paired(recursive_eql, hash1, hash2, (VALUE)&data);
- }
- /*
- * call-seq:
- * hsh == other_hash -> true or false
- *
- * Equality---Two hashes are equal if they each contain the same number
- * of keys and if each key-value pair is equal to (according to
- * <code>Object#==</code>) the corresponding elements in the other
- * hash.
- *
- * h1 = { "a" => 1, "c" => 2 }
- * h2 = { 7 => 35, "c" => 2, "a" => 1 }
- * h3 = { "a" => 1, "c" => 2, 7 => 35 }
- * h4 = { "a" => 1, "d" => 2, "f" => 35 }
- * h1 == h2 #=> false
- * h2 == h3 #=> true
- * h3 == h4 #=> false
- *
- */
- static VALUE
- rb_hash_equal(VALUE hash1, VALUE hash2)
- {
- return hash_equal(hash1, hash2, FALSE);
- }
- /*
- * call-seq:
- * hash.eql?(other) -> true or false
- *
- * Returns <code>true</code> if <i>hash</i> and <i>other</i> are
- * both hashes with the same content.
- */
- static VALUE
- rb_hash_eql(VALUE hash1, VALUE hash2)
- {
- return hash_equal(hash1, hash2, TRUE);
- }
- static int
- hash_i(VALUE key, VALUE val, VALUE arg)
- {
- st_index_t *hval = (st_index_t *)arg;
- st_index_t hdata[2];
- hdata[0] = rb_hash(key);
- hdata[1] = rb_hash(val);
- *hval ^= st_hash(hdata, sizeof(hdata), 0);
- return ST_CONTINUE;
- }
- static VALUE
- recursive_hash(VALUE hash, VALUE dummy, int recur)
- {
- st_index_t hval;
- if (!RHASH(hash)->ntbl)
- return LONG2FIX(0);
- hval = RHASH(hash)->ntbl->num_entries;
- if (!hval) return LONG2FIX(0);
- if (recur)
- hval = rb_hash_uint(rb_hash_start(rb_hash(rb_cHash)), hval);
- else
- rb_hash_foreach(hash, hash_i, (VALUE)&hval);
- hval = rb_hash_end(hval);
- return INT2FIX(hval);
- }
- /*
- * call-seq:
- * hsh.hash -> fixnum
- *
- * Compute a hash-code for this hash. Two hashes with the same content
- * will have the same hash code (and will compare using <code>eql?</code>).
- */
- static VALUE
- rb_hash_hash(VALUE hash)
- {
- return rb_exec_recursive_outer(recursive_hash, hash, 0);
- }
- static int
- rb_hash_invert_i(VALUE key, VALUE value, VALUE hash)
- {
- rb_hash_aset(hash, value, key);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.invert -> new_hash
- *
- * Returns a new hash created by using <i>hsh</i>'s values as keys, and
- * the keys as values.
- *
- * h = { "n" => 100, "m" => 100, "y" => 300, "d" => 200, "a" => 0 }
- * h.invert #=> {0=>"a", 100=>"m", 200=>"d", 300=>"y"}
- *
- */
- static VALUE
- rb_hash_invert(VALUE hash)
- {
- VALUE h = rb_hash_new();
- rb_hash_foreach(hash, rb_hash_invert_i, h);
- return h;
- }
- static int
- rb_hash_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
- {
- *value = arg;
- return ST_CONTINUE;
- }
- static NOINSERT_UPDATE_CALLBACK(rb_hash_update_callback)
- static int
- rb_hash_update_i(VALUE key, VALUE value, VALUE hash)
- {
- RHASH_UPDATE(hash, key, rb_hash_update_callback, value);
- return ST_CONTINUE;
- }
- static int
- rb_hash_update_block_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
- {
- VALUE newvalue = (VALUE)arg;
- if (existing) {
- newvalue = rb_yield_values(3, (VALUE)*key, (VALUE)*value, newvalue);
- }
- *value = (st_data_t)newvalue;
- return ST_CONTINUE;
- }
- static NOINSERT_UPDATE_CALLBACK(rb_hash_update_block_callback)
- static int
- rb_hash_update_block_i(VALUE key, VALUE value, VALUE hash)
- {
- RHASH_UPDATE(hash, key, rb_hash_update_block_callback, value);
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hsh.merge!(other_hash) -> hsh
- * hsh.update(other_hash) -> hsh
- * hsh.merge!(other_hash){|key, oldval, newval| block} -> hsh
- * hsh.update(other_hash){|key, oldval, newval| block} -> hsh
- *
- * Adds the contents of _other_hash_ to _hsh_. If no block is specified,
- * entries with duplicate keys are overwritten with the values from
- * _other_hash_, otherwise the value of each duplicate key is determined by
- * calling the block with the key, its value in _hsh_ and its value in
- * _other_hash_.
- *
- * h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge!(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
- *
- * h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge!(h2) { |key, v1, v2| v1 }
- * #=> {"a"=>100, "b"=>200, "c"=>300}
- */
- static VALUE
- rb_hash_update(VALUE hash1, VALUE hash2)
- {
- rb_hash_modify(hash1);
- hash2 = to_hash(hash2);
- if (rb_block_given_p()) {
- rb_hash_foreach(hash2, rb_hash_update_block_i, hash1);
- }
- else {
- rb_hash_foreach(hash2, rb_hash_update_i, hash1);
- }
- return hash1;
- }
- struct update_arg {
- VALUE hash;
- VALUE value;
- rb_hash_update_func *func;
- };
- static int
- rb_hash_update_func_callback(st_data_t *key, st_data_t *value, st_data_t arg0, int existing)
- {
- struct update_arg *arg = (struct update_arg *)arg0;
- VALUE newvalue = arg->value;
- if (existing) {
- newvalue = (*arg->func)((VALUE)*key, (VALUE)*value, newvalue);
- }
- *value = (st_data_t)newvalue;
- return ST_CONTINUE;
- }
- static NOINSERT_UPDATE_CALLBACK(rb_hash_update_func_callback)
- static int
- rb_hash_update_func_i(VALUE key, VALUE value, VALUE arg0)
- {
- struct update_arg *arg = (struct update_arg *)arg0;
- VALUE hash = arg->hash;
- arg->value = value;
- RHASH_UPDATE(hash, key, rb_hash_update_func_callback, arg);
- return ST_CONTINUE;
- }
- VALUE
- rb_hash_update_by(VALUE hash1, VALUE hash2, rb_hash_update_func *func)
- {
- rb_hash_modify(hash1);
- hash2 = to_hash(hash2);
- if (func) {
- struct update_arg arg;
- arg.hash = hash1;
- arg.func = func;
- rb_hash_foreach(hash2, rb_hash_update_func_i, (VALUE)&arg);
- }
- else {
- rb_hash_foreach(hash2, rb_hash_update_i, hash1);
- }
- return hash1;
- }
- /*
- * call-seq:
- * hsh.merge(other_hash) -> new_hash
- * hsh.merge(other_hash){|key, oldval, newval| block} -> new_hash
- *
- * Returns a new hash containing the contents of <i>other_hash</i> and
- * the contents of <i>hsh</i>. If no block is specified, the value for
- * entries with duplicate keys will be that of <i>other_hash</i>. Otherwise
- * the value for each duplicate key is determined by calling the block
- * with the key, its value in <i>hsh</i> and its value in <i>other_hash</i>.
- *
- * h1 = { "a" => 100, "b" => 200 }
- * h2 = { "b" => 254, "c" => 300 }
- * h1.merge(h2) #=> {"a"=>100, "b"=>254, "c"=>300}
- * h1.merge(h2){|key, oldval, newval| newval - oldval}
- * #=> {"a"=>100, "b"=>54, "c"=>300}
- * h1 #=> {"a"=>100, "b"=>200}
- *
- */
- static VALUE
- rb_hash_merge(VALUE hash1, VALUE hash2)
- {
- return rb_hash_update(rb_obj_dup(hash1), hash2);
- }
- static int
- assoc_i(VALUE key, VALUE val, VALUE arg)
- {
- VALUE *args = (VALUE *)arg;
- if (RTEST(rb_equal(args[0], key))) {
- args[1] = rb_assoc_new(key, val);
- return ST_STOP;
- }
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hash.assoc(obj) -> an_array or nil
- *
- * Searches through the hash comparing _obj_ with the key using <code>==</code>.
- * Returns the key-value pair (two elements array) or +nil+
- * if no match is found. See <code>Array#assoc</code>.
- *
- * h = {"colors" => ["red", "blue", "green"],
- * "letters" => ["a", "b", "c" ]}
- * h.assoc("letters") #=> ["letters", ["a", "b", "c"]]
- * h.assoc("foo") #=> nil
- */
- VALUE
- rb_hash_assoc(VALUE hash, VALUE obj)
- {
- VALUE args[2];
- args[0] = obj;
- args[1] = Qnil;
- rb_hash_foreach(hash, assoc_i, (VALUE)args);
- return args[1];
- }
- static int
- rassoc_i(VALUE key, VALUE val, VALUE arg)
- {
- VALUE *args = (VALUE *)arg;
- if (RTEST(rb_equal(args[0], val))) {
- args[1] = rb_assoc_new(key, val);
- return ST_STOP;
- }
- return ST_CONTINUE;
- }
- /*
- * call-seq:
- * hash.rassoc(obj) -> an_array or nil
- *
- * Searches through the hash comparing _obj_ with the value using <code>==</code>.
- * Returns the first key-value pair (two-element array) that matches. See
- * also <code>Array#rassoc</code>.
- *
- * a = {1=> "one", 2 => "two", 3 => "three", "ii" => "two"}
- * a.rassoc("two") #=> [2, "two"]
- * a.rassoc("four") #=> nil
- */
- VALUE
- rb_hash_rassoc(VALUE hash, VALUE obj)
- {
- VALUE args[2];
- args[0] = obj;
- args[1] = Qnil;
- rb_hash_foreach(hash, rassoc_i, (VALUE)args);
- return args[1];
- }
- /*
- * call-seq:
- * hash.flatten -> an_array
- * hash.flatten(level) -> an_array
- *
- * Returns a new array that is a one-dimensional flattening of this
- * hash. That is, for every key or value that is an array, extract
- * its elements into the new array. Unlike Array#flatten, this
- * method does not flatten recursively by default. The optional
- * <i>level</i> argument determines the level of recursion to flatten.
- *
- * a = {1=> "one", 2 => [2,"two"], 3 => "three"}
- * a.flatten # => [1, "one", 2, [2, "two"], 3, "three"]
- * a.flatten(2) # => [1, "one", 2, 2, "two", 3, "three"]
- */
- static VALUE
- rb_hash_flatten(int argc, VALUE *argv, VALUE hash)
- {
- VALUE ary, tmp;
- ary = rb_hash_to_a(hash);
- if (argc == 0) {
- argc = 1;
- tmp = INT2FIX(1);
- argv = &tmp;
- }
- rb_funcall2(ary, rb_intern("flatten!"), argc, argv);
- return ary;
- }
- /*
- * call-seq:
- * hsh.compare_by_identity -> hsh
- *
- * Makes <i>hsh</i> compare its keys by their identity, i.e. it
- * will consider exact same objects as same keys.
- *
- * h1 = { "a" => 100, "b" => 200, :c => "c" }
- * h1["a"] #=> 100
- * h1.compare_by_identity
- * h1.compare_by_identity? #=> true
- * h1["a"] #=> nil # different objects.
- * h1[:c] #=> "c" # same symbols are all same.
- *
- */
- static VALUE
- rb_hash_compare_by_id(VALUE hash)
- {
- rb_hash_modify(hash);
- RHASH(hash)->ntbl->type = &identhash;
- rb_hash_rehash(hash);
- return hash;
- }
- /*
- * call-seq:
- * hsh.compare_by_identity? -> true or false
- *
- * Returns <code>true</code> if <i>hsh</i> will compare its keys by
- * their identity. Also see <code>Hash#compare_by_identity</code>.
- *
- */
- static VALUE
- rb_hash_compare_by_id_p(VALUE hash)
- {
- if (!RHASH(hash)->ntbl)
- return Qfalse;
- if (RHASH(hash)->ntbl->type == &identhash) {
- return Qtrue;
- }
- return Qfalse;
- }
- static int path_tainted = -1;
- static char **origenviron;
- #ifdef _WIN32
- #de…
Large files files are truncated, but you can click here to view the full file