aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hash.c163
-rw-r--r--insns.def13
-rw-r--r--internal.h1
-rw-r--r--vm.c31
4 files changed, 117 insertions, 91 deletions
diff --git a/hash.c b/hash.c
index b583390819..6544c199bb 100644
--- a/hash.c
+++ b/hash.c
@@ -626,6 +626,103 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
return hash;
}
+static inline VALUE
+hash_alloc_from_st(VALUE klass, st_table *ntbl)
+{
+ VALUE h = hash_alloc(klass);
+ RHASH(h)->ntbl = ntbl;
+ return h;
+}
+
+static inline int
+hash_insert_raw(st_table *tbl, VALUE key, VALUE val)
+{
+ st_data_t v = (st_data_t)val;
+ st_data_t k = (rb_obj_class(k) == rb_cString) ?
+ (st_data_t)rb_str_new_frozen(key) :
+ (st_data_t)key;
+
+ return st_insert(tbl, k, v);
+}
+
+static VALUE
+rb_hash_new_from_values_with_klass(long argc, const VALUE *argv, VALUE klass)
+{
+ long i;
+ st_table *t;
+
+ if (argc % 2) {
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
+ }
+
+ t = st_init_table_with_size(&objhash, argc / 2);
+ for (i = 0; i < argc; /* */) {
+ VALUE key = argv[i++];
+ VALUE val = argv[i++];
+
+ hash_insert_raw(t, key, val);
+ }
+ return hash_alloc_from_st(klass, t);
+}
+
+VALUE
+rb_hash_new_from_values(long argc, const VALUE *argv)
+{
+ return rb_hash_new_from_values_with_klass(argc, argv, rb_cHash);
+}
+
+static VALUE
+rb_hash_new_from_object(VALUE klass, VALUE obj)
+{
+ VALUE tmp = rb_hash_s_try_convert(Qnil, obj);
+ if (!NIL_P(tmp)) {
+ VALUE hash = hash_alloc(klass);
+ if (RHASH(tmp)->ntbl) {
+ RHASH(hash)->ntbl = st_copy(RHASH(tmp)->ntbl);
+ }
+ return hash;
+ }
+
+ tmp = rb_check_array_type(obj);
+ if (!NIL_P(tmp)) {
+ long i;
+ long len = RARRAY_LEN(tmp);
+ st_table *tbl = len ? st_init_table_with_size(&objhash, len) : NULL;
+ for (i = 0; i < len; ++i) {
+ VALUE e = RARRAY_AREF(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_AREF(v, 1);
+ case 1:
+ key = RARRAY_AREF(v, 0);
+ hash_insert_raw(tbl, key, val);
+ }
+ }
+ return hash_alloc_from_st(klass, tbl);
+ }
+
+ rb_raise(rb_eArgError, "odd number of arguments for Hash");
+}
+
/*
* call-seq:
* Hash[ key, value, ... ] -> new_hash
@@ -649,69 +746,11 @@ rb_hash_initialize(int argc, VALUE *argv, VALUE hash)
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_AREF(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_AREF(v, 1);
- case 1:
- key = RARRAY_AREF(v, 0);
- rb_hash_aset(hash, key, val);
- }
- }
- return hash;
- }
+ switch (argc) {
+ case 0: return hash_alloc(klass);
+ case 1: return rb_hash_new_from_object(klass, argv[0]);
+ default: return rb_hash_new_from_values_with_klass(argc, argv, klass);
}
- if (argc % 2 != 0) {
- rb_raise(rb_eArgError, "odd number of arguments for Hash");
- }
-
- hash = hash_alloc(klass);
- if (argc > 0) {
- RHASH(hash)->ntbl = st_init_table_with_size(&objhash, argc / 2);
- }
- for (i=0; i<argc; i+=2) {
- rb_hash_aset(hash, argv[i], argv[i + 1]);
- }
-
- return hash;
}
static VALUE
diff --git a/insns.def b/insns.def
index a056b221d5..36ba48eaea 100644
--- a/insns.def
+++ b/insns.def
@@ -492,16 +492,13 @@ newhash
(...)
(VALUE val) // inc += 1 - num;
{
- rb_num_t i;
-
RUBY_DTRACE_CREATE_HOOK(HASH, num);
- val = rb_hash_new();
-
- for (i = num; i > 0; i -= 2) {
- const VALUE v = TOPN(i - 2);
- const VALUE k = TOPN(i - 1);
- rb_hash_aset(val, k, v);
+ if (num) {
+ val = rb_hash_new_from_values(num, STACK_ADDR_FROM_TOP(num));
+ }
+ else {
+ val = rb_hash_new();
}
POPN(num);
}
diff --git a/internal.h b/internal.h
index afea318376..de89418856 100644
--- a/internal.h
+++ b/internal.h
@@ -1236,6 +1236,7 @@ VALUE rb_hash_rehash(VALUE hash);
int rb_hash_add_new_element(VALUE hash, VALUE key, VALUE val);
#define HASH_DELETED FL_USER1
#define HASH_PROC_DEFAULT FL_USER2
+extern VALUE rb_hash_new_from_values(long, const VALUE *);
/* inits.c */
void rb_call_inits(void);
diff --git a/vm.c b/vm.c
index e64c072fdc..968f2dbf3e 100644
--- a/vm.c
+++ b/vm.c
@@ -2646,7 +2646,6 @@ m_core_set_postexe(VALUE self)
return Qnil;
}
-static VALUE core_hash_merge_ary(VALUE hash, VALUE ary);
static VALUE core_hash_from_ary(VALUE ary);
static VALUE core_hash_merge_kwd(int argc, VALUE *argv);
@@ -2656,7 +2655,6 @@ core_hash_merge(VALUE hash, long argc, const VALUE *argv)
long i;
Check_Type(hash, T_HASH);
- VM_ASSERT(argc % 2 == 0);
for (i=0; i<argc; i+=2) {
rb_hash_aset(hash, argv[i], argv[i+1]);
}
@@ -2674,27 +2672,18 @@ m_core_hash_from_ary(VALUE self, VALUE ary)
static VALUE
core_hash_from_ary(VALUE ary)
{
- VALUE hash = rb_hash_new();
+ long n;
- RUBY_DTRACE_CREATE_HOOK(HASH, (Check_Type(ary, T_ARRAY), RARRAY_LEN(ary)));
- return core_hash_merge_ary(hash, ary);
-}
-
-#if 0
-static VALUE
-m_core_hash_merge_ary(VALUE self, VALUE hash, VALUE ary)
-{
- REWIND_CFP(core_hash_merge_ary(hash, ary));
- return hash;
-}
-#endif
-
-static VALUE
-core_hash_merge_ary(VALUE hash, VALUE ary)
-{
Check_Type(ary, T_ARRAY);
- core_hash_merge(hash, RARRAY_LEN(ary), RARRAY_CONST_PTR(ary));
- return hash;
+ n = RARRAY_LEN(ary);
+ RUBY_DTRACE_CREATE_HOOK(HASH, n);
+ if (n) {
+ VM_ASSERT(n % 2 == 0);
+ return rb_hash_new_from_values(n, RARRAY_PTR(ary));
+ }
+ else {
+ return rb_hash_new();
+ }
}
static VALUE