google-protobuf 3.14.0-universal-darwin → 3.15.0.rc.1-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of google-protobuf might be problematic. Click here for more details.

@@ -0,0 +1,107 @@
1
+ // Protocol Buffers - Google's data interchange format
2
+ // Copyright 2008 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
+ #ifndef RUBY_PROTOBUF_DEFS_H_
32
+ #define RUBY_PROTOBUF_DEFS_H_
33
+
34
+ #include <ruby/ruby.h>
35
+
36
+ #include "protobuf.h"
37
+ #include "ruby-upb.h"
38
+
39
+ // -----------------------------------------------------------------------------
40
+ // TypeInfo
41
+ // -----------------------------------------------------------------------------
42
+
43
+ // This bundles a upb_fieldtype_t and msgdef/enumdef when appropriate. This is
44
+ // convenient for functions that need type information but cannot necessarily
45
+ // assume a upb_fielddef will be available.
46
+ //
47
+ // For example, Google::Protobuf::Map and Google::Protobuf::RepeatedField can
48
+ // be constructed with type information alone:
49
+ //
50
+ // # RepeatedField will internally store the type information in a TypeInfo.
51
+ // Google::Protobuf::RepeatedField.new(:message, FooMessage)
52
+
53
+ typedef struct {
54
+ upb_fieldtype_t type;
55
+ union {
56
+ const upb_msgdef* msgdef; // When type == UPB_TYPE_MESSAGE
57
+ const upb_enumdef* enumdef; // When type == UPB_TYPE_ENUM
58
+ } def;
59
+ } TypeInfo;
60
+
61
+ static inline TypeInfo TypeInfo_get(const upb_fielddef *f) {
62
+ TypeInfo ret = {upb_fielddef_type(f), {NULL}};
63
+ switch (ret.type) {
64
+ case UPB_TYPE_MESSAGE:
65
+ ret.def.msgdef = upb_fielddef_msgsubdef(f);
66
+ break;
67
+ case UPB_TYPE_ENUM:
68
+ ret.def.enumdef = upb_fielddef_enumsubdef(f);
69
+ break;
70
+ default:
71
+ break;
72
+ }
73
+ return ret;
74
+ }
75
+
76
+ TypeInfo TypeInfo_FromClass(int argc, VALUE* argv, int skip_arg,
77
+ VALUE* type_class, VALUE* init_arg);
78
+
79
+ static inline TypeInfo TypeInfo_from_type(upb_fieldtype_t type) {
80
+ TypeInfo ret = {type};
81
+ assert(type != UPB_TYPE_MESSAGE && type != UPB_TYPE_ENUM);
82
+ return ret;
83
+ }
84
+
85
+ // -----------------------------------------------------------------------------
86
+ // Other utilities
87
+ // -----------------------------------------------------------------------------
88
+
89
+ VALUE Descriptor_DefToClass(const upb_msgdef *m);
90
+
91
+ // Returns the underlying msgdef, enumdef, or symtab (respectively) for the
92
+ // given Descriptor, EnumDescriptor, or DescriptorPool Ruby object.
93
+ const upb_enumdef *EnumDescriptor_GetEnumDef(VALUE enum_desc_rb);
94
+ const upb_symtab *DescriptorPool_GetSymtab(VALUE desc_pool_rb);
95
+ const upb_msgdef *Descriptor_GetMsgDef(VALUE desc_rb);
96
+
97
+ // Returns a upb field type for the given Ruby symbol
98
+ // (eg. :float => UPB_TYPE_FLOAT).
99
+ upb_fieldtype_t ruby_to_fieldtype(VALUE type);
100
+
101
+ // The singleton generated pool (a DescriptorPool object).
102
+ extern VALUE generated_pool;
103
+
104
+ // Call at startup to register all types in this module.
105
+ void Defs_register(VALUE module);
106
+
107
+ #endif // RUBY_PROTOBUF_DEFS_H_
@@ -3,9 +3,9 @@
3
3
  require 'mkmf'
4
4
 
5
5
  if RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/
6
- $CFLAGS += " -std=gnu90 -O3 -DNDEBUG -Wall -Wdeclaration-after-statement -Wsign-compare"
6
+ $CFLAGS += " -std=gnu99 -O3 -DNDEBUG -fvisibility=hidden -Wall -Wsign-compare -Wno-declaration-after-statement"
7
7
  else
8
- $CFLAGS += " -std=gnu90 -O3 -DNDEBUG"
8
+ $CFLAGS += " -std=gnu99 -O3 -DNDEBUG"
9
9
  end
10
10
 
11
11
 
@@ -14,8 +14,7 @@ if RUBY_PLATFORM =~ /linux/
14
14
  $LDFLAGS += " -Wl,-wrap,memcpy"
15
15
  end
16
16
 
17
- $objs = ["protobuf.o", "defs.o", "storage.o", "message.o",
18
- "repeated_field.o", "map.o", "encode_decode.o", "upb.o",
19
- "wrap_memcpy.o"]
17
+ $objs = ["protobuf.o", "convert.o", "defs.o", "message.o",
18
+ "repeated_field.o", "map.o", "ruby-upb.o", "wrap_memcpy.o"]
20
19
 
21
20
  create_makefile("google/protobuf_c")
@@ -28,170 +28,231 @@
28
28
  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
29
  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
30
 
31
+ #include "convert.h"
32
+ #include "defs.h"
33
+ #include "message.h"
31
34
  #include "protobuf.h"
32
35
 
33
36
  // -----------------------------------------------------------------------------
34
- // Basic map operations on top of upb's strtable.
37
+ // Basic map operations on top of upb_map.
35
38
  //
36
39
  // Note that we roll our own `Map` container here because, as for
37
40
  // `RepeatedField`, we want a strongly-typed container. This is so that any user
38
41
  // errors due to incorrect map key or value types are raised as close as
39
42
  // possible to the error site, rather than at some deferred point (e.g.,
40
43
  // 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
44
  // -----------------------------------------------------------------------------
47
45
 
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
- if (TYPE(key) == T_SYMBOL) {
75
- key = rb_id2str(SYM2ID(key));
76
- }
77
- Check_Type(key, T_STRING);
78
- key = native_slot_encode_and_freeze_string(self->key_type, key);
79
- *out_key = RSTRING_PTR(key);
80
- *out_length = RSTRING_LEN(key);
81
- break;
82
-
83
- case UPB_TYPE_BOOL:
84
- case UPB_TYPE_INT32:
85
- case UPB_TYPE_INT64:
86
- case UPB_TYPE_UINT32:
87
- case UPB_TYPE_UINT64:
88
- native_slot_set("", self->key_type, Qnil, buf, key);
89
- *out_key = buf;
90
- *out_length = native_slot_size(self->key_type);
91
- break;
92
-
93
- default:
94
- // Map constructor should not allow a Map with another key type to be
95
- // constructed.
96
- assert(false);
97
- break;
98
- }
99
-
100
- return key;
101
- }
102
-
103
- static VALUE table_key_to_ruby(Map* self, upb_strview key) {
104
- switch (self->key_type) {
105
- case UPB_TYPE_BYTES:
106
- case UPB_TYPE_STRING: {
107
- VALUE ret = rb_str_new(key.data, key.size);
108
- rb_enc_associate(ret,
109
- (self->key_type == UPB_TYPE_BYTES) ?
110
- kRubyString8bitEncoding : kRubyStringUtf8Encoding);
111
- return ret;
112
- }
113
-
114
- case UPB_TYPE_BOOL:
115
- case UPB_TYPE_INT32:
116
- case UPB_TYPE_INT64:
117
- case UPB_TYPE_UINT32:
118
- case UPB_TYPE_UINT64:
119
- return native_slot_get(self->key_type, Qnil, key.data);
120
-
121
- default:
122
- assert(false);
123
- return Qnil;
124
- }
125
- }
126
-
127
- static void* value_memory(upb_value* v) {
128
- return (void*)(&v->val);
129
- }
130
-
131
46
  // -----------------------------------------------------------------------------
132
47
  // Map container type.
133
48
  // -----------------------------------------------------------------------------
134
49
 
50
+ typedef struct {
51
+ const upb_map *map; // Can convert to mutable when non-frozen.
52
+ upb_fieldtype_t key_type;
53
+ TypeInfo value_type_info;
54
+ VALUE value_type_class;
55
+ VALUE arena;
56
+ } Map;
57
+
58
+ static void Map_mark(void* _self) {
59
+ Map* self = _self;
60
+ rb_gc_mark(self->value_type_class);
61
+ rb_gc_mark(self->arena);
62
+ }
63
+
135
64
  const rb_data_type_t Map_type = {
136
65
  "Google::Protobuf::Map",
137
- { Map_mark, Map_free, NULL },
66
+ { Map_mark, RUBY_DEFAULT_FREE, NULL },
67
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
138
68
  };
139
69
 
140
70
  VALUE cMap;
141
71
 
142
- Map* ruby_to_Map(VALUE _self) {
72
+ static Map* ruby_to_Map(VALUE _self) {
143
73
  Map* self;
144
74
  TypedData_Get_Struct(_self, Map, &Map_type, self);
145
75
  return self;
146
76
  }
147
77
 
148
- void Map_mark(void* _self) {
149
- Map* self = _self;
78
+ static VALUE Map_alloc(VALUE klass) {
79
+ Map* self = ALLOC(Map);
80
+ self->map = NULL;
81
+ self->value_type_class = Qnil;
82
+ self->value_type_info.def.msgdef = NULL;
83
+ self->arena = Qnil;
84
+ return TypedData_Wrap_Struct(klass, &Map_type, self);
85
+ }
150
86
 
151
- rb_gc_mark(self->value_type_class);
152
- rb_gc_mark(self->parse_frame);
153
-
154
- if (self->value_type == UPB_TYPE_STRING ||
155
- self->value_type == UPB_TYPE_BYTES ||
156
- self->value_type == UPB_TYPE_MESSAGE) {
157
- upb_strtable_iter it;
158
- for (upb_strtable_begin(&it, &self->table);
159
- !upb_strtable_done(&it);
160
- upb_strtable_next(&it)) {
161
- upb_value v = upb_strtable_iter_value(&it);
162
- void* mem = value_memory(&v);
163
- native_slot_mark(self->value_type, mem);
87
+ VALUE Map_GetRubyWrapper(upb_map* map, upb_fieldtype_t key_type,
88
+ TypeInfo value_type, VALUE arena) {
89
+ PBRUBY_ASSERT(map);
90
+
91
+ VALUE val = ObjectCache_Get(map);
92
+
93
+ if (val == Qnil) {
94
+ val = Map_alloc(cMap);
95
+ Map* self;
96
+ ObjectCache_Add(map, val, Arena_get(arena));
97
+ TypedData_Get_Struct(val, Map, &Map_type, self);
98
+ self->map = map;
99
+ self->arena = arena;
100
+ self->key_type = key_type;
101
+ self->value_type_info = value_type;
102
+ if (self->value_type_info.type == UPB_TYPE_MESSAGE) {
103
+ const upb_msgdef *val_m = self->value_type_info.def.msgdef;
104
+ self->value_type_class = Descriptor_DefToClass(val_m);
164
105
  }
165
106
  }
107
+
108
+ return val;
166
109
  }
167
110
 
168
- void Map_free(void* _self) {
169
- Map* self = _self;
170
- upb_strtable_uninit(&self->table);
171
- xfree(self);
111
+ static VALUE Map_new_this_type(Map *from) {
112
+ VALUE arena_rb = Arena_new();
113
+ upb_map* map = upb_map_new(Arena_get(arena_rb), from->key_type,
114
+ from->value_type_info.type);
115
+ VALUE ret =
116
+ Map_GetRubyWrapper(map, from->key_type, from->value_type_info, arena_rb);
117
+ PBRUBY_ASSERT(ruby_to_Map(ret)->value_type_class == from->value_type_class);
118
+ return ret;
172
119
  }
173
120
 
174
- VALUE Map_alloc(VALUE klass) {
175
- Map* self = ALLOC(Map);
176
- memset(self, 0, sizeof(Map));
177
- self->value_type_class = Qnil;
178
- return TypedData_Wrap_Struct(klass, &Map_type, self);
121
+ static TypeInfo Map_keyinfo(Map* self) {
122
+ TypeInfo ret;
123
+ ret.type = self->key_type;
124
+ ret.def.msgdef = NULL;
125
+ return ret;
179
126
  }
180
127
 
181
- VALUE Map_set_frame(VALUE map, VALUE val) {
182
- Map* self = ruby_to_Map(map);
183
- self->parse_frame = val;
184
- return val;
128
+ static upb_map *Map_GetMutable(VALUE _self) {
129
+ rb_check_frozen(_self);
130
+ return (upb_map*)ruby_to_Map(_self)->map;
185
131
  }
186
132
 
187
- static bool needs_typeclass(upb_fieldtype_t type) {
188
- switch (type) {
189
- case UPB_TYPE_MESSAGE:
190
- case UPB_TYPE_ENUM:
191
- return true;
192
- default:
193
- return false;
133
+ VALUE Map_CreateHash(const upb_map* map, upb_fieldtype_t key_type,
134
+ TypeInfo val_info) {
135
+ VALUE hash = rb_hash_new();
136
+ size_t iter = UPB_MAP_BEGIN;
137
+ TypeInfo key_info = TypeInfo_from_type(key_type);
138
+
139
+ if (!map) return hash;
140
+
141
+ while (upb_mapiter_next(map, &iter)) {
142
+ upb_msgval key = upb_mapiter_key(map, iter);
143
+ upb_msgval val = upb_mapiter_value(map, iter);
144
+ VALUE key_val = Convert_UpbToRuby(key, key_info, Qnil);
145
+ VALUE val_val = Scalar_CreateHash(val, val_info);
146
+ rb_hash_aset(hash, key_val, val_val);
147
+ }
148
+
149
+ return hash;
150
+ }
151
+
152
+ VALUE Map_deep_copy(VALUE obj) {
153
+ Map* self = ruby_to_Map(obj);
154
+ VALUE new_arena_rb = Arena_new();
155
+ upb_arena *arena = Arena_get(new_arena_rb);
156
+ upb_map* new_map =
157
+ upb_map_new(arena, self->key_type, self->value_type_info.type);
158
+ size_t iter = UPB_MAP_BEGIN;
159
+ while (upb_mapiter_next(self->map, &iter)) {
160
+ upb_msgval key = upb_mapiter_key(self->map, iter);
161
+ upb_msgval val = upb_mapiter_value(self->map, iter);
162
+ upb_msgval val_copy = Msgval_DeepCopy(val, self->value_type_info, arena);
163
+ upb_map_set(new_map, key, val_copy, arena);
164
+ }
165
+
166
+ return Map_GetRubyWrapper(new_map, self->key_type, self->value_type_info,
167
+ new_arena_rb);
168
+ }
169
+
170
+ const upb_map* Map_GetUpbMap(VALUE val, const upb_fielddef *field) {
171
+ const upb_fielddef* key_field = map_field_key(field);
172
+ const upb_fielddef* value_field = map_field_value(field);
173
+ TypeInfo value_type_info = TypeInfo_get(value_field);
174
+ Map* self;
175
+
176
+ if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) ||
177
+ RTYPEDDATA_TYPE(val) != &Map_type) {
178
+ rb_raise(cTypeError, "Expected Map instance");
179
+ }
180
+
181
+ self = ruby_to_Map(val);
182
+ if (self->key_type != upb_fielddef_type(key_field)) {
183
+ rb_raise(cTypeError, "Map key type does not match field's key type");
184
+ }
185
+ if (self->value_type_info.type != value_type_info.type) {
186
+ rb_raise(cTypeError, "Map value type does not match field's value type");
187
+ }
188
+ if (self->value_type_info.def.msgdef != value_type_info.def.msgdef) {
189
+ rb_raise(cTypeError, "Map value type has wrong message/enum class");
190
+ }
191
+
192
+ return self->map;
193
+ }
194
+
195
+ void Map_Inspect(StringBuilder* b, const upb_map* map, upb_fieldtype_t key_type,
196
+ TypeInfo val_type) {
197
+ bool first = true;
198
+ TypeInfo key_type_info = {key_type};
199
+ StringBuilder_Printf(b, "{");
200
+ if (map) {
201
+ size_t iter = UPB_MAP_BEGIN;
202
+ while (upb_mapiter_next(map, &iter)) {
203
+ upb_msgval key = upb_mapiter_key(map, iter);
204
+ upb_msgval val = upb_mapiter_value(map, iter);
205
+ if (first) {
206
+ first = false;
207
+ } else {
208
+ StringBuilder_Printf(b, ", ");
209
+ }
210
+ StringBuilder_PrintMsgval(b, key, key_type_info);
211
+ StringBuilder_Printf(b, "=>");
212
+ StringBuilder_PrintMsgval(b, val, val_type);
213
+ }
214
+ }
215
+ StringBuilder_Printf(b, "}");
216
+ }
217
+
218
+ static int merge_into_self_callback(VALUE key, VALUE val, VALUE _self) {
219
+ Map* self = ruby_to_Map(_self);
220
+ upb_arena *arena = Arena_get(self->arena);
221
+ upb_msgval key_val = Convert_RubyToUpb(key, "", Map_keyinfo(self), arena);
222
+ upb_msgval val_val = Convert_RubyToUpb(val, "", self->value_type_info, arena);
223
+ upb_map_set(Map_GetMutable(_self), key_val, val_val, arena);
224
+ return ST_CONTINUE;
225
+ }
226
+
227
+ // Used only internally -- shared by #merge and #initialize.
228
+ static VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
229
+ if (TYPE(hashmap) == T_HASH) {
230
+ rb_hash_foreach(hashmap, merge_into_self_callback, _self);
231
+ } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) &&
232
+ RTYPEDDATA_TYPE(hashmap) == &Map_type) {
233
+ Map* self = ruby_to_Map(_self);
234
+ Map* other = ruby_to_Map(hashmap);
235
+ upb_arena *arena = Arena_get(self->arena);
236
+ upb_msg *self_msg = Map_GetMutable(_self);
237
+ size_t iter = UPB_MAP_BEGIN;
238
+
239
+ upb_arena_fuse(arena, Arena_get(other->arena));
240
+
241
+ if (self->key_type != other->key_type ||
242
+ self->value_type_info.type != other->value_type_info.type ||
243
+ self->value_type_class != other->value_type_class) {
244
+ rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types");
245
+ }
246
+
247
+ while (upb_mapiter_next(other->map, &iter)) {
248
+ upb_msgval key = upb_mapiter_key(other->map, iter);
249
+ upb_msgval val = upb_mapiter_value(other->map, iter);
250
+ upb_map_set(self_msg, key, val, arena);
251
+ }
252
+ } else {
253
+ rb_raise(rb_eArgError, "Unknown type merging into Map");
194
254
  }
255
+ return _self;
195
256
  }
196
257
 
197
258
  /*
@@ -224,9 +285,9 @@ static bool needs_typeclass(upb_fieldtype_t type) {
224
285
  * references to underlying objects will be shared if the value type is a
225
286
  * message type.
226
287
  */
227
- VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
288
+ static VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
228
289
  Map* self = ruby_to_Map(_self);
229
- int init_value_arg;
290
+ VALUE init_arg;
230
291
 
231
292
  // We take either two args (:key_type, :value_type), three args (:key_type,
232
293
  // :value_type, "ValueMessageType"), or four args (the above plus an initial
@@ -236,8 +297,9 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
236
297
  }
237
298
 
238
299
  self->key_type = ruby_to_fieldtype(argv[0]);
239
- self->value_type = ruby_to_fieldtype(argv[1]);
240
- self->parse_frame = Qnil;
300
+ self->value_type_info =
301
+ TypeInfo_FromClass(argc, argv, 1, &self->value_type_class, &init_arg);
302
+ self->arena = Arena_new();
241
303
 
242
304
  // Check that the key type is an allowed type.
243
305
  switch (self->key_type) {
@@ -254,21 +316,12 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
254
316
  rb_raise(rb_eArgError, "Invalid key type for map.");
255
317
  }
256
318
 
257
- init_value_arg = 2;
258
- if (needs_typeclass(self->value_type) && argc > 2) {
259
- self->value_type_class = argv[2];
260
- validate_type_class(self->value_type, self->value_type_class);
261
- init_value_arg = 3;
262
- }
263
-
264
- // Table value type is always UINT64: this ensures enough space to store the
265
- // native_slot value.
266
- if (!upb_strtable_init(&self->table, UPB_CTYPE_UINT64)) {
267
- rb_raise(rb_eRuntimeError, "Could not allocate table.");
268
- }
319
+ self->map = upb_map_new(Arena_get(self->arena), self->key_type,
320
+ self->value_type_info.type);
321
+ ObjectCache_Add(self->map, _self, Arena_get(self->arena));
269
322
 
270
- if (argc > init_value_arg) {
271
- Map_merge_into_self(_self, argv[init_value_arg]);
323
+ if (init_arg != Qnil) {
324
+ Map_merge_into_self(_self, init_arg);
272
325
  }
273
326
 
274
327
  return Qnil;
@@ -282,22 +335,16 @@ VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
282
335
  * Note that Map also includes Enumerable; map thus acts like a normal Ruby
283
336
  * sequence.
284
337
  */
285
- VALUE Map_each(VALUE _self) {
338
+ static VALUE Map_each(VALUE _self) {
286
339
  Map* self = ruby_to_Map(_self);
287
-
288
- upb_strtable_iter it;
289
- for (upb_strtable_begin(&it, &self->table);
290
- !upb_strtable_done(&it);
291
- upb_strtable_next(&it)) {
292
- VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it));
293
-
294
- upb_value v = upb_strtable_iter_value(&it);
295
- void* mem = value_memory(&v);
296
- VALUE value = native_slot_get(self->value_type,
297
- self->value_type_class,
298
- mem);
299
-
300
- rb_yield_values(2, key, value);
340
+ size_t iter = UPB_MAP_BEGIN;
341
+
342
+ while (upb_mapiter_next(self->map, &iter)) {
343
+ upb_msgval key = upb_mapiter_key(self->map, iter);
344
+ upb_msgval val = upb_mapiter_value(self->map, iter);
345
+ VALUE key_val = Convert_UpbToRuby(key, Map_keyinfo(self), self->arena);
346
+ VALUE val_val = Convert_UpbToRuby(val, self->value_type_info, self->arena);
347
+ rb_yield_values(2, key_val, val_val);
301
348
  }
302
349
 
303
350
  return Qnil;
@@ -309,17 +356,15 @@ VALUE Map_each(VALUE _self) {
309
356
  *
310
357
  * Returns the list of keys contained in the map, in unspecified order.
311
358
  */
312
- VALUE Map_keys(VALUE _self) {
359
+ static VALUE Map_keys(VALUE _self) {
313
360
  Map* self = ruby_to_Map(_self);
314
-
361
+ size_t iter = UPB_MAP_BEGIN;
315
362
  VALUE ret = rb_ary_new();
316
- upb_strtable_iter it;
317
- for (upb_strtable_begin(&it, &self->table);
318
- !upb_strtable_done(&it);
319
- upb_strtable_next(&it)) {
320
- VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it));
321
363
 
322
- rb_ary_push(ret, key);
364
+ while (upb_mapiter_next(self->map, &iter)) {
365
+ upb_msgval key = upb_mapiter_key(self->map, iter);
366
+ VALUE key_val = Convert_UpbToRuby(key, Map_keyinfo(self), self->arena);
367
+ rb_ary_push(ret, key_val);
323
368
  }
324
369
 
325
370
  return ret;
@@ -331,22 +376,15 @@ VALUE Map_keys(VALUE _self) {
331
376
  *
332
377
  * Returns the list of values contained in the map, in unspecified order.
333
378
  */
334
- VALUE Map_values(VALUE _self) {
379
+ static VALUE Map_values(VALUE _self) {
335
380
  Map* self = ruby_to_Map(_self);
336
-
381
+ size_t iter = UPB_MAP_BEGIN;
337
382
  VALUE ret = rb_ary_new();
338
- upb_strtable_iter it;
339
- for (upb_strtable_begin(&it, &self->table);
340
- !upb_strtable_done(&it);
341
- upb_strtable_next(&it)) {
342
-
343
- upb_value v = upb_strtable_iter_value(&it);
344
- void* mem = value_memory(&v);
345
- VALUE value = native_slot_get(self->value_type,
346
- self->value_type_class,
347
- mem);
348
-
349
- rb_ary_push(ret, value);
383
+
384
+ while (upb_mapiter_next(self->map, &iter)) {
385
+ upb_msgval val = upb_mapiter_value(self->map, iter);
386
+ VALUE val_val = Convert_UpbToRuby(val, self->value_type_info, self->arena);
387
+ rb_ary_push(ret, val_val);
350
388
  }
351
389
 
352
390
  return ret;
@@ -359,18 +397,13 @@ VALUE Map_values(VALUE _self) {
359
397
  * Accesses the element at the given key. Throws an exception if the key type is
360
398
  * incorrect. Returns nil when the key is not present in the map.
361
399
  */
362
- VALUE Map_index(VALUE _self, VALUE key) {
400
+ static VALUE Map_index(VALUE _self, VALUE key) {
363
401
  Map* self = ruby_to_Map(_self);
402
+ upb_msgval key_upb = Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
403
+ upb_msgval val;
364
404
 
365
- char keybuf[TABLE_KEY_BUF_LENGTH];
366
- const char* keyval = NULL;
367
- size_t length = 0;
368
- upb_value v;
369
- key = table_key(self, key, keybuf, &keyval, &length);
370
-
371
- if (upb_strtable_lookup2(&self->table, keyval, length, &v)) {
372
- void* mem = value_memory(&v);
373
- return native_slot_get(self->value_type, self->value_type_class, mem);
405
+ if (upb_map_get(self->map, key_upb, &val)) {
406
+ return Convert_UpbToRuby(val, self->value_type_info, self->arena);
374
407
  } else {
375
408
  return Qnil;
376
409
  }
@@ -384,33 +417,15 @@ VALUE Map_index(VALUE _self, VALUE key) {
384
417
  * Throws an exception if the key type is incorrect. Returns the new value that
385
418
  * was just inserted.
386
419
  */
387
- VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
420
+ static VALUE Map_index_set(VALUE _self, VALUE key, VALUE val) {
388
421
  Map* self = ruby_to_Map(_self);
389
- char keybuf[TABLE_KEY_BUF_LENGTH];
390
- const char* keyval = NULL;
391
- size_t length = 0;
392
- upb_value v;
393
- void* mem;
394
- key = table_key(self, key, keybuf, &keyval, &length);
395
-
396
- rb_check_frozen(_self);
422
+ upb_arena *arena = Arena_get(self->arena);
423
+ upb_msgval key_upb = Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
424
+ upb_msgval val_upb = Convert_RubyToUpb(val, "", self->value_type_info, arena);
397
425
 
398
- if (TYPE(value) == T_HASH) {
399
- VALUE args[1] = { value };
400
- value = rb_class_new_instance(1, args, self->value_type_class);
401
- }
402
-
403
- mem = value_memory(&v);
404
- native_slot_set("", self->value_type, self->value_type_class, mem, value);
405
-
406
- // Replace any existing value by issuing a 'remove' operation first.
407
- upb_strtable_remove2(&self->table, keyval, length, NULL);
408
- if (!upb_strtable_insert2(&self->table, keyval, length, v)) {
409
- rb_raise(rb_eRuntimeError, "Could not insert into table");
410
- }
426
+ upb_map_set(Map_GetMutable(_self), key_upb, val_upb, arena);
411
427
 
412
- // Ruby hashmap's :[]= method also returns the inserted value.
413
- return value;
428
+ return val;
414
429
  }
415
430
 
416
431
  /*
@@ -420,15 +435,11 @@ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
420
435
  * Returns true if the given key is present in the map. Throws an exception if
421
436
  * the key has the wrong type.
422
437
  */
423
- VALUE Map_has_key(VALUE _self, VALUE key) {
438
+ static VALUE Map_has_key(VALUE _self, VALUE key) {
424
439
  Map* self = ruby_to_Map(_self);
440
+ upb_msgval key_upb = Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
425
441
 
426
- char keybuf[TABLE_KEY_BUF_LENGTH];
427
- const char* keyval = NULL;
428
- size_t length = 0;
429
- key = table_key(self, key, keybuf, &keyval, &length);
430
-
431
- if (upb_strtable_lookup2(&self->table, keyval, length, NULL)) {
442
+ if (upb_map_get(self->map, key_upb, NULL)) {
432
443
  return Qtrue;
433
444
  } else {
434
445
  return Qfalse;
@@ -442,22 +453,25 @@ VALUE Map_has_key(VALUE _self, VALUE key) {
442
453
  * Deletes the value at the given key, if any, returning either the old value or
443
454
  * nil if none was present. Throws an exception if the key is of the wrong type.
444
455
  */
445
- VALUE Map_delete(VALUE _self, VALUE key) {
456
+ static VALUE Map_delete(VALUE _self, VALUE key) {
446
457
  Map* self = ruby_to_Map(_self);
447
- char keybuf[TABLE_KEY_BUF_LENGTH];
448
- const char* keyval = NULL;
449
- size_t length = 0;
450
- upb_value v;
451
- key = table_key(self, key, keybuf, &keyval, &length);
458
+ upb_msgval key_upb = Convert_RubyToUpb(key, "", Map_keyinfo(self), NULL);
459
+ upb_msgval val_upb;
460
+ VALUE ret;
452
461
 
453
462
  rb_check_frozen(_self);
454
463
 
455
- if (upb_strtable_remove2(&self->table, keyval, length, &v)) {
456
- void* mem = value_memory(&v);
457
- return native_slot_get(self->value_type, self->value_type_class, mem);
464
+ // TODO(haberman): make upb_map_delete() also capable of returning the deleted
465
+ // value.
466
+ if (upb_map_get(self->map, key_upb, &val_upb)) {
467
+ ret = Convert_UpbToRuby(val_upb, self->value_type_info, self->arena);
458
468
  } else {
459
- return Qnil;
469
+ ret = Qnil;
460
470
  }
471
+
472
+ upb_map_delete(Map_GetMutable(_self), key_upb);
473
+
474
+ return ret;
461
475
  }
462
476
 
463
477
  /*
@@ -466,17 +480,8 @@ VALUE Map_delete(VALUE _self, VALUE key) {
466
480
  *
467
481
  * Removes all entries from the map.
468
482
  */
469
- VALUE Map_clear(VALUE _self) {
470
- Map* self = ruby_to_Map(_self);
471
-
472
- rb_check_frozen(_self);
473
-
474
- // Uninit and reinit the table -- this is faster than iterating and doing a
475
- // delete-lookup on each key.
476
- upb_strtable_uninit(&self->table);
477
- if (!upb_strtable_init(&self->table, UPB_CTYPE_INT64)) {
478
- rb_raise(rb_eRuntimeError, "Unable to re-initialize table");
479
- }
483
+ static VALUE Map_clear(VALUE _self) {
484
+ upb_map_clear(Map_GetMutable(_self));
480
485
  return Qnil;
481
486
  }
482
487
 
@@ -486,24 +491,9 @@ VALUE Map_clear(VALUE _self) {
486
491
  *
487
492
  * Returns the number of entries (key-value pairs) in the map.
488
493
  */
489
- VALUE Map_length(VALUE _self) {
494
+ static VALUE Map_length(VALUE _self) {
490
495
  Map* self = ruby_to_Map(_self);
491
- return ULL2NUM(upb_strtable_count(&self->table));
492
- }
493
-
494
- VALUE Map_new_this_type(VALUE _self) {
495
- Map* self = ruby_to_Map(_self);
496
- VALUE new_map = Qnil;
497
- VALUE key_type = fieldtype_to_ruby(self->key_type);
498
- VALUE value_type = fieldtype_to_ruby(self->value_type);
499
- if (self->value_type_class != Qnil) {
500
- new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 3,
501
- key_type, value_type, self->value_type_class);
502
- } else {
503
- new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2,
504
- key_type, value_type);
505
- }
506
- return new_map;
496
+ return ULL2NUM(upb_map_size(self->map));
507
497
  }
508
498
 
509
499
  /*
@@ -513,54 +503,23 @@ VALUE Map_new_this_type(VALUE _self) {
513
503
  * Duplicates this map with a shallow copy. References to all non-primitive
514
504
  * element objects (e.g., submessages) are shared.
515
505
  */
516
- VALUE Map_dup(VALUE _self) {
517
- Map* self = ruby_to_Map(_self);
518
- VALUE new_map = Map_new_this_type(_self);
519
- Map* new_self = ruby_to_Map(new_map);
520
-
521
- upb_strtable_iter it;
522
- for (upb_strtable_begin(&it, &self->table);
523
- !upb_strtable_done(&it);
524
- upb_strtable_next(&it)) {
525
- upb_strview k = upb_strtable_iter_key(&it);
526
- upb_value v = upb_strtable_iter_value(&it);
527
- void* mem = value_memory(&v);
528
- upb_value dup;
529
- void* dup_mem = value_memory(&dup);
530
- native_slot_dup(self->value_type, dup_mem, mem);
531
-
532
- if (!upb_strtable_insert2(&new_self->table, k.data, k.size, dup)) {
533
- rb_raise(rb_eRuntimeError, "Error inserting value into new table");
534
- }
535
- }
536
-
537
- return new_map;
538
- }
539
-
540
- // Used by Google::Protobuf.deep_copy but not exposed directly.
541
- VALUE Map_deep_copy(VALUE _self) {
506
+ static VALUE Map_dup(VALUE _self) {
542
507
  Map* self = ruby_to_Map(_self);
543
- VALUE new_map = Map_new_this_type(_self);
544
- Map* new_self = ruby_to_Map(new_map);
545
-
546
- upb_strtable_iter it;
547
- for (upb_strtable_begin(&it, &self->table);
548
- !upb_strtable_done(&it);
549
- upb_strtable_next(&it)) {
550
- upb_strview k = upb_strtable_iter_key(&it);
551
- upb_value v = upb_strtable_iter_value(&it);
552
- void* mem = value_memory(&v);
553
- upb_value dup;
554
- void* dup_mem = value_memory(&dup);
555
- native_slot_deep_copy(self->value_type, self->value_type_class, dup_mem,
556
- mem);
557
-
558
- if (!upb_strtable_insert2(&new_self->table, k.data, k.size, dup)) {
559
- rb_raise(rb_eRuntimeError, "Error inserting value into new table");
560
- }
508
+ VALUE new_map_rb = Map_new_this_type(self);
509
+ Map* new_self = ruby_to_Map(new_map_rb);
510
+ size_t iter = UPB_MAP_BEGIN;
511
+ upb_arena *arena = Arena_get(new_self->arena);
512
+ upb_map *new_map = Map_GetMutable(new_map_rb);
513
+
514
+ upb_arena_fuse(arena, Arena_get(self->arena));
515
+
516
+ while (upb_mapiter_next(self->map, &iter)) {
517
+ upb_msgval key = upb_mapiter_key(self->map, iter);
518
+ upb_msgval val = upb_mapiter_value(self->map, iter);
519
+ upb_map_set(new_map, key, val, arena);
561
520
  }
562
521
 
563
- return new_map;
522
+ return new_map_rb;
564
523
  }
565
524
 
566
525
  /*
@@ -579,12 +538,11 @@ VALUE Map_deep_copy(VALUE _self) {
579
538
  VALUE Map_eq(VALUE _self, VALUE _other) {
580
539
  Map* self = ruby_to_Map(_self);
581
540
  Map* other;
582
- upb_strtable_iter it;
583
541
 
584
542
  // Allow comparisons to Ruby hashmaps by converting to a temporary Map
585
543
  // instance. Slow, but workable.
586
544
  if (TYPE(_other) == T_HASH) {
587
- VALUE other_map = Map_new_this_type(_self);
545
+ VALUE other_map = Map_new_this_type(self);
588
546
  Map_merge_into_self(other_map, _other);
589
547
  _other = other_map;
590
548
  }
@@ -595,33 +553,27 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
595
553
  return Qtrue;
596
554
  }
597
555
  if (self->key_type != other->key_type ||
598
- self->value_type != other->value_type ||
556
+ self->value_type_info.type != other->value_type_info.type ||
599
557
  self->value_type_class != other->value_type_class) {
600
558
  return Qfalse;
601
559
  }
602
- if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) {
560
+ if (upb_map_size(self->map) != upb_map_size(other->map)) {
603
561
  return Qfalse;
604
562
  }
605
563
 
606
564
  // For each member of self, check that an equal member exists at the same key
607
565
  // in other.
608
- for (upb_strtable_begin(&it, &self->table);
609
- !upb_strtable_done(&it);
610
- upb_strtable_next(&it)) {
611
- upb_strview k = upb_strtable_iter_key(&it);
612
- upb_value v = upb_strtable_iter_value(&it);
613
- void* mem = value_memory(&v);
614
- upb_value other_v;
615
- void* other_mem = value_memory(&other_v);
616
-
617
- if (!upb_strtable_lookup2(&other->table, k.data, k.size, &other_v)) {
566
+ size_t iter = UPB_MAP_BEGIN;
567
+ while (upb_mapiter_next(self->map, &iter)) {
568
+ upb_msgval key = upb_mapiter_key(self->map, iter);
569
+ upb_msgval val = upb_mapiter_value(self->map, iter);
570
+ upb_msgval other_val;
571
+ if (!upb_map_get(other->map, key, &other_val)) {
618
572
  // Not present in other map.
619
573
  return Qfalse;
620
574
  }
621
-
622
- if (!native_slot_eq(self->value_type, self->value_type_class, mem,
623
- other_mem)) {
624
- // Present, but value not equal.
575
+ if (!Msgval_IsEqual(val, other_val, self->value_type_info)) {
576
+ // Present but different value.
625
577
  return Qfalse;
626
578
  }
627
579
  }
@@ -629,6 +581,21 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
629
581
  return Qtrue;
630
582
  }
631
583
 
584
+ /*
585
+ * call-seq:
586
+ * Message.freeze => self
587
+ *
588
+ * Freezes the message object. We have to intercept this so we can pin the
589
+ * Ruby object into memory so we don't forget it's frozen.
590
+ */
591
+ static VALUE Map_freeze(VALUE _self) {
592
+ Map* self = ruby_to_Map(_self);
593
+
594
+ ObjectCache_Pin(self->map, _self, Arena_get(self->arena));
595
+ RB_OBJ_FREEZE(_self);
596
+ return _self;
597
+ }
598
+
632
599
  /*
633
600
  * call-seq:
634
601
  * Map.hash => hash_value
@@ -637,26 +604,18 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
637
604
  */
638
605
  VALUE Map_hash(VALUE _self) {
639
606
  Map* self = ruby_to_Map(_self);
640
-
641
- st_index_t h = rb_hash_start(0);
642
- VALUE hash_sym = rb_intern("hash");
643
-
644
- upb_strtable_iter it;
645
- for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it);
646
- upb_strtable_next(&it)) {
647
- VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it));
648
-
649
- upb_value v = upb_strtable_iter_value(&it);
650
- void* mem = value_memory(&v);
651
- VALUE value = native_slot_get(self->value_type,
652
- self->value_type_class,
653
- mem);
654
-
655
- h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0)));
656
- h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0)));
607
+ uint64_t hash = 0;
608
+
609
+ size_t iter = UPB_MAP_BEGIN;
610
+ TypeInfo key_info = {self->key_type};
611
+ while (upb_mapiter_next(self->map, &iter)) {
612
+ upb_msgval key = upb_mapiter_key(self->map, iter);
613
+ upb_msgval val = upb_mapiter_value(self->map, iter);
614
+ hash = Msgval_GetHash(key, key_info, hash);
615
+ hash = Msgval_GetHash(val, self->value_type_info, hash);
657
616
  }
658
617
 
659
- return INT2FIX(h);
618
+ return LL2NUM(hash);
660
619
  }
661
620
 
662
621
  /*
@@ -667,24 +626,7 @@ VALUE Map_hash(VALUE _self) {
667
626
  */
668
627
  VALUE Map_to_h(VALUE _self) {
669
628
  Map* self = ruby_to_Map(_self);
670
- VALUE hash = rb_hash_new();
671
- upb_strtable_iter it;
672
- for (upb_strtable_begin(&it, &self->table);
673
- !upb_strtable_done(&it);
674
- upb_strtable_next(&it)) {
675
- VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it));
676
- upb_value v = upb_strtable_iter_value(&it);
677
- void* mem = value_memory(&v);
678
- VALUE value = native_slot_get(self->value_type,
679
- self->value_type_class,
680
- mem);
681
-
682
- if (self->value_type == UPB_TYPE_MESSAGE) {
683
- value = Message_to_h(value);
684
- }
685
- rb_hash_aset(hash, key, value);
686
- }
687
- return hash;
629
+ return Map_CreateHash(self->map, self->key_type, self->value_type_info);
688
630
  }
689
631
 
690
632
  /*
@@ -698,34 +640,11 @@ VALUE Map_to_h(VALUE _self) {
698
640
  VALUE Map_inspect(VALUE _self) {
699
641
  Map* self = ruby_to_Map(_self);
700
642
 
701
- VALUE str = rb_str_new2("{");
702
-
703
- bool first = true;
704
- VALUE inspect_sym = rb_intern("inspect");
705
-
706
- upb_strtable_iter it;
707
- for (upb_strtable_begin(&it, &self->table); !upb_strtable_done(&it);
708
- upb_strtable_next(&it)) {
709
- VALUE key = table_key_to_ruby(self, upb_strtable_iter_key(&it));
710
-
711
- upb_value v = upb_strtable_iter_value(&it);
712
- void* mem = value_memory(&v);
713
- VALUE value = native_slot_get(self->value_type,
714
- self->value_type_class,
715
- mem);
716
-
717
- if (!first) {
718
- str = rb_str_cat2(str, ", ");
719
- } else {
720
- first = false;
721
- }
722
- str = rb_str_append(str, rb_funcall(key, inspect_sym, 0));
723
- str = rb_str_cat2(str, "=>");
724
- str = rb_str_append(str, rb_funcall(value, inspect_sym, 0));
725
- }
726
-
727
- str = rb_str_cat2(str, "}");
728
- return str;
643
+ StringBuilder* builder = StringBuilder_New();
644
+ Map_Inspect(builder, self->map, self->key_type, self->value_type_info);
645
+ VALUE ret = StringBuilder_ToRubyString(builder);
646
+ StringBuilder_Free(builder);
647
+ return ret;
729
648
  }
730
649
 
731
650
  /*
@@ -737,79 +656,11 @@ VALUE Map_inspect(VALUE _self) {
737
656
  * in the new copy of this map. Returns the new copy of this map with merged
738
657
  * contents.
739
658
  */
740
- VALUE Map_merge(VALUE _self, VALUE hashmap) {
659
+ static VALUE Map_merge(VALUE _self, VALUE hashmap) {
741
660
  VALUE dupped = Map_dup(_self);
742
661
  return Map_merge_into_self(dupped, hashmap);
743
662
  }
744
663
 
745
- static int merge_into_self_callback(VALUE key, VALUE value, VALUE self) {
746
- Map_index_set(self, key, value);
747
- return ST_CONTINUE;
748
- }
749
-
750
- // Used only internally -- shared by #merge and #initialize.
751
- VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
752
- if (TYPE(hashmap) == T_HASH) {
753
- rb_hash_foreach(hashmap, merge_into_self_callback, _self);
754
- } else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) &&
755
- RTYPEDDATA_TYPE(hashmap) == &Map_type) {
756
-
757
- Map* self = ruby_to_Map(_self);
758
- Map* other = ruby_to_Map(hashmap);
759
- upb_strtable_iter it;
760
-
761
- if (self->key_type != other->key_type ||
762
- self->value_type != other->value_type ||
763
- self->value_type_class != other->value_type_class) {
764
- rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types");
765
- }
766
-
767
- for (upb_strtable_begin(&it, &other->table);
768
- !upb_strtable_done(&it);
769
- upb_strtable_next(&it)) {
770
- upb_strview k = upb_strtable_iter_key(&it);
771
-
772
- // Replace any existing value by issuing a 'remove' operation first.
773
- upb_value v;
774
- upb_value oldv;
775
- upb_strtable_remove2(&self->table, k.data, k.size, &oldv);
776
-
777
- v = upb_strtable_iter_value(&it);
778
- upb_strtable_insert2(&self->table, k.data, k.size, v);
779
- }
780
- } else {
781
- rb_raise(rb_eArgError, "Unknown type merging into Map");
782
- }
783
- return _self;
784
- }
785
-
786
- // Internal method: map iterator initialization (used for serialization).
787
- void Map_begin(VALUE _self, Map_iter* iter) {
788
- Map* self = ruby_to_Map(_self);
789
- iter->self = self;
790
- upb_strtable_begin(&iter->it, &self->table);
791
- }
792
-
793
- void Map_next(Map_iter* iter) {
794
- upb_strtable_next(&iter->it);
795
- }
796
-
797
- bool Map_done(Map_iter* iter) {
798
- return upb_strtable_done(&iter->it);
799
- }
800
-
801
- VALUE Map_iter_key(Map_iter* iter) {
802
- return table_key_to_ruby(iter->self, upb_strtable_iter_key(&iter->it));
803
- }
804
-
805
- VALUE Map_iter_value(Map_iter* iter) {
806
- upb_value v = upb_strtable_iter_value(&iter->it);
807
- void* mem = value_memory(&v);
808
- return native_slot_get(iter->self->value_type,
809
- iter->self->value_type_class,
810
- mem);
811
- }
812
-
813
664
  void Map_register(VALUE module) {
814
665
  VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
815
666
  rb_define_alloc_func(klass, Map_alloc);
@@ -828,6 +679,7 @@ void Map_register(VALUE module) {
828
679
  rb_define_method(klass, "length", Map_length, 0);
829
680
  rb_define_method(klass, "dup", Map_dup, 0);
830
681
  rb_define_method(klass, "==", Map_eq, 1);
682
+ rb_define_method(klass, "freeze", Map_freeze, 0);
831
683
  rb_define_method(klass, "hash", Map_hash, 0);
832
684
  rb_define_method(klass, "to_h", Map_to_h, 0);
833
685
  rb_define_method(klass, "inspect", Map_inspect, 0);