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)
table_key(Map * self,VALUE key,char * buf,const char ** out_key,size_t * out_length)66 static 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       Check_Type(key, T_STRING);
75       key = native_slot_encode_and_freeze_string(self->key_type, key);
76       *out_key = RSTRING_PTR(key);
77       *out_length = RSTRING_LEN(key);
78       break;
79 
80     case UPB_TYPE_BOOL:
81     case UPB_TYPE_INT32:
82     case UPB_TYPE_INT64:
83     case UPB_TYPE_UINT32:
84     case UPB_TYPE_UINT64:
85       native_slot_set("", self->key_type, Qnil, buf, key);
86       *out_key = buf;
87       *out_length = native_slot_size(self->key_type);
88       break;
89 
90     default:
91       // Map constructor should not allow a Map with another key type to be
92       // constructed.
93       assert(false);
94       break;
95   }
96 
97   return key;
98 }
99 
table_key_to_ruby(Map * self,const char * buf,size_t length)100 static VALUE table_key_to_ruby(Map* self, const char* buf, size_t length) {
101   switch (self->key_type) {
102     case UPB_TYPE_BYTES:
103     case UPB_TYPE_STRING: {
104       VALUE ret = rb_str_new(buf, length);
105       rb_enc_associate(ret,
106                        (self->key_type == UPB_TYPE_BYTES) ?
107                        kRubyString8bitEncoding : kRubyStringUtf8Encoding);
108       return ret;
109     }
110 
111     case UPB_TYPE_BOOL:
112     case UPB_TYPE_INT32:
113     case UPB_TYPE_INT64:
114     case UPB_TYPE_UINT32:
115     case UPB_TYPE_UINT64:
116       return native_slot_get(self->key_type, Qnil, buf);
117 
118     default:
119       assert(false);
120       return Qnil;
121   }
122 }
123 
value_memory(upb_value * v)124 static void* value_memory(upb_value* v) {
125   return (void*)(&v->val);
126 }
127 
128 // -----------------------------------------------------------------------------
129 // Map container type.
130 // -----------------------------------------------------------------------------
131 
132 const rb_data_type_t Map_type = {
133   "Google::Protobuf::Map",
134   { Map_mark, Map_free, NULL },
135 };
136 
137 VALUE cMap;
138 
ruby_to_Map(VALUE _self)139 Map* ruby_to_Map(VALUE _self) {
140   Map* self;
141   TypedData_Get_Struct(_self, Map, &Map_type, self);
142   return self;
143 }
144 
Map_mark(void * _self)145 void Map_mark(void* _self) {
146   Map* self = _self;
147 
148   rb_gc_mark(self->value_type_class);
149   rb_gc_mark(self->parse_frame);
150 
151   if (self->value_type == UPB_TYPE_STRING ||
152       self->value_type == UPB_TYPE_BYTES ||
153       self->value_type == UPB_TYPE_MESSAGE) {
154     upb_strtable_iter it;
155     for (upb_strtable_begin(&it, &self->table);
156          !upb_strtable_done(&it);
157          upb_strtable_next(&it)) {
158       upb_value v = upb_strtable_iter_value(&it);
159       void* mem = value_memory(&v);
160       native_slot_mark(self->value_type, mem);
161     }
162   }
163 }
164 
Map_free(void * _self)165 void Map_free(void* _self) {
166   Map* self = _self;
167   upb_strtable_uninit(&self->table);
168   xfree(self);
169 }
170 
Map_alloc(VALUE klass)171 VALUE Map_alloc(VALUE klass) {
172   Map* self = ALLOC(Map);
173   memset(self, 0, sizeof(Map));
174   self->value_type_class = Qnil;
175   return TypedData_Wrap_Struct(klass, &Map_type, self);
176 }
177 
Map_set_frame(VALUE map,VALUE val)178 VALUE Map_set_frame(VALUE map, VALUE val) {
179   Map* self = ruby_to_Map(map);
180   self->parse_frame = val;
181   return val;
182 }
183 
needs_typeclass(upb_fieldtype_t type)184 static bool needs_typeclass(upb_fieldtype_t type) {
185   switch (type) {
186     case UPB_TYPE_MESSAGE:
187     case UPB_TYPE_ENUM:
188       return true;
189     default:
190       return false;
191   }
192 }
193 
194 /*
195  * call-seq:
196  *     Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
197  *     => new map
198  *
199  * Allocates a new Map container. This constructor may be called with 2, 3, or 4
200  * arguments. The first two arguments are always present and are symbols (taking
201  * on the same values as field-type symbols in message descriptors) that
202  * indicate the type of the map key and value fields.
203  *
204  * The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
205  * :string, :bytes.
206  *
207  * The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
208  * :string, :bytes, :enum, :message.
209  *
210  * The third argument, value_typeclass, must be present if value_type is :enum
211  * or :message. As in RepeatedField#new, this argument must be a message class
212  * (for :message) or enum module (for :enum).
213  *
214  * The last argument, if present, provides initial content for map. Note that
215  * this may be an ordinary Ruby hashmap or another Map instance with identical
216  * key and value types. Also note that this argument may be present whether or
217  * not value_typeclass is present (and it is unambiguously separate from
218  * value_typeclass because value_typeclass's presence is strictly determined by
219  * value_type). The contents of this initial hashmap or Map instance are
220  * shallow-copied into the new Map: the original map is unmodified, but
221  * references to underlying objects will be shared if the value type is a
222  * message type.
223  */
Map_init(int argc,VALUE * argv,VALUE _self)224 VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
225   Map* self = ruby_to_Map(_self);
226   int init_value_arg;
227 
228   // We take either two args (:key_type, :value_type), three args (:key_type,
229   // :value_type, "ValueMessageType"), or four args (the above plus an initial
230   // hashmap).
231   if (argc < 2 || argc > 4) {
232     rb_raise(rb_eArgError, "Map constructor expects 2, 3 or 4 arguments.");
233   }
234 
235   self->key_type = ruby_to_fieldtype(argv[0]);
236   self->value_type = ruby_to_fieldtype(argv[1]);
237   self->parse_frame = Qnil;
238 
239   // Check that the key type is an allowed type.
240   switch (self->key_type) {
241     case UPB_TYPE_INT32:
242     case UPB_TYPE_INT64:
243     case UPB_TYPE_UINT32:
244     case UPB_TYPE_UINT64:
245     case UPB_TYPE_BOOL:
246     case UPB_TYPE_STRING:
247     case UPB_TYPE_BYTES:
248       // These are OK.
249       break;
250     default:
251       rb_raise(rb_eArgError, "Invalid key type for map.");
252   }
253 
254   init_value_arg = 2;
255   if (needs_typeclass(self->value_type) && argc > 2) {
256     self->value_type_class = argv[2];
257     validate_type_class(self->value_type, self->value_type_class);
258     init_value_arg = 3;
259   }
260 
261   // Table value type is always UINT64: this ensures enough space to store the
262   // native_slot value.
263   if (!upb_strtable_init(&self->table, UPB_CTYPE_UINT64)) {
264     rb_raise(rb_eRuntimeError, "Could not allocate table.");
265   }
266 
267   if (argc > init_value_arg) {
268     Map_merge_into_self(_self, argv[init_value_arg]);
269   }
270 
271   return Qnil;
272 }
273 
274 /*
275  * call-seq:
276  *     Map.each(&block)
277  *
278  * Invokes &block on each |key, value| pair in the map, in unspecified order.
279  * Note that Map also includes Enumerable; map thus acts like a normal Ruby
280  * sequence.
281  */
Map_each(VALUE _self)282 VALUE Map_each(VALUE _self) {
283   Map* self = ruby_to_Map(_self);
284 
285   upb_strtable_iter it;
286   for (upb_strtable_begin(&it, &self->table);
287        !upb_strtable_done(&it);
288        upb_strtable_next(&it)) {
289 
290     VALUE key = table_key_to_ruby(
291         self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
292 
293     upb_value v = upb_strtable_iter_value(&it);
294     void* mem = value_memory(&v);
295     VALUE value = native_slot_get(self->value_type,
296                                   self->value_type_class,
297                                   mem);
298 
299     rb_yield_values(2, key, value);
300   }
301 
302   return Qnil;
303 }
304 
305 /*
306  * call-seq:
307  *     Map.keys => [list_of_keys]
308  *
309  * Returns the list of keys contained in the map, in unspecified order.
310  */
Map_keys(VALUE _self)311 VALUE Map_keys(VALUE _self) {
312   Map* self = ruby_to_Map(_self);
313 
314   VALUE ret = rb_ary_new();
315   upb_strtable_iter it;
316   for (upb_strtable_begin(&it, &self->table);
317        !upb_strtable_done(&it);
318        upb_strtable_next(&it)) {
319 
320     VALUE key = table_key_to_ruby(
321         self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
322 
323     rb_ary_push(ret, key);
324   }
325 
326   return ret;
327 }
328 
329 /*
330  * call-seq:
331  *     Map.values => [list_of_values]
332  *
333  * Returns the list of values contained in the map, in unspecified order.
334  */
Map_values(VALUE _self)335 VALUE Map_values(VALUE _self) {
336   Map* self = ruby_to_Map(_self);
337 
338   VALUE ret = rb_ary_new();
339   upb_strtable_iter it;
340   for (upb_strtable_begin(&it, &self->table);
341        !upb_strtable_done(&it);
342        upb_strtable_next(&it)) {
343 
344     upb_value v = upb_strtable_iter_value(&it);
345     void* mem = value_memory(&v);
346     VALUE value = native_slot_get(self->value_type,
347                                   self->value_type_class,
348                                   mem);
349 
350     rb_ary_push(ret, value);
351   }
352 
353   return ret;
354 }
355 
356 /*
357  * call-seq:
358  *     Map.[](key) => value
359  *
360  * Accesses the element at the given key. Throws an exception if the key type is
361  * incorrect. Returns nil when the key is not present in the map.
362  */
Map_index(VALUE _self,VALUE key)363 VALUE Map_index(VALUE _self, VALUE key) {
364   Map* self = ruby_to_Map(_self);
365 
366   char keybuf[TABLE_KEY_BUF_LENGTH];
367   const char* keyval = NULL;
368   size_t length = 0;
369   upb_value v;
370   key = table_key(self, key, keybuf, &keyval, &length);
371 
372   if (upb_strtable_lookup2(&self->table, keyval, length, &v)) {
373     void* mem = value_memory(&v);
374     return native_slot_get(self->value_type, self->value_type_class, mem);
375   } else {
376     return Qnil;
377   }
378 }
379 
380 /*
381  * call-seq:
382  *     Map.[]=(key, value) => value
383  *
384  * Inserts or overwrites the value at the given key with the given new value.
385  * Throws an exception if the key type is incorrect. Returns the new value that
386  * was just inserted.
387  */
Map_index_set(VALUE _self,VALUE key,VALUE value)388 VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
389   rb_check_frozen(_self);
390 
391   Map* self = ruby_to_Map(_self);
392 
393   char keybuf[TABLE_KEY_BUF_LENGTH];
394   const char* keyval = NULL;
395   size_t length = 0;
396   upb_value v;
397   void* mem;
398   key = table_key(self, key, keybuf, &keyval, &length);
399 
400   mem = value_memory(&v);
401   native_slot_set("", self->value_type, self->value_type_class, mem, value);
402 
403   // Replace any existing value by issuing a 'remove' operation first.
404   upb_strtable_remove2(&self->table, keyval, length, NULL);
405   if (!upb_strtable_insert2(&self->table, keyval, length, v)) {
406     rb_raise(rb_eRuntimeError, "Could not insert into table");
407   }
408 
409   // Ruby hashmap's :[]= method also returns the inserted value.
410   return value;
411 }
412 
413 /*
414  * call-seq:
415  *     Map.has_key?(key) => bool
416  *
417  * Returns true if the given key is present in the map. Throws an exception if
418  * the key has the wrong type.
419  */
Map_has_key(VALUE _self,VALUE key)420 VALUE Map_has_key(VALUE _self, VALUE key) {
421   Map* self = ruby_to_Map(_self);
422 
423   char keybuf[TABLE_KEY_BUF_LENGTH];
424   const char* keyval = NULL;
425   size_t length = 0;
426   key = table_key(self, key, keybuf, &keyval, &length);
427 
428   if (upb_strtable_lookup2(&self->table, keyval, length, NULL)) {
429     return Qtrue;
430   } else {
431     return Qfalse;
432   }
433 }
434 
435 /*
436  * call-seq:
437  *     Map.delete(key) => old_value
438  *
439  * Deletes the value at the given key, if any, returning either the old value or
440  * nil if none was present. Throws an exception if the key is of the wrong type.
441  */
Map_delete(VALUE _self,VALUE key)442 VALUE Map_delete(VALUE _self, VALUE key) {
443   rb_check_frozen(_self);
444 
445   Map* self = ruby_to_Map(_self);
446 
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   if (upb_strtable_remove2(&self->table, keyval, length, &v)) {
454     void* mem = value_memory(&v);
455     return native_slot_get(self->value_type, self->value_type_class, mem);
456   } else {
457     return Qnil;
458   }
459 }
460 
461 /*
462  * call-seq:
463  *     Map.clear
464  *
465  * Removes all entries from the map.
466  */
Map_clear(VALUE _self)467 VALUE Map_clear(VALUE _self) {
468   rb_check_frozen(_self);
469 
470   Map* self = ruby_to_Map(_self);
471 
472   // Uninit and reinit the table -- this is faster than iterating and doing a
473   // delete-lookup on each key.
474   upb_strtable_uninit(&self->table);
475   if (!upb_strtable_init(&self->table, UPB_CTYPE_INT64)) {
476     rb_raise(rb_eRuntimeError, "Unable to re-initialize table");
477   }
478   return Qnil;
479 }
480 
481 /*
482  * call-seq:
483  *     Map.length
484  *
485  * Returns the number of entries (key-value pairs) in the map.
486  */
Map_length(VALUE _self)487 VALUE Map_length(VALUE _self) {
488   Map* self = ruby_to_Map(_self);
489   return ULL2NUM(upb_strtable_count(&self->table));
490 }
491 
Map_new_this_type(VALUE _self)492 static VALUE Map_new_this_type(VALUE _self) {
493   Map* self = ruby_to_Map(_self);
494   VALUE new_map = Qnil;
495   VALUE key_type = fieldtype_to_ruby(self->key_type);
496   VALUE value_type = fieldtype_to_ruby(self->value_type);
497   if (self->value_type_class != Qnil) {
498     new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 3,
499                          key_type, value_type, self->value_type_class);
500   } else {
501     new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2,
502                          key_type, value_type);
503   }
504   return new_map;
505 }
506 
507 /*
508  * call-seq:
509  *     Map.dup => new_map
510  *
511  * Duplicates this map with a shallow copy. References to all non-primitive
512  * element objects (e.g., submessages) are shared.
513  */
Map_dup(VALUE _self)514 VALUE Map_dup(VALUE _self) {
515   Map* self = ruby_to_Map(_self);
516   VALUE new_map = Map_new_this_type(_self);
517   Map* new_self = ruby_to_Map(new_map);
518 
519   upb_strtable_iter it;
520   for (upb_strtable_begin(&it, &self->table);
521        !upb_strtable_done(&it);
522        upb_strtable_next(&it)) {
523 
524     upb_value v = upb_strtable_iter_value(&it);
525     void* mem = value_memory(&v);
526     upb_value dup;
527     void* dup_mem = value_memory(&dup);
528     native_slot_dup(self->value_type, dup_mem, mem);
529 
530     if (!upb_strtable_insert2(&new_self->table,
531                               upb_strtable_iter_key(&it),
532                               upb_strtable_iter_keylength(&it),
533                               dup)) {
534       rb_raise(rb_eRuntimeError, "Error inserting value into new table");
535     }
536   }
537 
538   return new_map;
539 }
540 
541 // Used by Google::Protobuf.deep_copy but not exposed directly.
Map_deep_copy(VALUE _self)542 VALUE Map_deep_copy(VALUE _self) {
543   Map* self = ruby_to_Map(_self);
544   VALUE new_map = Map_new_this_type(_self);
545   Map* new_self = ruby_to_Map(new_map);
546 
547   upb_strtable_iter it;
548   for (upb_strtable_begin(&it, &self->table);
549        !upb_strtable_done(&it);
550        upb_strtable_next(&it)) {
551 
552     upb_value v = upb_strtable_iter_value(&it);
553     void* mem = value_memory(&v);
554     upb_value dup;
555     void* dup_mem = value_memory(&dup);
556     native_slot_deep_copy(self->value_type, dup_mem, mem);
557 
558     if (!upb_strtable_insert2(&new_self->table,
559                               upb_strtable_iter_key(&it),
560                               upb_strtable_iter_keylength(&it),
561                               dup)) {
562       rb_raise(rb_eRuntimeError, "Error inserting value into new table");
563     }
564   }
565 
566   return new_map;
567 }
568 
569 /*
570  * call-seq:
571  *     Map.==(other) => boolean
572  *
573  * Compares this map to another. Maps are equal if they have identical key sets,
574  * and for each key, the values in both maps compare equal. Elements are
575  * compared as per normal Ruby semantics, by calling their :== methods (or
576  * performing a more efficient comparison for primitive types).
577  *
578  * Maps with dissimilar key types or value types/typeclasses are never equal,
579  * even if value comparison (for example, between integers and floats) would
580  * have otherwise indicated that every element has equal value.
581  */
Map_eq(VALUE _self,VALUE _other)582 VALUE Map_eq(VALUE _self, VALUE _other) {
583   Map* self = ruby_to_Map(_self);
584   Map* other;
585   upb_strtable_iter it;
586 
587   // Allow comparisons to Ruby hashmaps by converting to a temporary Map
588   // instance. Slow, but workable.
589   if (TYPE(_other) == T_HASH) {
590     VALUE other_map = Map_new_this_type(_self);
591     Map_merge_into_self(other_map, _other);
592     _other = other_map;
593   }
594 
595   other = ruby_to_Map(_other);
596 
597   if (self == other) {
598     return Qtrue;
599   }
600   if (self->key_type != other->key_type ||
601       self->value_type != other->value_type ||
602       self->value_type_class != other->value_type_class) {
603     return Qfalse;
604   }
605   if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) {
606     return Qfalse;
607   }
608 
609   // For each member of self, check that an equal member exists at the same key
610   // in other.
611   for (upb_strtable_begin(&it, &self->table);
612        !upb_strtable_done(&it);
613        upb_strtable_next(&it)) {
614 
615     upb_value v = upb_strtable_iter_value(&it);
616     void* mem = value_memory(&v);
617     upb_value other_v;
618     void* other_mem = value_memory(&other_v);
619 
620     if (!upb_strtable_lookup2(&other->table,
621                               upb_strtable_iter_key(&it),
622                               upb_strtable_iter_keylength(&it),
623                               &other_v)) {
624       // Not present in other map.
625       return Qfalse;
626     }
627 
628     if (!native_slot_eq(self->value_type, mem, other_mem)) {
629       // Present, but value not equal.
630       return Qfalse;
631     }
632   }
633 
634   return Qtrue;
635 }
636 
637 /*
638  * call-seq:
639  *     Map.hash => hash_value
640  *
641  * Returns a hash value based on this map's contents.
642  */
Map_hash(VALUE _self)643 VALUE Map_hash(VALUE _self) {
644   Map* self = ruby_to_Map(_self);
645 
646   st_index_t h = rb_hash_start(0);
647   VALUE hash_sym = rb_intern("hash");
648 
649   upb_strtable_iter it;
650   for (upb_strtable_begin(&it, &self->table);
651        !upb_strtable_done(&it);
652        upb_strtable_next(&it)) {
653     VALUE key = table_key_to_ruby(
654         self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
655 
656     upb_value v = upb_strtable_iter_value(&it);
657     void* mem = value_memory(&v);
658     VALUE value = native_slot_get(self->value_type,
659                                   self->value_type_class,
660                                   mem);
661 
662     h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0)));
663     h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0)));
664   }
665 
666   return INT2FIX(h);
667 }
668 
669 /*
670  * call-seq:
671  *     Map.to_h => {}
672  *
673  * Returns a Ruby Hash object containing all the values within the map
674  */
Map_to_h(VALUE _self)675 VALUE Map_to_h(VALUE _self) {
676   Map* self = ruby_to_Map(_self);
677   VALUE hash = rb_hash_new();
678   upb_strtable_iter it;
679   for (upb_strtable_begin(&it, &self->table);
680        !upb_strtable_done(&it);
681        upb_strtable_next(&it)) {
682     VALUE key = table_key_to_ruby(
683         self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
684     upb_value v = upb_strtable_iter_value(&it);
685     void* mem = value_memory(&v);
686     VALUE value = native_slot_get(self->value_type,
687                                   self->value_type_class,
688                                   mem);
689 
690     if (self->value_type == UPB_TYPE_MESSAGE) {
691       value = Message_to_h(value);
692     }
693     rb_hash_aset(hash, key, value);
694   }
695   return hash;
696 }
697 
698 /*
699  * call-seq:
700  *     Map.inspect => string
701  *
702  * Returns a string representing this map's elements. It will be formatted as
703  * "{key => value, key => value, ...}", with each key and value string
704  * representation computed by its own #inspect method.
705  */
Map_inspect(VALUE _self)706 VALUE Map_inspect(VALUE _self) {
707   Map* self = ruby_to_Map(_self);
708 
709   VALUE str = rb_str_new2("{");
710 
711   bool first = true;
712   VALUE inspect_sym = rb_intern("inspect");
713 
714   upb_strtable_iter it;
715   for (upb_strtable_begin(&it, &self->table);
716        !upb_strtable_done(&it);
717        upb_strtable_next(&it)) {
718     VALUE key = table_key_to_ruby(
719         self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
720 
721     upb_value v = upb_strtable_iter_value(&it);
722     void* mem = value_memory(&v);
723     VALUE value = native_slot_get(self->value_type,
724                                   self->value_type_class,
725                                   mem);
726 
727     if (!first) {
728       str = rb_str_cat2(str, ", ");
729     } else {
730       first = false;
731     }
732     str = rb_str_append(str, rb_funcall(key, inspect_sym, 0));
733     str = rb_str_cat2(str, "=>");
734     str = rb_str_append(str, rb_funcall(value, inspect_sym, 0));
735   }
736 
737   str = rb_str_cat2(str, "}");
738   return str;
739 }
740 
741 /*
742  * call-seq:
743  *     Map.merge(other_map) => map
744  *
745  * Copies key/value pairs from other_map into a copy of this map. If a key is
746  * set in other_map and this map, the value from other_map overwrites the value
747  * in the new copy of this map. Returns the new copy of this map with merged
748  * contents.
749  */
Map_merge(VALUE _self,VALUE hashmap)750 VALUE Map_merge(VALUE _self, VALUE hashmap) {
751   VALUE dupped = Map_dup(_self);
752   return Map_merge_into_self(dupped, hashmap);
753 }
754 
merge_into_self_callback(VALUE key,VALUE value,VALUE self)755 static int merge_into_self_callback(VALUE key, VALUE value, VALUE self) {
756   Map_index_set(self, key, value);
757   return ST_CONTINUE;
758 }
759 
760 // Used only internally -- shared by #merge and #initialize.
Map_merge_into_self(VALUE _self,VALUE hashmap)761 VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
762   if (TYPE(hashmap) == T_HASH) {
763     rb_hash_foreach(hashmap, merge_into_self_callback, _self);
764   } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) &&
765              RTYPEDDATA_TYPE(hashmap) == &Map_type) {
766 
767     Map* self = ruby_to_Map(_self);
768     Map* other = ruby_to_Map(hashmap);
769     upb_strtable_iter it;
770 
771     if (self->key_type != other->key_type ||
772         self->value_type != other->value_type ||
773         self->value_type_class != other->value_type_class) {
774       rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types");
775     }
776 
777     for (upb_strtable_begin(&it, &other->table);
778          !upb_strtable_done(&it);
779          upb_strtable_next(&it)) {
780 
781       // Replace any existing value by issuing a 'remove' operation first.
782       upb_value v;
783       upb_value oldv;
784       upb_strtable_remove2(&self->table,
785                            upb_strtable_iter_key(&it),
786                            upb_strtable_iter_keylength(&it),
787                            &oldv);
788 
789       v = upb_strtable_iter_value(&it);
790       upb_strtable_insert2(&self->table,
791                            upb_strtable_iter_key(&it),
792                            upb_strtable_iter_keylength(&it),
793                            v);
794     }
795   } else {
796     rb_raise(rb_eArgError, "Unknown type merging into Map");
797   }
798   return _self;
799 }
800 
801 // Internal method: map iterator initialization (used for serialization).
Map_begin(VALUE _self,Map_iter * iter)802 void Map_begin(VALUE _self, Map_iter* iter) {
803   Map* self = ruby_to_Map(_self);
804   iter->self = self;
805   upb_strtable_begin(&iter->it, &self->table);
806 }
807 
Map_next(Map_iter * iter)808 void Map_next(Map_iter* iter) {
809   upb_strtable_next(&iter->it);
810 }
811 
Map_done(Map_iter * iter)812 bool Map_done(Map_iter* iter) {
813   return upb_strtable_done(&iter->it);
814 }
815 
Map_iter_key(Map_iter * iter)816 VALUE Map_iter_key(Map_iter* iter) {
817   return table_key_to_ruby(
818       iter->self,
819       upb_strtable_iter_key(&iter->it),
820       upb_strtable_iter_keylength(&iter->it));
821 }
822 
Map_iter_value(Map_iter * iter)823 VALUE Map_iter_value(Map_iter* iter) {
824   upb_value v = upb_strtable_iter_value(&iter->it);
825   void* mem = value_memory(&v);
826   return native_slot_get(iter->self->value_type,
827                          iter->self->value_type_class,
828                          mem);
829 }
830 
Map_register(VALUE module)831 void Map_register(VALUE module) {
832   VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
833   rb_define_alloc_func(klass, Map_alloc);
834   rb_gc_register_address(&cMap);
835   cMap = klass;
836 
837   rb_define_method(klass, "initialize", Map_init, -1);
838   rb_define_method(klass, "each", Map_each, 0);
839   rb_define_method(klass, "keys", Map_keys, 0);
840   rb_define_method(klass, "values", Map_values, 0);
841   rb_define_method(klass, "[]", Map_index, 1);
842   rb_define_method(klass, "[]=", Map_index_set, 2);
843   rb_define_method(klass, "has_key?", Map_has_key, 1);
844   rb_define_method(klass, "delete", Map_delete, 1);
845   rb_define_method(klass, "clear", Map_clear, 0);
846   rb_define_method(klass, "length", Map_length, 0);
847   rb_define_method(klass, "dup", Map_dup, 0);
848   rb_define_method(klass, "==", Map_eq, 1);
849   rb_define_method(klass, "hash", Map_hash, 0);
850   rb_define_method(klass, "to_h", Map_to_h, 0);
851   rb_define_method(klass, "inspect", Map_inspect, 0);
852   rb_define_method(klass, "merge", Map_merge, 1);
853   rb_include_module(klass, rb_mEnumerable);
854 }
855