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