// Used by Google::Protobuf.deep_copy but not exposed directly. VALUE Map_deep_copy(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE new_map = Map_new_this_type(_self); Map* new_self = ruby_to_Map(new_map); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); upb_value dup; void* dup_mem = value_memory(&dup); native_slot_deep_copy(self->value_type, dup_mem, mem); if (!upb_strtable_insert2(&new_self->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), dup)) { rb_raise(rb_eRuntimeError, "Error inserting value into new table"); } } return new_map; }
/* * call-seq: * Map.==(other) => boolean * * Compares this map to another. Maps are equal if they have identical key sets, * and for each key, the values in both maps compare equal. Elements are * compared as per normal Ruby semantics, by calling their :== methods (or * performing a more efficient comparison for primitive types). * * Maps with dissimilar key types or value types/typeclasses are never equal, * even if value comparison (for example, between integers and floats) would * have otherwise indicated that every element has equal value. */ VALUE Map_eq(VALUE _self, VALUE _other) { Map* self = ruby_to_Map(_self); Map* other; upb_strtable_iter it; // Allow comparisons to Ruby hashmaps by converting to a temporary Map // instance. Slow, but workable. if (TYPE(_other) == T_HASH) { VALUE other_map = Map_new_this_type(_self); Map_merge_into_self(other_map, _other); _other = other_map; } other = ruby_to_Map(_other); if (self == other) { return Qtrue; } if (self->key_type != other->key_type || self->value_type != other->value_type || self->value_type_class != other->value_type_class) { return Qfalse; } if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) { return Qfalse; } // For each member of self, check that an equal member exists at the same key // in other. for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); upb_value other_v; void* other_mem = value_memory(&other_v); if (!upb_strtable_lookup2(&other->table, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it), &other_v)) { // Not present in other map. return Qfalse; } if (!native_slot_eq(self->value_type, mem, other_mem)) { // Present, but value not equal. return Qfalse; } } return Qtrue; }
VALUE Map_iter_value(Map_iter* iter) { upb_value v = upb_strtable_iter_value(&iter->it); void* mem = value_memory(&v); return native_slot_get(iter->self->value_type, iter->self->value_type_class, mem); }
/* * call-seq: * Map.inspect => string * * Returns a string representing this map's elements. It will be formatted as * "{key => value, key => value, ...}", with each key and value string * representation computed by its own #inspect method. */ VALUE Map_inspect(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE str = rb_str_new2("{"); bool first = true; VALUE inspect_sym = rb_intern("inspect"); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); if (!first) { str = rb_str_cat2(str, ", "); } else { first = false; } str = rb_str_append(str, rb_funcall(key, inspect_sym, 0)); str = rb_str_cat2(str, "=>"); str = rb_str_append(str, rb_funcall(value, inspect_sym, 0)); } str = rb_str_cat2(str, "}"); return str; }
/* * call-seq: * Map.hash => hash_value * * Returns a hash value based on this map's contents. */ VALUE Map_hash(VALUE _self) { Map* self = ruby_to_Map(_self); st_index_t h = rb_hash_start(0); VALUE hash_sym = rb_intern("hash"); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0))); h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0))); } return INT2FIX(h); }
/* * call-seq: * Map.delete(key) => old_value * * Deletes the value at the given key, if any, returning either the old value or * nil if none was present. Throws an exception if the key is of the wrong type. */ VALUE Map_delete(VALUE _self, VALUE key) { Map* self = ruby_to_Map(_self); char keybuf[TABLE_KEY_BUF_LENGTH]; const char* keyval = NULL; size_t length = 0; upb_value v; key = table_key(self, key, keybuf, &keyval, &length); if (upb_strtable_remove2(&self->table, keyval, length, &v)) { void* mem = value_memory(&v); return native_slot_get(self->value_type, self->value_type_class, mem); } else { return Qnil; } }
void Map_mark(void* _self) { Map* self = _self; rb_gc_mark(self->value_type_class); if (self->value_type == UPB_TYPE_STRING || self->value_type == UPB_TYPE_BYTES || self->value_type == UPB_TYPE_MESSAGE) { upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); native_slot_mark(self->value_type, mem); } } }
/* * call-seq: * Map.values => [list_of_values] * * Returns the list of values contained in the map, in unspecified order. */ VALUE Map_values(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE ret = rb_ary_new(); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); rb_ary_push(ret, value); } return ret; }
/* * call-seq: * Map.[]=(key, value) => value * * Inserts or overwrites the value at the given key with the given new value. * Throws an exception if the key type is incorrect. Returns the new value that * was just inserted. */ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) { Map* self = ruby_to_Map(_self); char keybuf[TABLE_KEY_BUF_LENGTH]; const char* keyval = NULL; size_t length = 0; table_key(self, key, keybuf, &keyval, &length); upb_value v; void* mem = value_memory(&v); native_slot_set(self->value_type, self->value_type_class, mem, value); // Replace any existing value by issuing a 'remove' operation first. upb_strtable_remove2(&self->table, keyval, length, NULL); if (!upb_strtable_insert2(&self->table, keyval, length, v)) { rb_raise(rb_eRuntimeError, "Could not insert into table"); } // Ruby hashmap's :[]= method also returns the inserted value. return value; }
/* * call-seq: * Map.to_h => {} * * Returns a Ruby Hash object containing all the values within the map */ VALUE Map_to_h(VALUE _self) { Map* self = ruby_to_Map(_self); VALUE hash = rb_hash_new(); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); if (self->value_type == UPB_TYPE_MESSAGE) { value = Message_to_h(value); } rb_hash_aset(hash, key, value); } return hash; }
/* * call-seq: * Map.each(&block) * * Invokes &block on each |key, value| pair in the map, in unspecified order. * Note that Map also includes Enumerable; map thus acts like a normal Ruby * sequence. */ VALUE Map_each(VALUE _self) { Map* self = ruby_to_Map(_self); upb_strtable_iter it; for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); upb_strtable_next(&it)) { VALUE key = table_key_to_ruby( self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it)); upb_value v = upb_strtable_iter_value(&it); void* mem = value_memory(&v); VALUE value = native_slot_get(self->value_type, self->value_type_class, mem); rb_yield_values(2, key, value); } return Qnil; }