ruby/ext/google/protobuf_c/map.c
Go to the documentation of this file.
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)
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);
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 
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) ?
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 
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 
139 Map* ruby_to_Map(VALUE _self) {
140  Map* self;
141  TypedData_Get_Struct(_self, Map, &Map_type, self);
142  return self;
143 }
144 
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) {
155  for (upb_strtable_begin(&it, &self->table);
157  upb_strtable_next(&it)) {
159  void* mem = value_memory(&v);
160  native_slot_mark(self->value_type, mem);
161  }
162  }
163 }
164 
165 void Map_free(void* _self) {
166  Map* self = _self;
167  upb_strtable_uninit(&self->table);
168  xfree(self);
169 }
170 
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 
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 
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  */
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  */
282 VALUE Map_each(VALUE _self) {
283  Map* self = ruby_to_Map(_self);
284 
286  for (upb_strtable_begin(&it, &self->table);
288  upb_strtable_next(&it)) {
289 
290  VALUE key = table_key_to_ruby(
292 
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  */
311 VALUE Map_keys(VALUE _self) {
312  Map* self = ruby_to_Map(_self);
313 
314  VALUE ret = rb_ary_new();
316  for (upb_strtable_begin(&it, &self->table);
318  upb_strtable_next(&it)) {
319 
320  VALUE key = table_key_to_ruby(
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  */
335 VALUE Map_values(VALUE _self) {
336  Map* self = ruby_to_Map(_self);
337 
338  VALUE ret = rb_ary_new();
340  for (upb_strtable_begin(&it, &self->table);
342  upb_strtable_next(&it)) {
343 
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  */
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  */
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  */
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  */
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  */
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  */
487 VALUE Map_length(VALUE _self) {
488  Map* self = ruby_to_Map(_self);
489  return ULL2NUM(upb_strtable_count(&self->table));
490 }
491 
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,
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  */
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 
520  for (upb_strtable_begin(&it, &self->table);
522  upb_strtable_next(&it)) {
523 
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,
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.
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 
548  for (upb_strtable_begin(&it, &self->table);
550  upb_strtable_next(&it)) {
551 
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,
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  */
582 VALUE Map_eq(VALUE _self, VALUE _other) {
583  Map* self = ruby_to_Map(_self);
584  Map* other;
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);
613  upb_strtable_next(&it)) {
614 
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,
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  */
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 
650  for (upb_strtable_begin(&it, &self->table);
652  upb_strtable_next(&it)) {
653  VALUE key = table_key_to_ruby(
655 
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  */
675 VALUE Map_to_h(VALUE _self) {
676  Map* self = ruby_to_Map(_self);
677  VALUE hash = rb_hash_new();
679  for (upb_strtable_begin(&it, &self->table);
681  upb_strtable_next(&it)) {
682  VALUE key = table_key_to_ruby(
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) {
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  */
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 
715  for (upb_strtable_begin(&it, &self->table);
717  upb_strtable_next(&it)) {
718  VALUE key = table_key_to_ruby(
720 
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  */
750 VALUE Map_merge(VALUE _self, VALUE hashmap) {
751  VALUE dupped = Map_dup(_self);
752  return Map_merge_into_self(dupped, hashmap);
753 }
754 
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.
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);
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);
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,
787  &oldv);
788 
790  upb_strtable_insert2(&self->table,
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).
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 
808 void Map_next(Map_iter* iter) {
809  upb_strtable_next(&iter->it);
810 }
811 
812 bool Map_done(Map_iter* iter) {
813  return upb_strtable_done(&iter->it);
814 }
815 
816 VALUE Map_iter_key(Map_iter* iter) {
817  return table_key_to_ruby(
818  iter->self,
819  upb_strtable_iter_key(&iter->it),
821 }
822 
823 VALUE Map_iter_value(Map_iter* iter) {
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 
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 }
native_slot_dup
void native_slot_dup(upb_fieldtype_t type, void *to, void *from)
Definition: ruby/ext/google/protobuf_c/storage.c:358
Map_inspect
VALUE Map_inspect(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:706
native_slot_set
bool native_slot_set(upb_fieldtype_t type, const zend_class_entry *klass, void *memory, zval *value TSRMLS_DC)
Definition: php/ext/google/protobuf/storage.c:87
Map_merge
VALUE Map_merge(VALUE _self, VALUE hashmap)
Definition: ruby/ext/google/protobuf_c/map.c:750
cMap
VALUE cMap
Definition: ruby/ext/google/protobuf_c/map.c:137
value_memory
static void * value_memory(upb_value *v)
Definition: ruby/ext/google/protobuf_c/map.c:124
Map
Definition: ruby/ext/google/protobuf_c/protobuf.h:442
Map_values
VALUE Map_values(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:335
NULL
NULL
Definition: test_security_zap.cpp:405
Map_delete
VALUE Map_delete(VALUE _self, VALUE key)
Definition: ruby/ext/google/protobuf_c/map.c:442
upb_strtable_iter_keylength
size_t upb_strtable_iter_keylength(const upb_strtable_iter *i)
Definition: php/ext/google/protobuf/upb.c:5029
length
GLenum GLuint GLenum GLsizei length
Definition: glcorearb.h:2695
Message_to_h
VALUE Message_to_h(VALUE _self)
Definition: ruby/ext/google/protobuf_c/message.c:599
table_key_to_ruby
static VALUE table_key_to_ruby(Map *self, const char *buf, size_t length)
Definition: ruby/ext/google/protobuf_c/map.c:100
Map_merge_into_self
VALUE Map_merge_into_self(VALUE _self, VALUE hashmap)
Definition: ruby/ext/google/protobuf_c/map.c:761
upb_value
Definition: php/ext/google/protobuf/upb.h:2656
Map_dup
VALUE Map_dup(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:514
UPB_TYPE_INT32
@ UPB_TYPE_INT32
Definition: php/ext/google/protobuf/upb.h:415
Map_iter
Definition: ruby/ext/google/protobuf_c/protobuf.h:480
self
PHP_PROTO_OBJECT_FREE_END PHP_PROTO_OBJECT_DTOR_END intern self
Definition: php/ext/google/protobuf/map.c:543
Map_clear
VALUE Map_clear(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:467
protobuf.h
Map_done
bool Map_done(Map_iter *iter)
Definition: ruby/ext/google/protobuf_c/map.c:812
validate_type_class
void validate_type_class(upb_fieldtype_t type, VALUE klass)
Definition: repeated_field.c:523
upb_strtable_init
UPB_INLINE bool upb_strtable_init(upb_strtable *table, upb_ctype_t ctype)
Definition: php/ext/google/protobuf/upb.h:2880
map
zval * map
Definition: php/ext/google/protobuf/encode_decode.c:473
UPB_CTYPE_UINT64
@ UPB_CTYPE_UINT64
Definition: php/ext/google/protobuf/upb.h:2646
native_slot_size
size_t native_slot_size(upb_fieldtype_t type)
Definition: php/ext/google/protobuf/storage.c:43
TABLE_KEY_BUF_LENGTH
#define TABLE_KEY_BUF_LENGTH
Definition: ruby/ext/google/protobuf_c/map.c:65
native_slot_eq
bool native_slot_eq(upb_fieldtype_t type, void *mem1, void *mem2)
Definition: ruby/ext/google/protobuf_c/storage.c:382
Map::value_type_class
VALUE value_type_class
Definition: ruby/ext/google/protobuf_c/protobuf.h:445
ruby_to_Map
Map * ruby_to_Map(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:139
ALLOC
#define ALLOC(class_name)
Definition: php/ext/google/protobuf/protobuf.h:1477
merge_into_self_callback
static int merge_into_self_callback(VALUE key, VALUE value, VALUE self)
Definition: ruby/ext/google/protobuf_c/map.c:755
Map_mark
void Map_mark(void *_self)
Definition: ruby/ext/google/protobuf_c/map.c:145
key_type
upb_fieldtype_t key_type
Definition: php/ext/google/protobuf/protobuf.h:1062
UPB_TYPE_UINT32
@ UPB_TYPE_UINT32
Definition: php/ext/google/protobuf/upb.h:416
upb_strtable_next
void upb_strtable_next(upb_strtable_iter *i)
Definition: php/ext/google/protobuf/upb.c:5014
Map_each
VALUE Map_each(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:282
needs_typeclass
static bool needs_typeclass(upb_fieldtype_t type)
Definition: ruby/ext/google/protobuf_c/map.c:184
Map_index
VALUE Map_index(VALUE _self, VALUE key)
Definition: ruby/ext/google/protobuf_c/map.c:363
upb_strtable_done
bool upb_strtable_done(const upb_strtable_iter *i)
Definition: php/ext/google/protobuf/upb.c:5018
upb_strtable_iter_key
const char * upb_strtable_iter_key(const upb_strtable_iter *i)
Definition: php/ext/google/protobuf/upb.c:5024
Map_next
void Map_next(Map_iter *iter)
Definition: ruby/ext/google/protobuf_c/map.c:808
Map_keys
VALUE Map_keys(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:311
kRubyStringUtf8Encoding
rb_encoding * kRubyStringUtf8Encoding
Definition: ruby/ext/google/protobuf_c/protobuf.c:67
native_slot_mark
void native_slot_mark(upb_fieldtype_t type, void *memory)
Definition: ruby/ext/google/protobuf_c/storage.c:346
update_failure_list.str
str
Definition: update_failure_list.py:41
upb_strtable_iter_value
upb_value upb_strtable_iter_value(const upb_strtable_iter *i)
Definition: php/ext/google/protobuf/upb.c:5036
UPB_TYPE_STRING
@ UPB_TYPE_STRING
Definition: php/ext/google/protobuf/upb.h:419
upb_strtable_uninit
UPB_INLINE void upb_strtable_uninit(upb_strtable *table)
Definition: php/ext/google/protobuf/upb.h:2888
native_slot_deep_copy
void native_slot_deep_copy(upb_fieldtype_t type, void *to, void *from)
Definition: ruby/ext/google/protobuf_c/storage.c:362
kRubyString8bitEncoding
rb_encoding * kRubyString8bitEncoding
Definition: ruby/ext/google/protobuf_c/protobuf.c:69
native_slot_get
void native_slot_get(upb_fieldtype_t type, const void *memory, CACHED_VALUE *cache TSRMLS_DC)
Definition: php/ext/google/protobuf/storage.c:311
UPB_TYPE_MESSAGE
@ UPB_TYPE_MESSAGE
Definition: php/ext/google/protobuf/upb.h:421
Map_begin
void Map_begin(VALUE _self, Map_iter *iter)
Definition: ruby/ext/google/protobuf_c/map.c:802
buf
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glcorearb.h:4175
UPB_TYPE_ENUM
@ UPB_TYPE_ENUM
Definition: php/ext/google/protobuf/upb.h:417
key
const SETUP_TEARDOWN_TESTCONTEXT char * key
Definition: test_wss_transport.cpp:10
Map_eq
VALUE Map_eq(VALUE _self, VALUE _other)
Definition: ruby/ext/google/protobuf_c/map.c:582
UPB_CTYPE_INT64
@ UPB_CTYPE_INT64
Definition: php/ext/google/protobuf/upb.h:2644
Map::table
upb_strtable table
Definition: ruby/ext/google/protobuf_c/protobuf.h:447
Map_hash
VALUE Map_hash(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:643
Map_length
VALUE Map_length(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:487
Map_register
void Map_register(VALUE module)
Definition: ruby/ext/google/protobuf_c/map.c:831
UPB_TYPE_UINT64
@ UPB_TYPE_UINT64
Definition: php/ext/google/protobuf/upb.h:425
Map_iter::it
upb_strtable_iter it
Definition: ruby/ext/google/protobuf_c/protobuf.h:482
Map_init
VALUE Map_init(int argc, VALUE *argv, VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:224
Map_alloc
VALUE Map_alloc(VALUE klass)
Definition: ruby/ext/google/protobuf_c/map.c:171
upb_strtable_insert2
UPB_INLINE bool upb_strtable_insert2(upb_strtable *t, const char *key, size_t len, upb_value val)
Definition: php/ext/google/protobuf/upb.h:2922
type
GLenum type
Definition: glcorearb.h:2695
table_key
static VALUE table_key(Map *self, VALUE key, char *buf, const char **out_key, size_t *out_length)
Definition: ruby/ext/google/protobuf_c/map.c:66
Map_index_set
VALUE Map_index_set(VALUE _self, VALUE key, VALUE value)
Definition: ruby/ext/google/protobuf_c/map.c:388
v
const GLdouble * v
Definition: glcorearb.h:3106
Map_set_frame
VALUE Map_set_frame(VALUE map, VALUE val)
Definition: ruby/ext/google/protobuf_c/map.c:178
fieldtype_to_ruby
VALUE fieldtype_to_ruby(upb_fieldtype_t type)
Definition: defs.c:752
Map::key_type
upb_fieldtype_t key_type
Definition: ruby/ext/google/protobuf_c/protobuf.h:443
value_type
zend_class_entry * value_type
Definition: php/ext/google/protobuf/message.c:2546
native_slot_encode_and_freeze_string
VALUE native_slot_encode_and_freeze_string(upb_fieldtype_t type, VALUE value)
Definition: ruby/ext/google/protobuf_c/storage.c:94
upb_strtable_begin
void upb_strtable_begin(upb_strtable_iter *i, const upb_strtable *t)
Definition: php/ext/google/protobuf/upb.c:5009
Map_to_h
VALUE Map_to_h(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:675
UPB_TYPE_BYTES
@ UPB_TYPE_BYTES
Definition: php/ext/google/protobuf/upb.h:420
Map_iter::self
Map * self
Definition: ruby/ext/google/protobuf_c/protobuf.h:481
first
GLint first
Definition: glcorearb.h:2830
TYPE
#define TYPE(u, l)
Definition: php/ext/google/protobuf/upb.c:8510
UPB_TYPE_INT64
@ UPB_TYPE_INT64
Definition: php/ext/google/protobuf/upb.h:424
Map_has_key
VALUE Map_has_key(VALUE _self, VALUE key)
Definition: ruby/ext/google/protobuf_c/map.c:420
val
GLuint GLfloat * val
Definition: glcorearb.h:3604
Map_iter_value
VALUE Map_iter_value(Map_iter *iter)
Definition: ruby/ext/google/protobuf_c/map.c:823
Map_type
const rb_data_type_t Map_type
Definition: ruby/ext/google/protobuf_c/map.c:132
value
GLsizei const GLfloat * value
Definition: glcorearb.h:3093
Map_free
void Map_free(void *_self)
Definition: ruby/ext/google/protobuf_c/map.c:165
upb_strtable_lookup2
bool upb_strtable_lookup2(const upb_strtable *t, const char *key, size_t len, upb_value *v)
Definition: php/ext/google/protobuf/upb.c:4985
upb_strtable_iter
Definition: php/ext/google/protobuf/upb.h:3061
ruby_to_fieldtype
upb_fieldtype_t ruby_to_fieldtype(VALUE type)
Definition: defs.c:724
Map::value_type
upb_fieldtype_t value_type
Definition: ruby/ext/google/protobuf_c/protobuf.h:444
Map_new_this_type
static VALUE Map_new_this_type(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:492
UPB_TYPE_BOOL
@ UPB_TYPE_BOOL
Definition: php/ext/google/protobuf/upb.h:412
upb_strtable_count
UPB_INLINE size_t upb_strtable_count(const upb_strtable *t)
Definition: php/ext/google/protobuf/upb.h:2894
Map_iter_key
VALUE Map_iter_key(Map_iter *iter)
Definition: ruby/ext/google/protobuf_c/map.c:816
upb_strtable_remove2
UPB_INLINE bool upb_strtable_remove2(upb_strtable *t, const char *key, size_t len, upb_value *val)
Definition: php/ext/google/protobuf/upb.h:2951
klass
zend_class_entry * klass
Definition: php/ext/google/protobuf/protobuf.h:801
it
MapIter it
Definition: php/ext/google/protobuf/map.c:205
upb_fieldtype_t
upb_fieldtype_t
Definition: php/ext/google/protobuf/upb.h:410
h
GLfloat GLfloat GLfloat GLfloat h
Definition: glcorearb.h:4147
Map_deep_copy
VALUE Map_deep_copy(VALUE _self)
Definition: ruby/ext/google/protobuf_c/map.c:542


libaditof
Author(s):
autogenerated on Wed May 21 2025 02:06:55