1// Protocol Buffers - Google's data interchange format 2// Copyright 2014 Google Inc. All rights reserved. 3// https://developers.google.com/protocol-buffers/ 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31#include "protobuf.h" 32 33// ----------------------------------------------------------------------------- 34// Basic map operations on top of upb's strtable. 35// 36// Note that we roll our own `Map` container here because, as for 37// `RepeatedField`, we want a strongly-typed container. This is so that any user 38// errors due to incorrect map key or value types are raised as close as 39// possible to the error site, rather than at some deferred point (e.g., 40// serialization). 41// 42// We build our `Map` on top of upb_strtable so that we're able to take 43// advantage of the native_slot storage abstraction, as RepeatedField does. 44// (This is not quite a perfect mapping -- see the key conversions below -- but 45// gives us full support and error-checking for all value types for free.) 46// ----------------------------------------------------------------------------- 47 48// Map values are stored using the native_slot abstraction (as with repeated 49// field values), but keys are a bit special. Since we use a strtable, we need 50// to store keys as sequences of bytes such that equality of those bytes maps 51// one-to-one to equality of keys. We store strings directly (i.e., they map to 52// their own bytes) and integers as native integers (using the native_slot 53// abstraction). 54 55// Note that there is another tradeoff here in keeping string keys as native 56// strings rather than Ruby strings: traversing the Map requires conversion to 57// Ruby string values on every traversal, potentially creating more garbage. We 58// should consider ways to cache a Ruby version of the key if this becomes an 59// issue later. 60 61// Forms a key to use with the underlying strtable from a Ruby key value. |buf| 62// must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to 63// construct a key byte sequence if needed. |out_key| and |out_length| provide 64// the resulting key data/length. 65#define TABLE_KEY_BUF_LENGTH 8 // sizeof(uint64_t) 66static VALUE table_key(Map* self, VALUE key, 67 char* buf, 68 const char** out_key, 69 size_t* out_length) { 70 switch (self->key_type) { 71 case UPB_TYPE_BYTES: 72 case UPB_TYPE_STRING: 73 // Strings: use string content directly. 74 if (TYPE(key) == T_SYMBOL) { 75 key = rb_id2str(SYM2ID(key)); 76 } 77 Check_Type(key, T_STRING); 78 key = native_slot_encode_and_freeze_string(self->key_type, key); 79 *out_key = RSTRING_PTR(key); 80 *out_length = RSTRING_LEN(key); 81 break; 82 83 case UPB_TYPE_BOOL: 84 case UPB_TYPE_INT32: 85 case UPB_TYPE_INT64: 86 case UPB_TYPE_UINT32: 87 case UPB_TYPE_UINT64: 88 native_slot_set("", self->key_type, Qnil, buf, key); 89 *out_key = buf; 90 *out_length = native_slot_size(self->key_type); 91 break; 92 93 default: 94 // Map constructor should not allow a Map with another key type to be 95 // constructed. 96 assert(false); 97 break; 98 } 99 100 return key; 101} 102 103static VALUE table_key_to_ruby(Map* self, upb_strview key) { 104 switch (self->key_type) { 105 case UPB_TYPE_BYTES: 106 case UPB_TYPE_STRING: { 107 VALUE ret = rb_str_new(key.data, key.size); 108 rb_enc_associate(ret, 109 (self->key_type == UPB_TYPE_BYTES) ? 110 kRubyString8bitEncoding : kRubyStringUtf8Encoding); 111 return ret; 112 } 113 114 case UPB_TYPE_BOOL: 115 case UPB_TYPE_INT32: 116 case UPB_TYPE_INT64: 117 case UPB_TYPE_UINT32: 118 case UPB_TYPE_UINT64: 119 return native_slot_get(self->key_type, Qnil, key.data); 120 121 default: 122 assert(false); 123 return Qnil; 124 } 125} 126 127static void* value_memory(upb_value* v) { 128 return (void*)(&v->val); 129} 130 131// ----------------------------------------------------------------------------- 132// Map container type. 133// ----------------------------------------------------------------------------- 134 135const rb_data_type_t Map_type = { 136 "Google::Protobuf::Map", 137 { Map_mark, Map_free, NULL }, 138}; 139 140VALUE cMap; 141 142Map* ruby_to_Map(VALUE _self) { 143 Map* self; 144 TypedData_Get_Struct(_self, Map, &Map_type, self); 145 return self; 146} 147 148void Map_mark(void* _self) { 149 Map* self = _self; 150 151 rb_gc_mark(self->value_type_class); 152 rb_gc_mark(self->parse_frame); 153 154 if (self->value_type == UPB_TYPE_STRING || 155 self->value_type == UPB_TYPE_BYTES || 156 self->value_type == UPB_TYPE_MESSAGE) { 157 upb_strtable_iter it; 158 for (upb_strtable_begin(&it, &self->table); 159 !upb_strtable_done(&it); 160 upb_strtable_next(&it)) { 161 upb_value v = upb_strtable_iter_value(&it); 162 void* mem = value_memory(&v); 163 native_slot_mark(self->value_type, mem); 164 } 165 } 166} 167 168void Map_free(void* _self) { 169 Map* self = _self; 170 upb_strtable_uninit(&self->table); 171 xfree(self); 172} 173 174VALUE Map_alloc(VALUE klass) { 175 Map* self = ALLOC(Map); 176 memset(self, 0, sizeof(Map)); 177 self->value_type_class = Qnil; 178 return TypedData_Wrap_Struct(klass, &Map_type, self); 179} 180 181VALUE Map_set_frame(VALUE map, VALUE val) { 182 Map* self = ruby_to_Map(map); 183 self->parse_frame = val; 184 return val; 185} 186 187static bool needs_typeclass(upb_fieldtype_t type) { 188 switch (type) { 189 case UPB_TYPE_MESSAGE: 190 case UPB_TYPE_ENUM: 191 return true; 192 default: 193 return false; 194 } 195} 196 197/* 198 * call-seq: 199 * Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {}) 200 * => new map 201 * 202 * Allocates a new Map container. This constructor may be called with 2, 3, or 4 203 * arguments. The first two arguments are always present and are symbols (taking 204 * on the same values as field-type symbols in message descriptors) that 205 * indicate the type of the map key and value fields. 206 * 207 * The supported key types are: :int32, :int64, :uint32, :uint64, :bool, 208 * :string, :bytes. 209 * 210 * The supported value types are: :int32, :int64, :uint32, :uint64, :bool, 211 * :string, :bytes, :enum, :message. 212 * 213 * The third argument, value_typeclass, must be present if value_type is :enum 214 * or :message. As in RepeatedField#new, this argument must be a message class 215 * (for :message) or enum module (for :enum). 216 * 217 * The last argument, if present, provides initial content for map. Note that 218 * this may be an ordinary Ruby hashmap or another Map instance with identical 219 * key and value types. Also note that this argument may be present whether or 220 * not value_typeclass is present (and it is unambiguously separate from 221 * value_typeclass because value_typeclass's presence is strictly determined by 222 * value_type). The contents of this initial hashmap or Map instance are 223 * shallow-copied into the new Map: the original map is unmodified, but 224 * references to underlying objects will be shared if the value type is a 225 * message type. 226 */ 227VALUE Map_init(int argc, VALUE* argv, VALUE _self) { 228 Map* self = ruby_to_Map(_self); 229 int init_value_arg; 230 231 // We take either two args (:key_type, :value_type), three args (:key_type, 232 // :value_type, "ValueMessageType"), or four args (the above plus an initial 233 // hashmap). 234 if (argc < 2 || argc > 4) { 235 rb_raise(rb_eArgError, "Map constructor expects 2, 3 or 4 arguments."); 236 } 237 238 self->key_type = ruby_to_fieldtype(argv[0]); 239 self->value_type = ruby_to_fieldtype(argv[1]); 240 self->parse_frame = Qnil; 241 242 // Check that the key type is an allowed type. 243 switch (self->key_type) { 244 case UPB_TYPE_INT32: 245 case UPB_TYPE_INT64: 246 case UPB_TYPE_UINT32: 247 case UPB_TYPE_UINT64: 248 case UPB_TYPE_BOOL: 249 case UPB_TYPE_STRING: 250 case UPB_TYPE_BYTES: 251 // These are OK. 252 break; 253 default: 254 rb_raise(rb_eArgError, "Invalid key type for map."); 255 } 256 257 init_value_arg = 2; 258 if (needs_typeclass(self->value_type) && argc > 2) { 259 self->value_type_class = argv[2]; 260 validate_type_class(self->value_type, self->value_type_class); 261 init_value_arg = 3; 262 } 263 264 // Table value type is always UINT64: this ensures enough space to store the 265 // native_slot value. 266 if (!upb_strtable_init(&self->table, UPB_CTYPE_UINT64)) { 267 rb_raise(rb_eRuntimeError, "Could not allocate table."); 268 } 269 270 if (argc > init_value_arg) { 271 Map_merge_into_self(_self, argv[init_value_arg]); 272 } 273 274 return Qnil; 275} 276 277/* 278 * call-seq: 279 * Map.each(&block) 280 * 281 * Invokes &block on each |key, value| pair in the map, in unspecified order. 282 * Note that Map also includes Enumerable; map thus acts like a normal Ruby 283 * sequence. 284 */ 285VALUE Map_each(VALUE _self) { 286 Map* self = ruby_to_Map(_self); 287 288 upb_strtable_iter it; 289 for (upb_strtable_begin(&it, &self->table); 290 !upb_strtable_done(&it); 291 upb_strtable_next(&it)) { 292 VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it)); 293 294 upb_value v = upb_strtable_iter_value(&it); 295 void* mem = value_memory(&v); 296 VALUE value = native_slot_get(self->value_type, 297 self->value_type_class, 298 mem); 299 300 rb_yield_values(2, key, value); 301 } 302 303 return Qnil; 304} 305 306/* 307 * call-seq: 308 * Map.keys => [list_of_keys] 309 * 310 * Returns the list of keys contained in the map, in unspecified order. 311 */ 312VALUE Map_keys(VALUE _self) { 313 Map* self = ruby_to_Map(_self); 314 315 VALUE ret = rb_ary_new(); 316 upb_strtable_iter it; 317 for (upb_strtable_begin(&it, &self->table); 318 !upb_strtable_done(&it); 319 upb_strtable_next(&it)) { 320 VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it)); 321 322 rb_ary_push(ret, key); 323 } 324 325 return ret; 326} 327 328/* 329 * call-seq: 330 * Map.values => [list_of_values] 331 * 332 * Returns the list of values contained in the map, in unspecified order. 333 */ 334VALUE Map_values(VALUE _self) { 335 Map* self = ruby_to_Map(_self); 336 337 VALUE ret = rb_ary_new(); 338 upb_strtable_iter it; 339 for (upb_strtable_begin(&it, &self->table); 340 !upb_strtable_done(&it); 341 upb_strtable_next(&it)) { 342 343 upb_value v = upb_strtable_iter_value(&it); 344 void* mem = value_memory(&v); 345 VALUE value = native_slot_get(self->value_type, 346 self->value_type_class, 347 mem); 348 349 rb_ary_push(ret, value); 350 } 351 352 return ret; 353} 354 355/* 356 * call-seq: 357 * Map.[](key) => value 358 * 359 * Accesses the element at the given key. Throws an exception if the key type is 360 * incorrect. Returns nil when the key is not present in the map. 361 */ 362VALUE Map_index(VALUE _self, VALUE key) { 363 Map* self = ruby_to_Map(_self); 364 365 char keybuf[TABLE_KEY_BUF_LENGTH]; 366 const char* keyval = NULL; 367 size_t length = 0; 368 upb_value v; 369 key = table_key(self, key, keybuf, &keyval, &length); 370 371 if (upb_strtable_lookup2(&self->table, keyval, length, &v)) { 372 void* mem = value_memory(&v); 373 return native_slot_get(self->value_type, self->value_type_class, mem); 374 } else { 375 return Qnil; 376 } 377} 378 379/* 380 * call-seq: 381 * Map.[]=(key, value) => value 382 * 383 * Inserts or overwrites the value at the given key with the given new value. 384 * Throws an exception if the key type is incorrect. Returns the new value that 385 * was just inserted. 386 */ 387VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) { 388 Map* self = ruby_to_Map(_self); 389 char keybuf[TABLE_KEY_BUF_LENGTH]; 390 const char* keyval = NULL; 391 size_t length = 0; 392 upb_value v; 393 void* mem; 394 key = table_key(self, key, keybuf, &keyval, &length); 395 396 rb_check_frozen(_self); 397 398 if (TYPE(value) == T_HASH) { 399 VALUE args[1] = { value }; 400 value = rb_class_new_instance(1, args, self->value_type_class); 401 } 402 403 mem = value_memory(&v); 404 native_slot_set("", self->value_type, self->value_type_class, mem, value); 405 406 // Replace any existing value by issuing a 'remove' operation first. 407 upb_strtable_remove2(&self->table, keyval, length, NULL); 408 if (!upb_strtable_insert2(&self->table, keyval, length, v)) { 409 rb_raise(rb_eRuntimeError, "Could not insert into table"); 410 } 411 412 // Ruby hashmap's :[]= method also returns the inserted value. 413 return value; 414} 415 416/* 417 * call-seq: 418 * Map.has_key?(key) => bool 419 * 420 * Returns true if the given key is present in the map. Throws an exception if 421 * the key has the wrong type. 422 */ 423VALUE Map_has_key(VALUE _self, VALUE key) { 424 Map* self = ruby_to_Map(_self); 425 426 char keybuf[TABLE_KEY_BUF_LENGTH]; 427 const char* keyval = NULL; 428 size_t length = 0; 429 key = table_key(self, key, keybuf, &keyval, &length); 430 431 if (upb_strtable_lookup2(&self->table, keyval, length, NULL)) { 432 return Qtrue; 433 } else { 434 return Qfalse; 435 } 436} 437 438/* 439 * call-seq: 440 * Map.delete(key) => old_value 441 * 442 * Deletes the value at the given key, if any, returning either the old value or 443 * nil if none was present. Throws an exception if the key is of the wrong type. 444 */ 445VALUE Map_delete(VALUE _self, VALUE key) { 446 Map* self = ruby_to_Map(_self); 447 char keybuf[TABLE_KEY_BUF_LENGTH]; 448 const char* keyval = NULL; 449 size_t length = 0; 450 upb_value v; 451 key = table_key(self, key, keybuf, &keyval, &length); 452 453 rb_check_frozen(_self); 454 455 if (upb_strtable_remove2(&self->table, keyval, length, &v)) { 456 void* mem = value_memory(&v); 457 return native_slot_get(self->value_type, self->value_type_class, mem); 458 } else { 459 return Qnil; 460 } 461} 462 463/* 464 * call-seq: 465 * Map.clear 466 * 467 * Removes all entries from the map. 468 */ 469VALUE Map_clear(VALUE _self) { 470 Map* self = ruby_to_Map(_self); 471 472 rb_check_frozen(_self); 473 474 // Uninit and reinit the table -- this is faster than iterating and doing a 475 // delete-lookup on each key. 476 upb_strtable_uninit(&self->table); 477 if (!upb_strtable_init(&self->table, UPB_CTYPE_INT64)) { 478 rb_raise(rb_eRuntimeError, "Unable to re-initialize table"); 479 } 480 return Qnil; 481} 482 483/* 484 * call-seq: 485 * Map.length 486 * 487 * Returns the number of entries (key-value pairs) in the map. 488 */ 489VALUE Map_length(VALUE _self) { 490 Map* self = ruby_to_Map(_self); 491 return ULL2NUM(upb_strtable_count(&self->table)); 492} 493 494VALUE Map_new_this_type(VALUE _self) { 495 Map* self = ruby_to_Map(_self); 496 VALUE new_map = Qnil; 497 VALUE key_type = fieldtype_to_ruby(self->key_type); 498 VALUE value_type = fieldtype_to_ruby(self->value_type); 499 if (self->value_type_class != Qnil) { 500 new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 3, 501 key_type, value_type, self->value_type_class); 502 } else { 503 new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2, 504 key_type, value_type); 505 } 506 return new_map; 507} 508 509/* 510 * call-seq: 511 * Map.dup => new_map 512 * 513 * Duplicates this map with a shallow copy. References to all non-primitive 514 * element objects (e.g., submessages) are shared. 515 */ 516VALUE Map_dup(VALUE _self) { 517 Map* self = ruby_to_Map(_self); 518 VALUE new_map = Map_new_this_type(_self); 519 Map* new_self = ruby_to_Map(new_map); 520 521 upb_strtable_iter it; 522 for (upb_strtable_begin(&it, &self->table); 523 !upb_strtable_done(&it); 524 upb_strtable_next(&it)) { 525 upb_strview k = upb_strtable_iter_key(&it); 526 upb_value v = upb_strtable_iter_value(&it); 527 void* mem = value_memory(&v); 528 upb_value dup; 529 void* dup_mem = value_memory(&dup); 530 native_slot_dup(self->value_type, dup_mem, mem); 531 532 if (!upb_strtable_insert2(&new_self->table, k.data, k.size, dup)) { 533 rb_raise(rb_eRuntimeError, "Error inserting value into new table"); 534 } 535 } 536 537 return new_map; 538} 539 540// Used by Google::Protobuf.deep_copy but not exposed directly. 541VALUE Map_deep_copy(VALUE _self) { 542 Map* self = ruby_to_Map(_self); 543 VALUE new_map = Map_new_this_type(_self); 544 Map* new_self = ruby_to_Map(new_map); 545 546 upb_strtable_iter it; 547 for (upb_strtable_begin(&it, &self->table); 548 !upb_strtable_done(&it); 549 upb_strtable_next(&it)) { 550 upb_strview k = upb_strtable_iter_key(&it); 551 upb_value v = upb_strtable_iter_value(&it); 552 void* mem = value_memory(&v); 553 upb_value dup; 554 void* dup_mem = value_memory(&dup); 555 native_slot_deep_copy(self->value_type, self->value_type_class, dup_mem, 556 mem); 557 558 if (!upb_strtable_insert2(&new_self->table, k.data, k.size, dup)) { 559 rb_raise(rb_eRuntimeError, "Error inserting value into new table"); 560 } 561 } 562 563 return new_map; 564} 565 566/* 567 * call-seq: 568 * Map.==(other) => boolean 569 * 570 * Compares this map to another. Maps are equal if they have identical key sets, 571 * and for each key, the values in both maps compare equal. Elements are 572 * compared as per normal Ruby semantics, by calling their :== methods (or 573 * performing a more efficient comparison for primitive types). 574 * 575 * Maps with dissimilar key types or value types/typeclasses are never equal, 576 * even if value comparison (for example, between integers and floats) would 577 * have otherwise indicated that every element has equal value. 578 */ 579VALUE Map_eq(VALUE _self, VALUE _other) { 580 Map* self = ruby_to_Map(_self); 581 Map* other; 582 upb_strtable_iter it; 583 584 // Allow comparisons to Ruby hashmaps by converting to a temporary Map 585 // instance. Slow, but workable. 586 if (TYPE(_other) == T_HASH) { 587 VALUE other_map = Map_new_this_type(_self); 588 Map_merge_into_self(other_map, _other); 589 _other = other_map; 590 } 591 592 other = ruby_to_Map(_other); 593 594 if (self == other) { 595 return Qtrue; 596 } 597 if (self->key_type != other->key_type || 598 self->value_type != other->value_type || 599 self->value_type_class != other->value_type_class) { 600 return Qfalse; 601 } 602 if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) { 603 return Qfalse; 604 } 605 606 // For each member of self, check that an equal member exists at the same key 607 // in other. 608 for (upb_strtable_begin(&it, &self->table); 609 !upb_strtable_done(&it); 610 upb_strtable_next(&it)) { 611 upb_strview k = upb_strtable_iter_key(&it); 612 upb_value v = upb_strtable_iter_value(&it); 613 void* mem = value_memory(&v); 614 upb_value other_v; 615 void* other_mem = value_memory(&other_v); 616 617 if (!upb_strtable_lookup2(&other->table, k.data, k.size, &other_v)) { 618 // Not present in other map. 619 return Qfalse; 620 } 621 622 if (!native_slot_eq(self->value_type, self->value_type_class, mem, 623 other_mem)) { 624 // Present, but value not equal. 625 return Qfalse; 626 } 627 } 628 629 return Qtrue; 630} 631 632/* 633 * call-seq: 634 * Map.hash => hash_value 635 * 636 * Returns a hash value based on this map's contents. 637 */ 638VALUE Map_hash(VALUE _self) { 639 Map* self = ruby_to_Map(_self); 640 641 st_index_t h = rb_hash_start(0); 642 VALUE hash_sym = rb_intern("hash"); 643 644 upb_strtable_iter it; 645 for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); 646 upb_strtable_next(&it)) { 647 VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it)); 648 649 upb_value v = upb_strtable_iter_value(&it); 650 void* mem = value_memory(&v); 651 VALUE value = native_slot_get(self->value_type, 652 self->value_type_class, 653 mem); 654 655 h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0))); 656 h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0))); 657 } 658 659 return INT2FIX(h); 660} 661 662/* 663 * call-seq: 664 * Map.to_h => {} 665 * 666 * Returns a Ruby Hash object containing all the values within the map 667 */ 668VALUE Map_to_h(VALUE _self) { 669 Map* self = ruby_to_Map(_self); 670 VALUE hash = rb_hash_new(); 671 upb_strtable_iter it; 672 for (upb_strtable_begin(&it, &self->table); 673 !upb_strtable_done(&it); 674 upb_strtable_next(&it)) { 675 VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it)); 676 upb_value v = upb_strtable_iter_value(&it); 677 void* mem = value_memory(&v); 678 VALUE value = native_slot_get(self->value_type, 679 self->value_type_class, 680 mem); 681 682 if (self->value_type == UPB_TYPE_MESSAGE) { 683 value = Message_to_h(value); 684 } 685 rb_hash_aset(hash, key, value); 686 } 687 return hash; 688} 689 690/* 691 * call-seq: 692 * Map.inspect => string 693 * 694 * Returns a string representing this map's elements. It will be formatted as 695 * "{key => value, key => value, ...}", with each key and value string 696 * representation computed by its own #inspect method. 697 */ 698VALUE Map_inspect(VALUE _self) { 699 Map* self = ruby_to_Map(_self); 700 701 VALUE str = rb_str_new2("{"); 702 703 bool first = true; 704 VALUE inspect_sym = rb_intern("inspect"); 705 706 upb_strtable_iter it; 707 for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it); 708 upb_strtable_next(&it)) { 709 VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it)); 710 711 upb_value v = upb_strtable_iter_value(&it); 712 void* mem = value_memory(&v); 713 VALUE value = native_slot_get(self->value_type, 714 self->value_type_class, 715 mem); 716 717 if (!first) { 718 str = rb_str_cat2(str, ", "); 719 } else { 720 first = false; 721 } 722 str = rb_str_append(str, rb_funcall(key, inspect_sym, 0)); 723 str = rb_str_cat2(str, "=>"); 724 str = rb_str_append(str, rb_funcall(value, inspect_sym, 0)); 725 } 726 727 str = rb_str_cat2(str, "}"); 728 return str; 729} 730 731/* 732 * call-seq: 733 * Map.merge(other_map) => map 734 * 735 * Copies key/value pairs from other_map into a copy of this map. If a key is 736 * set in other_map and this map, the value from other_map overwrites the value 737 * in the new copy of this map. Returns the new copy of this map with merged 738 * contents. 739 */ 740VALUE Map_merge(VALUE _self, VALUE hashmap) { 741 VALUE dupped = Map_dup(_self); 742 return Map_merge_into_self(dupped, hashmap); 743} 744 745static int merge_into_self_callback(VALUE key, VALUE value, VALUE self) { 746 Map_index_set(self, key, value); 747 return ST_CONTINUE; 748} 749 750// Used only internally -- shared by #merge and #initialize. 751VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) { 752 if (TYPE(hashmap) == T_HASH) { 753 rb_hash_foreach(hashmap, merge_into_self_callback, _self); 754 } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) && 755 RTYPEDDATA_TYPE(hashmap) == &Map_type) { 756 757 Map* self = ruby_to_Map(_self); 758 Map* other = ruby_to_Map(hashmap); 759 upb_strtable_iter it; 760 761 if (self->key_type != other->key_type || 762 self->value_type != other->value_type || 763 self->value_type_class != other->value_type_class) { 764 rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types"); 765 } 766 767 for (upb_strtable_begin(&it, &other->table); 768 !upb_strtable_done(&it); 769 upb_strtable_next(&it)) { 770 upb_strview k = upb_strtable_iter_key(&it); 771 772 // Replace any existing value by issuing a 'remove' operation first. 773 upb_value v; 774 upb_value oldv; 775 upb_strtable_remove2(&self->table, k.data, k.size, &oldv); 776 777 v = upb_strtable_iter_value(&it); 778 upb_strtable_insert2(&self->table, k.data, k.size, v); 779 } 780 } else { 781 rb_raise(rb_eArgError, "Unknown type merging into Map"); 782 } 783 return _self; 784} 785 786// Internal method: map iterator initialization (used for serialization). 787void Map_begin(VALUE _self, Map_iter* iter) { 788 Map* self = ruby_to_Map(_self); 789 iter->self = self; 790 upb_strtable_begin(&iter->it, &self->table); 791} 792 793void Map_next(Map_iter* iter) { 794 upb_strtable_next(&iter->it); 795} 796 797bool Map_done(Map_iter* iter) { 798 return upb_strtable_done(&iter->it); 799} 800 801VALUE Map_iter_key(Map_iter* iter) { 802 return table_key_to_ruby(iter->self, upb_strtable_iter_key(&iter->it)); 803} 804 805VALUE Map_iter_value(Map_iter* iter) { 806 upb_value v = upb_strtable_iter_value(&iter->it); 807 void* mem = value_memory(&v); 808 return native_slot_get(iter->self->value_type, 809 iter->self->value_type_class, 810 mem); 811} 812 813void Map_register(VALUE module) { 814 VALUE klass = rb_define_class_under(module, "Map", rb_cObject); 815 rb_define_alloc_func(klass, Map_alloc); 816 rb_gc_register_address(&cMap); 817 cMap = klass; 818 819 rb_define_method(klass, "initialize", Map_init, -1); 820 rb_define_method(klass, "each", Map_each, 0); 821 rb_define_method(klass, "keys", Map_keys, 0); 822 rb_define_method(klass, "values", Map_values, 0); 823 rb_define_method(klass, "[]", Map_index, 1); 824 rb_define_method(klass, "[]=", Map_index_set, 2); 825 rb_define_method(klass, "has_key?", Map_has_key, 1); 826 rb_define_method(klass, "delete", Map_delete, 1); 827 rb_define_method(klass, "clear", Map_clear, 0); 828 rb_define_method(klass, "length", Map_length, 0); 829 rb_define_method(klass, "dup", Map_dup, 0); 830 rb_define_method(klass, "==", Map_eq, 1); 831 rb_define_method(klass, "hash", Map_hash, 0); 832 rb_define_method(klass, "to_h", Map_to_h, 0); 833 rb_define_method(klass, "inspect", Map_inspect, 0); 834 rb_define_method(klass, "merge", Map_merge, 1); 835 rb_include_module(klass, rb_mEnumerable); 836} 837