summaryrefslogtreecommitdiff
path: root/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'hash.c')
-rw-r--r--hash.c268
1 files changed, 142 insertions, 126 deletions
diff --git a/hash.c b/hash.c
index 8b1d5fde8b..9ba8c3d4fe 100644
--- a/hash.c
+++ b/hash.c
@@ -471,58 +471,11 @@ ar_set_entry(VALUE hash, unsigned int index, st_data_t key, st_data_t val, st_ha
#define RHASH_AR_TABLE_SIZE(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \
RHASH_AR_TABLE_SIZE_RAW(h))
-#define RHASH_AR_TABLE_BOUND_RAW(h) \
- ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \
- (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT)))
-
-#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s)
-#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type)
-
#define HASH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(HASH_DEBUG, expr, #expr)
-static inline unsigned int
-RHASH_AR_TABLE_BOUND(VALUE h)
-{
- HASH_ASSERT(RHASH_AR_TABLE_P(h));
- const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h);
- HASH_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE);
- return bound;
-}
-
#if HASH_DEBUG
#define hash_verify(hash) hash_verify_(hash, __FILE__, __LINE__)
-void
-rb_hash_dump(VALUE hash)
-{
- rb_obj_info_dump(hash);
-
- if (RHASH_AR_TABLE_P(hash)) {
- unsigned i, bound = RHASH_AR_TABLE_BOUND(hash);
-
- fprintf(stderr, " size:%u bound:%u\n",
- RHASH_AR_TABLE_SIZE(hash), bound);
-
- for (i=0; i<bound; i++) {
- st_data_t k, v;
-
- if (!ar_cleared_entry(hash, i)) {
- char b1[0x100], b2[0x100];
- ar_table_pair *pair = RHASH_AR_TABLE_REF(hash, i);
- k = pair->key;
- v = pair->val;
- fprintf(stderr, " %d key:%s val:%s hint:%02x\n", i,
- rb_raw_obj_info(b1, 0x100, k),
- rb_raw_obj_info(b2, 0x100, v),
- ar_hint(hash, i));
- }
- else {
- fprintf(stderr, " %d empty\n", i);
- }
- }
- }
-}
-
static VALUE
hash_verify_(VALUE hash, const char *file, int line)
{
@@ -575,7 +528,7 @@ hash_st_table_init(VALUE hash, const struct st_hash_type *type, st_index_t size)
RHASH_SET_ST_FLAG(hash);
}
-void
+static void
rb_hash_st_table_set(VALUE hash, st_table *st)
{
HASH_ASSERT(st != NULL);
@@ -635,6 +588,7 @@ RHASH_AR_TABLE_SIZE_DEC(VALUE h)
static inline void
RHASH_AR_TABLE_CLEAR(VALUE h)
{
+ RUBY_ASSERT(rb_gc_obj_slot_size(h) >= sizeof(struct RHash) + sizeof(ar_table));
RBASIC(h)->flags &= ~RHASH_AR_TABLE_SIZE_MASK;
RBASIC(h)->flags &= ~RHASH_AR_TABLE_BOUND_MASK;
@@ -750,6 +704,8 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE];
unsigned int bound, size;
+ RUBY_ASSERT(rb_gc_obj_slot_size(hash) >= sizeof(struct RHash) + sizeof(ar_table));
+
// prepare hash values
do {
st_data_t keys[RHASH_AR_TABLE_MAX_SIZE];
@@ -774,7 +730,7 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
st_init_existing_table_with_size(new_tab, &objhash, size);
ar_each_key(ar, bound, ar_each_key_insert, NULL, new_tab, hashes);
hash_ar_free_and_clear_table(hash);
- RHASH_ST_TABLE_SET(hash, new_tab);
+ rb_hash_st_table_set(hash, new_tab);
return RHASH_ST_TABLE(hash);
}
}
@@ -1173,12 +1129,15 @@ ar_values(VALUE hash, st_data_t *values, st_index_t size)
static ar_table*
ar_copy(VALUE hash1, VALUE hash2)
{
+ RUBY_ASSERT(rb_gc_obj_slot_size(hash1) >= sizeof(struct RHash) + sizeof(ar_table));
ar_table *old_tab = RHASH_AR_TABLE(hash2);
ar_table *new_tab = RHASH_AR_TABLE(hash1);
- *new_tab = *old_tab;
+ unsigned int bound = RHASH_AR_TABLE_BOUND(hash2);
+ new_tab->ar_hint.word = old_tab->ar_hint.word;
+ MEMCPY(&new_tab->pairs, &old_tab->pairs, ar_table_pair, bound);
RHASH_AR_TABLE(hash1)->ar_hint.word = RHASH_AR_TABLE(hash2)->ar_hint.word;
- RHASH_AR_TABLE_BOUND_SET(hash1, RHASH_AR_TABLE_BOUND(hash2));
+ RHASH_AR_TABLE_BOUND_SET(hash1, bound);
RHASH_AR_TABLE_SIZE_SET(hash1, RHASH_AR_TABLE_SIZE(hash2));
rb_gc_writebarrier_remember(hash1);
@@ -1204,17 +1163,13 @@ hash_st_free(VALUE hash)
{
HASH_ASSERT(RHASH_ST_TABLE_P(hash));
- st_table *tab = RHASH_ST_TABLE(hash);
-
- xfree(tab->bins);
- xfree(tab->entries);
+ rb_st_free_embedded_table(RHASH_ST_TABLE(hash));
}
static void
hash_st_free_and_clear_table(VALUE hash)
{
hash_st_free(hash);
-
RHASH_ST_CLEAR(hash);
}
@@ -1466,14 +1421,9 @@ compact_after_delete(VALUE hash)
static VALUE
hash_alloc_flags(VALUE klass, VALUE flags, VALUE ifnone, bool st)
{
- const VALUE wb = (RGENGC_WB_PROTECTED_HASH ? FL_WB_PROTECTED : 0);
const size_t size = sizeof(struct RHash) + (st ? sizeof(st_table) : sizeof(ar_table));
-
- NEWOBJ_OF(hash, struct RHash, klass, T_HASH | wb | flags, size, 0);
-
- RHASH_SET_IFNONE((VALUE)hash, ifnone);
-
- return (VALUE)hash;
+ VALUE hash = rb_newobj_of(klass, T_HASH | flags, size);
+ return rb_hash_set_ifnone(hash, ifnone);
}
static VALUE
@@ -1525,9 +1475,30 @@ rb_hash_new_capa(long capa)
return rb_hash_new_with_size((st_index_t)capa);
}
+VALUE
+rb_hash_alloc_fixed_size(VALUE klass, st_index_t size)
+{
+ VALUE ret;
+ if (size > RHASH_AR_TABLE_MAX_SIZE) {
+ ret = hash_alloc_flags(klass, 0, Qnil, true);
+ hash_st_table_init(ret, &objhash, size);
+ }
+ else {
+ size_t slot_size = sizeof(struct RHash) + offsetof(ar_table, pairs) + size * sizeof(ar_table_pair);
+ ret = rb_newobj_of(klass, T_HASH, slot_size);
+ }
+
+ RHASH_SET_IFNONE(ret, Qnil);
+ return ret;
+}
+
static VALUE
hash_copy(VALUE ret, VALUE hash)
{
+ if (rb_hash_compare_by_id_p(hash)) {
+ rb_gc_register_pinning_obj(ret);
+ }
+
if (RHASH_AR_TABLE_P(hash)) {
if (RHASH_AR_TABLE_P(ret)) {
ar_copy(ret, hash);
@@ -1585,9 +1556,8 @@ rb_hash_dup(VALUE hash)
const VALUE flags = RBASIC(hash)->flags;
VALUE ret = hash_dup(hash, rb_obj_class(hash), flags & RHASH_PROC_DEFAULT);
- if (rb_obj_exivar_p(hash)) {
- rb_copy_generic_ivar(ret, hash);
- }
+ rb_copy_generic_ivar(ret, hash);
+
return ret;
}
@@ -1970,6 +1940,9 @@ rb_hash_rehash_i(VALUE key, VALUE value, VALUE arg)
else {
st_insert(RHASH_ST_TABLE(arg), (st_data_t)key, (st_data_t)value);
}
+
+ RB_OBJ_WRITTEN(arg, Qundef, key);
+ RB_OBJ_WRITTEN(arg, Qundef, value);
return ST_CONTINUE;
}
@@ -2013,7 +1986,7 @@ rb_hash_rehash(VALUE hash)
rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp);
hash_st_free(hash);
- RHASH_ST_TABLE_SET(hash, tbl);
+ rb_hash_st_table_set(hash, tbl);
RHASH_ST_CLEAR(tmp);
}
hash_verify(hash);
@@ -2672,9 +2645,9 @@ rb_hash_slice(int argc, VALUE *argv, VALUE hash)
* Returns a copy of +self+ that excludes entries for the given +keys+;
* any +keys+ that are not found are ignored:
*
- * h = {foo:0, bar: 1, baz: 2} # => {:foo=>0, :bar=>1, :baz=>2}
- * h.except(:baz, :foo) # => {:bar=>1}
- * h.except(:bar, :nosuch) # => {:foo=>0, :baz=>2}
+ * h = {foo:0, bar: 1, baz: 2} # => {foo: 0, bar: 1, baz: 2}
+ * h.except(:baz, :foo) # => {bar: 1}
+ * h.except(:bar, :nosuch) # => {foo: 0, baz: 2}
*
* Related: see {Methods for Deleting}[rdoc-ref:Hash@Methods+for+Deleting].
*/
@@ -2907,7 +2880,7 @@ hash_aset(st_data_t *key, st_data_t *val, struct update_arg *arg, int existing)
VALUE
rb_hash_key_str(VALUE key)
{
- if (!rb_obj_exivar_p(key) && RBASIC_CLASS(key) == rb_cString) {
+ if (!rb_obj_gen_fields_p(key) && RBASIC_CLASS(key) == rb_cString) {
return rb_fstring(key);
}
else {
@@ -2951,7 +2924,7 @@ NOINSERT_UPDATE_CALLBACK(hash_aset_str)
* h = {foo: 0, bar: 1}
* h[:baz] = 2 # => 2
* h[:baz] # => 2
- * h # => {:foo=>0, :bar=>1, :baz=>2}
+ * h # => {foo: 0, bar: 1, baz: 2}
*
* Related: #[]; see also {Methods for Assigning}[rdoc-ref:Hash@Methods+for+Assigning].
*/
@@ -4007,17 +3980,13 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
/*
* call-seq:
- * self == object -> true or false
- *
- * Returns whether +self+ and +object+ are equal.
+ * self == other -> true or false
*
- * Returns +true+ if all of the following are true:
- *
- * - +object+ is a +Hash+ object (or can be converted to one).
- * - +self+ and +object+ have the same keys (regardless of order).
- * - For each key +key+, <tt>self[key] == object[key]</tt>.
+ * Returns whether all of the following are true:
*
- * Otherwise, returns +false+.
+ * - +other+ is a +Hash+ object (or can be converted to one).
+ * - +self+ and +other+ have the same keys (regardless of order).
+ * - For each key +key+, <tt>self[key] == other[key]</tt>.
*
* Examples:
*
@@ -4026,8 +3995,8 @@ hash_equal(VALUE hash1, VALUE hash2, int eql)
* h == {bar: 1, foo: 0} # => true # Equal entries (different order).
* h == 1 # => false # Object not a hash.
* h == {} # => false # Different number of entries.
- * h == {foo: 0, bar: 1} # => false # Different key.
- * h == {foo: 0, bar: 1} # => false # Different value.
+ * h == {foo: 0, bat: 1} # => false # Different key.
+ * h == {foo: 0, bar: 2} # => false # Different value.
*
* Related: see {Methods for Comparing}[rdoc-ref:Hash@Methods+for+Comparing].
*/
@@ -4524,21 +4493,21 @@ flatten_i(VALUE key, VALUE val, VALUE ary)
* Examples; note that entry <tt>foo: {bar: 1, baz: 2}</tt> is never flattened.
*
* h = {foo: {bar: 1, baz: 2}, bat: [:bam, [:bap, [:bah]]]}
- * h.flatten(1) # => [:foo, {:bar=>1, :baz=>2}, :bat, [:bam, [:bap, [:bah]]]]
- * h.flatten(2) # => [:foo, {:bar=>1, :baz=>2}, :bat, :bam, [:bap, [:bah]]]
- * h.flatten(3) # => [:foo, {:bar=>1, :baz=>2}, :bat, :bam, :bap, [:bah]]
- * h.flatten(4) # => [:foo, {:bar=>1, :baz=>2}, :bat, :bam, :bap, :bah]
- * h.flatten(5) # => [:foo, {:bar=>1, :baz=>2}, :bat, :bam, :bap, :bah]
+ * h.flatten(1) # => [:foo, {bar: 1, baz: 2}, :bat, [:bam, [:bap, [:bah]]]]
+ * h.flatten(2) # => [:foo, {bar: 1, baz: 2}, :bat, :bam, [:bap, [:bah]]]
+ * h.flatten(3) # => [:foo, {bar: 1, baz: 2}, :bat, :bam, :bap, [:bah]]
+ * h.flatten(4) # => [:foo, {bar: 1, baz: 2}, :bat, :bam, :bap, :bah]
+ * h.flatten(5) # => [:foo, {bar: 1, baz: 2}, :bat, :bam, :bap, :bah]
*
* With negative integer +depth+,
* flattens all levels:
*
- * h.flatten(-1) # => [:foo, {:bar=>1, :baz=>2}, :bat, :bam, :bap, :bah]
+ * h.flatten(-1) # => [:foo, {bar: 1, baz: 2}, :bat, :bam, :bap, :bah]
*
* With +depth+ zero,
* returns the equivalent of #to_a:
*
- * h.flatten(0) # => [[:foo, {:bar=>1, :baz=>2}], [:bat, [:bam, [:bap, [:bah]]]]]
+ * h.flatten(0) # => [[:foo, {bar: 1, baz: 2}], [:bat, [:bam, [:bap, [:bah]]]]]
*
* Related: see {Methods for Converting}[rdoc-ref:Hash@Methods+for+Converting].
*/
@@ -4649,7 +4618,7 @@ rb_hash_compact_bang(VALUE hash)
* returns +self+:
*
* By default, two keys are considered to be the same key
- * if and only if they are _equal_ objects (per method #==):
+ * if and only if they are _equal_ objects (per method #eql?):
*
* h = {}
* h['x'] = 0
@@ -4700,10 +4669,12 @@ rb_hash_compare_by_id(VALUE hash)
// We know for sure `identtable` is an st table,
// so we can skip `ar_force_convert_table` here.
- RHASH_ST_TABLE_SET(hash, identtable);
+ rb_hash_st_table_set(hash, identtable);
RHASH_ST_CLEAR(tmp);
}
+ rb_gc_register_pinning_obj(hash);
+
return hash;
}
@@ -4733,6 +4704,7 @@ rb_ident_hash_new(void)
{
VALUE hash = rb_hash_new();
hash_st_table_init(hash, &identhash, 0);
+ rb_gc_register_pinning_obj(hash);
return hash;
}
@@ -4741,6 +4713,7 @@ rb_ident_hash_new_with_size(st_index_t size)
{
VALUE hash = rb_hash_new();
hash_st_table_init(hash, &identhash, size);
+ rb_gc_register_pinning_obj(hash);
return hash;
}
@@ -4882,7 +4855,7 @@ rb_hash_any_p(int argc, VALUE *argv, VALUE hash)
* h.dig(:hello) # => nil
* h.default_proc = -> (hash, _key) { hash }
* h.dig(:hello, :world)
- * # => {:foo=>{:bar=>[:a, :b, :c]}}
+ * # => {foo: {bar: [:a, :b, :c]}}
*
* Related: {Methods for Fetching}[rdoc-ref:Hash@Methods+for+Fetching].
*/
@@ -4919,10 +4892,9 @@ hash_le(VALUE hash1, VALUE hash2)
/*
* call-seq:
- * self <= other_hash -> true or false
+ * self <= other -> true or false
*
- * Returns +true+ if the entries of +self+ are a subset of the entries of +other_hash+,
- * +false+ otherwise:
+ * Returns whether the entries of +self+ are a subset of the entries of +other+:
*
* h0 = {foo: 0, bar: 1}
* h1 = {foo: 0, bar: 1, baz: 2}
@@ -4930,7 +4902,7 @@ hash_le(VALUE hash1, VALUE hash2)
* h0 <= h1 # => true
* h1 <= h0 # => false
*
- * See {Hash Inclusion}[rdoc-ref:hash_inclusion.rdoc].
+ * See {Hash Inclusion}[rdoc-ref:language/hash_inclusion.rdoc].
*
* Raises TypeError if +other_hash+ is not a hash and cannot be converted to a hash.
*
@@ -4946,20 +4918,19 @@ rb_hash_le(VALUE hash, VALUE other)
/*
* call-seq:
- * self < other_hash -> true or false
+ * self < other -> true or false
*
- * Returns +true+ if the entries of +self+ are a proper subset of the entries of +other_hash+,
- * +false+ otherwise:
+ * Returns whether the entries of +self+ are a proper subset of the entries of +other+:
*
* h = {foo: 0, bar: 1}
* h < {foo: 0, bar: 1, baz: 2} # => true # Proper subset.
* h < {baz: 2, bar: 1, foo: 0} # => true # Order may differ.
* h < h # => false # Not a proper subset.
* h < {bar: 1, foo: 0} # => false # Not a proper subset.
- * h < {foo: 0, bar: 1, baz: 2} # => false # Different key.
- * h < {foo: 0, bar: 1, baz: 2} # => false # Different value.
+ * h < {foo: 0, bat: 1, baz: 2} # => false # Different key.
+ * h < {foo: 0, bar: 3, baz: 2} # => false # Different value.
*
- * See {Hash Inclusion}[rdoc-ref:hash_inclusion.rdoc].
+ * See {Hash Inclusion}[rdoc-ref:language/hash_inclusion.rdoc].
*
* Raises TypeError if +other_hash+ is not a hash and cannot be converted to a hash.
*
@@ -4975,10 +4946,9 @@ rb_hash_lt(VALUE hash, VALUE other)
/*
* call-seq:
- * self >= other_hash -> true or false
+ * self >= other -> true or false
*
- * Returns +true+ if the entries of +self+ are a superset of the entries of +other_hash+,
- * +false+ otherwise:
+ * Returns whether the entries of +self+ are a superset of the entries of +other+:
*
* h0 = {foo: 0, bar: 1, baz: 2}
* h1 = {foo: 0, bar: 1}
@@ -4986,7 +4956,7 @@ rb_hash_lt(VALUE hash, VALUE other)
* h0 >= h0 # => true
* h1 >= h0 # => false
*
- * See {Hash Inclusion}[rdoc-ref:hash_inclusion.rdoc].
+ * See {Hash Inclusion}[rdoc-ref:language/hash_inclusion.rdoc].
*
* Raises TypeError if +other_hash+ is not a hash and cannot be converted to a hash.
*
@@ -5002,20 +4972,19 @@ rb_hash_ge(VALUE hash, VALUE other)
/*
* call-seq:
- * self > other_hash -> true or false
+ * self > other -> true or false
*
- * Returns +true+ if the entries of +self+ are a proper superset of the entries of +other_hash+,
- * +false+ otherwise:
+ * Returns whether the entries of +self+ are a proper superset of the entries of +other+:
*
* h = {foo: 0, bar: 1, baz: 2}
* h > {foo: 0, bar: 1} # => true # Proper superset.
* h > {bar: 1, foo: 0} # => true # Order may differ.
* h > h # => false # Not a proper superset.
* h > {baz: 2, bar: 1, foo: 0} # => false # Not a proper superset.
- * h > {foo: 0, bar: 1} # => false # Different key.
- * h > {foo: 0, bar: 1} # => false # Different value.
+ * h > {foo: 0, bat: 1} # => false # Different key.
+ * h > {foo: 0, bar: 3} # => false # Different value.
*
- * See {Hash Inclusion}[rdoc-ref:hash_inclusion.rdoc].
+ * See {Hash Inclusion}[rdoc-ref:language/hash_inclusion.rdoc].
*
* Raises TypeError if +other_hash+ is not a hash and cannot be converted to a hash.
*
@@ -5048,6 +5017,8 @@ hash_proc_call(RB_BLOCK_CALL_FUNC_ARGLIST(key, hash))
* proc.call(:foo) # => 0
* proc.call(:bar) # => 1
* proc.call(:nosuch) # => nil
+ * h.default_proc = proc { |hash, key| "Missing key: #{key}" } # This affect the existing proc object
+ * proc.call(:nosuch) # => "Missing key: #{nosuch}"
*
* Related: see {Methods for Converting}[rdoc-ref:Hash@Methods+for+Converting].
*/
@@ -5138,6 +5109,14 @@ rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
}
}
+VALUE
+rb_hash_new_with_bulk_insert(long argc, const VALUE *argv)
+{
+ VALUE val = rb_hash_new_with_size(argc / 2);
+ rb_hash_bulk_insert(argc, argv, val);
+ return val;
+}
+
static char **origenviron;
#ifdef _WIN32
#define GET_ENVIRON(e) ((e) = rb_w32_get_environ())
@@ -5368,7 +5347,7 @@ static VALUE
env_fetch(int argc, VALUE *argv, VALUE _)
{
VALUE key;
- long block_given;
+ int block_given;
const char *nam;
VALUE env;
@@ -5391,6 +5370,42 @@ env_fetch(int argc, VALUE *argv, VALUE _)
return env;
}
+/*
+ * call-seq:
+ * ENV.fetch_values(*names) -> array of values
+ * ENV.fetch_values(*names) {|name| ... } -> array of values
+ *
+ * Returns an Array containing the environment variable values associated with
+ * the given names:
+ * ENV.replace('foo' => '0', 'bar' => '1', 'baz' => '2')
+ * ENV.fetch_values('foo', 'baz') # => ["0", "2"]
+ *
+ * Otherwise if a block is given yields +name+ to
+ * the block and returns the block's return value:
+ * ENV.fetch_values('foo', 'bam') {|key| key.to_s} # => ["0", "bam"]
+ *
+ * Raises KeyError if +name+ is valid, but not found and block is not given:
+ * ENV.fetch_values('foo', 'bam') # Raises KeyError (key not found: "bam")
+ *
+ * Returns an empty Array if no names given.
+ *
+ * Raises an exception if any name is invalid.
+ * See {Invalid Names and Values}[rdoc-ref:ENV@Invalid+Names+and+Values].
+ */
+
+static VALUE
+env_fetch_values(int argc, VALUE *argv, VALUE ehash)
+{
+ VALUE result = rb_ary_new2(argc);
+ long i;
+
+ for (i=0; i<argc; i++) {
+ rb_ary_push(result, env_fetch(1, &argv[i], ehash));
+ }
+
+ return result;
+}
+
#if defined(_WIN32) || (defined(HAVE_SETENV) && defined(HAVE_UNSETENV))
#elif defined __sun
static int
@@ -6470,7 +6485,7 @@ env_rassoc(VALUE dmy, VALUE obj)
while (*env) {
const char *p = *env;
- char *s = strchr(p, '=');
+ const char *s = strchr(p, '=');
if (s++) {
long len = strlen(s);
if (RSTRING_LEN(obj) == len && strncmp(s, RSTRING_PTR(obj), len) == 0) {
@@ -6690,7 +6705,7 @@ env_shift(VALUE _)
char **env = GET_ENVIRON(environ);
if (*env) {
const char *p = *env;
- char *s = strchr(p, '=');
+ const char *s = strchr(p, '=');
if (s) {
key = env_str_new(p, s-p, enc);
VALUE val = env_str_new2(getenv(RSTRING_PTR(key)), enc);
@@ -6912,7 +6927,7 @@ static const rb_data_type_t env_data_type = {
* A \Hash object maps each of its unique keys to a specific value.
*
* A hash has certain similarities to an Array, but:
-
+ *
* - An array index is always an integer.
* - A hash key can be (almost) any object.
*
@@ -7248,8 +7263,8 @@ static const rb_data_type_t env_data_type = {
*
* First, what's elsewhere. Class +Hash+:
*
- * - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
- * - Includes {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
+ * - Inherits from {class Object}[rdoc-ref:Object@Whats+Here].
+ * - Includes {module Enumerable}[rdoc-ref:Enumerable@Whats+Here],
* which provides dozens of additional methods.
*
* Here, class +Hash+ provides methods that are useful for:
@@ -7313,7 +7328,7 @@ static const rb_data_type_t env_data_type = {
* - #keys: Returns an array containing all keys in +self+.
* - #rassoc: Returns a 2-element array consisting of the key and value
* of the first-found entry having a given value.
- * - #values: Returns an array containing all values in +self+/
+ * - #values: Returns an array containing all values in +self+.
* - #values_at: Returns an array containing values for given keys.
*
* ==== Methods for Assigning
@@ -7362,7 +7377,6 @@ static const rb_data_type_t env_data_type = {
*
* ==== Methods for Transforming Keys and Values
*
- * - #flatten!: Returns +self+, flattened.
* - #invert: Returns a hash with the each key-value pair inverted.
* - #transform_keys: Returns a copy of +self+ with modified keys.
* - #transform_keys!: Modifies keys in +self+
@@ -7474,7 +7488,8 @@ Init_Hash(void)
rb_define_singleton_method(rb_cHash, "ruby2_keywords_hash?", rb_hash_s_ruby2_keywords_hash_p, 1);
rb_define_singleton_method(rb_cHash, "ruby2_keywords_hash", rb_hash_s_ruby2_keywords_hash, 1);
- rb_cHash_empty_frozen = rb_hash_freeze(rb_hash_new());
+ rb_cHash_empty_frozen = rb_hash_freeze(rb_hash_alloc_fixed_size(rb_cHash, 0));
+ RB_OBJ_SET_SHAREABLE(rb_cHash_empty_frozen);
rb_vm_register_global_object(rb_cHash_empty_frozen);
/* Document-class: ENV
@@ -7560,8 +7575,8 @@ Init_Hash(void)
*
* First, what's elsewhere. Class +ENV+:
*
- * - Inherits from {class Object}[rdoc-ref:Object@What-27s+Here].
- * - Extends {module Enumerable}[rdoc-ref:Enumerable@What-27s+Here],
+ * - Inherits from {class Object}[rdoc-ref:Object@Whats+Here].
+ * - Extends {module Enumerable}[rdoc-ref:Enumerable@Whats+Here],
*
* Here, class +ENV+ provides methods that are useful for:
*
@@ -7613,6 +7628,7 @@ Init_Hash(void)
* - ::clone: Raises an exception.
* - ::except: Returns a hash of all name/value pairs except those given.
* - ::fetch: Returns the value for the given name.
+ * - ::fetch_values: Returns array containing the values associated with given names.
* - ::inspect: Returns the contents of +ENV+ as a string.
* - ::invert: Returns a hash whose keys are the +ENV+ values,
and whose values are the corresponding +ENV+ names.
@@ -7644,11 +7660,11 @@ Init_Hash(void)
origenviron = environ;
envtbl = TypedData_Wrap_Struct(rb_cObject, &env_data_type, NULL);
rb_extend_object(envtbl, rb_mEnumerable);
- FL_SET_RAW(envtbl, RUBY_FL_SHAREABLE);
-
+ RB_OBJ_SET_SHAREABLE(envtbl);
rb_define_singleton_method(envtbl, "[]", rb_f_getenv, 1);
rb_define_singleton_method(envtbl, "fetch", env_fetch, -1);
+ rb_define_singleton_method(envtbl, "fetch_values", env_fetch_values, -1);
rb_define_singleton_method(envtbl, "[]=", env_aset_m, 2);
rb_define_singleton_method(envtbl, "store", env_aset_m, 2);
rb_define_singleton_method(envtbl, "each", env_each_pair, 0);