google-protobuf 3.7.0 → 3.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -60,47 +60,30 @@ rb_data_type_t Message_type = {
60
60
  VALUE Message_alloc(VALUE klass) {
61
61
  VALUE descriptor = rb_ivar_get(klass, descriptor_instancevar_interned);
62
62
  Descriptor* desc = ruby_to_Descriptor(descriptor);
63
- MessageHeader* msg = (MessageHeader*)ALLOC_N(
64
- uint8_t, sizeof(MessageHeader) + desc->layout->size);
63
+ MessageHeader* msg;
65
64
  VALUE ret;
66
65
 
67
- memset(Message_data(msg), 0, desc->layout->size);
66
+ if (desc->layout == NULL) {
67
+ create_layout(desc);
68
+ }
68
69
 
69
- // We wrap first so that everything in the message object is GC-rooted in case
70
- // a collection happens during object creation in layout_init().
71
- ret = TypedData_Wrap_Struct(klass, &Message_type, msg);
70
+ msg = (void*)ALLOC_N(uint8_t, sizeof(MessageHeader) + desc->layout->size);
72
71
  msg->descriptor = desc;
73
- rb_ivar_set(ret, descriptor_instancevar_interned, descriptor);
74
-
75
72
  msg->unknown_fields = NULL;
73
+ memcpy(Message_data(msg), desc->layout->empty_template, desc->layout->size);
76
74
 
77
- layout_init(desc->layout, Message_data(msg));
75
+ ret = TypedData_Wrap_Struct(klass, &Message_type, msg);
76
+ rb_ivar_set(ret, descriptor_instancevar_interned, descriptor);
78
77
 
79
78
  return ret;
80
79
  }
81
80
 
82
81
  static const upb_fielddef* which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
83
- upb_oneof_iter it;
84
- size_t case_ofs;
85
82
  uint32_t oneof_case;
86
- const upb_fielddef* first_field;
87
83
  const upb_fielddef* f;
88
84
 
89
- // If no fields in the oneof, always nil.
90
- if (upb_oneofdef_numfields(o) == 0) {
91
- return NULL;
92
- }
93
- // Grab the first field in the oneof so we can get its layout info to find the
94
- // oneof_case field.
95
- upb_oneof_begin(&it, o);
96
- assert(!upb_oneof_done(&it));
97
- first_field = upb_oneof_iter_field(&it);
98
- assert(upb_fielddef_containingoneof(first_field) != NULL);
99
-
100
- case_ofs =
101
- self->descriptor->layout->
102
- fields[upb_fielddef_index(first_field)].case_offset;
103
- oneof_case = *((uint32_t*)((char*)Message_data(self) + case_ofs));
85
+ oneof_case =
86
+ slot_read_oneof_case(self->descriptor->layout, Message_data(self), o);
104
87
 
105
88
  if (oneof_case == ONEOF_CASE_NONE) {
106
89
  return NULL;
@@ -118,19 +101,63 @@ enum {
118
101
  METHOD_GETTER = 1,
119
102
  METHOD_SETTER = 2,
120
103
  METHOD_CLEAR = 3,
121
- METHOD_PRESENCE = 4
104
+ METHOD_PRESENCE = 4,
105
+ METHOD_ENUM_GETTER = 5,
106
+ METHOD_WRAPPER_GETTER = 6,
107
+ METHOD_WRAPPER_SETTER = 7
122
108
  };
123
109
 
124
- static int extract_method_call(VALUE method_name, MessageHeader* self,
125
- const upb_fielddef **f, const upb_oneofdef **o) {
126
- Check_Type(method_name, T_SYMBOL);
110
+ // Check if the field is a well known wrapper type
111
+ bool is_wrapper_type_field(const upb_fielddef* field) {
112
+ const upb_msgdef *m;
113
+ if (upb_fielddef_type(field) != UPB_TYPE_MESSAGE) {
114
+ return false;
115
+ }
116
+ m = upb_fielddef_msgsubdef(field);
117
+ switch (upb_msgdef_wellknowntype(m)) {
118
+ case UPB_WELLKNOWN_DOUBLEVALUE:
119
+ case UPB_WELLKNOWN_FLOATVALUE:
120
+ case UPB_WELLKNOWN_INT64VALUE:
121
+ case UPB_WELLKNOWN_UINT64VALUE:
122
+ case UPB_WELLKNOWN_INT32VALUE:
123
+ case UPB_WELLKNOWN_UINT32VALUE:
124
+ case UPB_WELLKNOWN_STRINGVALUE:
125
+ case UPB_WELLKNOWN_BYTESVALUE:
126
+ case UPB_WELLKNOWN_BOOLVALUE:
127
+ return true;
128
+ default:
129
+ return false;
130
+ }
131
+ }
127
132
 
128
- VALUE method_str = rb_id2str(SYM2ID(method_name));
129
- char* name = RSTRING_PTR(method_str);
130
- size_t name_len = RSTRING_LEN(method_str);
133
+ // Get a new Ruby wrapper type and set the initial value
134
+ VALUE ruby_wrapper_type(VALUE type_class, VALUE value) {
135
+ if (value != Qnil) {
136
+ VALUE hash = rb_hash_new();
137
+ rb_hash_aset(hash, rb_str_new2("value"), value);
138
+ {
139
+ VALUE args[1] = {hash};
140
+ return rb_class_new_instance(1, args, type_class);
141
+ }
142
+ }
143
+ return Qnil;
144
+ }
145
+
146
+ static int extract_method_call(VALUE method_name, MessageHeader* self,
147
+ const upb_fielddef **f, const upb_oneofdef **o) {
148
+ VALUE method_str;
149
+ char* name;
150
+ size_t name_len;
131
151
  int accessor_type;
132
152
  const upb_oneofdef* test_o;
133
153
  const upb_fielddef* test_f;
154
+ bool has_field;
155
+
156
+ Check_Type(method_name, T_SYMBOL);
157
+
158
+ method_str = rb_id2str(SYM2ID(method_name));
159
+ name = RSTRING_PTR(method_str);
160
+ name_len = RSTRING_LEN(method_str);
134
161
 
135
162
  if (name[name_len - 1] == '=') {
136
163
  accessor_type = METHOD_SETTER;
@@ -139,13 +166,13 @@ static int extract_method_call(VALUE method_name, MessageHeader* self,
139
166
  // we don't strip the prefix.
140
167
  } else if (strncmp("clear_", name, 6) == 0 &&
141
168
  !upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
142
- &test_f, &test_o)) {
169
+ &test_f, &test_o)) {
143
170
  accessor_type = METHOD_CLEAR;
144
171
  name = name + 6;
145
172
  name_len = name_len - 6;
146
173
  } else if (strncmp("has_", name, 4) == 0 && name[name_len - 1] == '?' &&
147
174
  !upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
148
- &test_f, &test_o)) {
175
+ &test_f, &test_o)) {
149
176
  accessor_type = METHOD_PRESENCE;
150
177
  name = name + 4;
151
178
  name_len = name_len - 5;
@@ -153,18 +180,76 @@ static int extract_method_call(VALUE method_name, MessageHeader* self,
153
180
  accessor_type = METHOD_GETTER;
154
181
  }
155
182
 
183
+ has_field = upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
184
+ &test_f, &test_o);
185
+
186
+ // Look for wrapper type accessor of the form <field_name>_as_value
187
+ if (!has_field &&
188
+ (accessor_type == METHOD_GETTER || accessor_type == METHOD_SETTER) &&
189
+ name_len > 9 && strncmp(name + name_len - 9, "_as_value", 9) == 0) {
190
+ const upb_oneofdef* test_o_wrapper;
191
+ const upb_fielddef* test_f_wrapper;
192
+ char wrapper_field_name[name_len - 8];
193
+
194
+ // Find the field name
195
+ strncpy(wrapper_field_name, name, name_len - 9);
196
+ wrapper_field_name[name_len - 9] = '\0';
197
+
198
+ // Check if field exists and is a wrapper type
199
+ if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name,
200
+ name_len - 9, &test_f_wrapper, &test_o_wrapper) &&
201
+ is_wrapper_type_field(test_f_wrapper)) {
202
+ // It does exist!
203
+ has_field = true;
204
+ if (accessor_type == METHOD_SETTER) {
205
+ accessor_type = METHOD_WRAPPER_SETTER;
206
+ } else {
207
+ accessor_type = METHOD_WRAPPER_GETTER;
208
+ }
209
+ test_o = test_o_wrapper;
210
+ test_f = test_f_wrapper;
211
+ }
212
+ }
213
+
214
+ // Look for enum accessor of the form <enum_name>_const
215
+ if (!has_field && accessor_type == METHOD_GETTER &&
216
+ name_len > 6 && strncmp(name + name_len - 6, "_const", 6) == 0) {
217
+ const upb_oneofdef* test_o_enum;
218
+ const upb_fielddef* test_f_enum;
219
+ char enum_name[name_len - 5];
220
+
221
+ // Find enum field name
222
+ strncpy(enum_name, name, name_len - 6);
223
+ enum_name[name_len - 6] = '\0';
224
+
225
+ // Check if enum field exists
226
+ if (upb_msgdef_lookupname(self->descriptor->msgdef, enum_name, name_len - 6,
227
+ &test_f_enum, &test_o_enum) &&
228
+ upb_fielddef_type(test_f_enum) == UPB_TYPE_ENUM) {
229
+ // It does exist!
230
+ has_field = true;
231
+ accessor_type = METHOD_ENUM_GETTER;
232
+ test_o = test_o_enum;
233
+ test_f = test_f_enum;
234
+ }
235
+ }
236
+
156
237
  // Verify the name corresponds to a oneof or field in this message.
157
- if (!upb_msgdef_lookupname(self->descriptor->msgdef, name, name_len,
158
- &test_f, &test_o)) {
238
+ if (!has_field) {
159
239
  return METHOD_UNKNOWN;
160
240
  }
161
241
 
162
242
  // Method calls like 'has_foo?' are not allowed if field "foo" does not have
163
243
  // a hasbit (e.g. repeated fields or non-message type fields for proto3
164
244
  // syntax).
165
- if (accessor_type == METHOD_PRESENCE && test_f != NULL &&
166
- !upb_fielddef_haspresence(test_f)) {
167
- return METHOD_UNKNOWN;
245
+ if (accessor_type == METHOD_PRESENCE && test_f != NULL) {
246
+ if (!upb_fielddef_haspresence(test_f)) return METHOD_UNKNOWN;
247
+
248
+ // TODO(haberman): remove this case, allow for proto3 oneofs.
249
+ if (upb_fielddef_realcontainingoneof(test_f) &&
250
+ upb_filedef_syntax(upb_fielddef_file(test_f)) == UPB_SYNTAX_PROTO3) {
251
+ return METHOD_UNKNOWN;
252
+ }
168
253
  }
169
254
 
170
255
  *o = test_o;
@@ -203,41 +288,44 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
203
288
  MessageHeader* self;
204
289
  const upb_oneofdef* o;
205
290
  const upb_fielddef* f;
291
+ int accessor_type;
206
292
 
207
293
  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
208
294
  if (argc < 1) {
209
295
  rb_raise(rb_eArgError, "Expected method name as first argument.");
210
296
  }
211
297
 
212
- int accessor_type = extract_method_call(argv[0], self, &f, &o);
298
+ accessor_type = extract_method_call(argv[0], self, &f, &o);
213
299
  if (accessor_type == METHOD_UNKNOWN || (o == NULL && f == NULL) ) {
214
300
  return rb_call_super(argc, argv);
215
- } else if (accessor_type == METHOD_SETTER) {
301
+ } else if (accessor_type == METHOD_SETTER || accessor_type == METHOD_WRAPPER_SETTER) {
216
302
  if (argc != 2) {
217
303
  rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc);
218
304
  }
305
+ rb_check_frozen(_self);
219
306
  } else if (argc != 1) {
220
307
  rb_raise(rb_eArgError, "Expected 1 argument, received %d", argc);
221
308
  }
222
309
 
223
310
  // Return which of the oneof fields are set
224
311
  if (o != NULL) {
312
+ const upb_fielddef* oneof_field = which_oneof_field(self, o);
313
+
225
314
  if (accessor_type == METHOD_SETTER) {
226
315
  rb_raise(rb_eRuntimeError, "Oneof accessors are read-only.");
227
316
  }
228
317
 
229
- const upb_fielddef* oneof_field = which_oneof_field(self, o);
230
318
  if (accessor_type == METHOD_PRESENCE) {
231
319
  return oneof_field == NULL ? Qfalse : Qtrue;
232
320
  } else if (accessor_type == METHOD_CLEAR) {
233
321
  if (oneof_field != NULL) {
234
- layout_clear(self->descriptor->layout, Message_data(self), oneof_field);
322
+ layout_clear(self->descriptor->layout, Message_data(self), oneof_field);
235
323
  }
236
324
  return Qnil;
237
325
  } else {
238
326
  // METHOD_ACCESSOR
239
327
  return oneof_field == NULL ? Qnil :
240
- ID2SYM(rb_intern(upb_fielddef_name(oneof_field)));
328
+ ID2SYM(rb_intern(upb_fielddef_name(oneof_field)));
241
329
  }
242
330
  // Otherwise we're operating on a single proto field
243
331
  } else if (accessor_type == METHOD_SETTER) {
@@ -248,6 +336,41 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
248
336
  return Qnil;
249
337
  } else if (accessor_type == METHOD_PRESENCE) {
250
338
  return layout_has(self->descriptor->layout, Message_data(self), f);
339
+ } else if (accessor_type == METHOD_WRAPPER_GETTER) {
340
+ VALUE value = layout_get(self->descriptor->layout, Message_data(self), f);
341
+ switch (TYPE(value)) {
342
+ case T_DATA:
343
+ return rb_funcall(value, rb_intern("value"), 0);
344
+ case T_NIL:
345
+ return Qnil;
346
+ default:
347
+ return value;
348
+ }
349
+ } else if (accessor_type == METHOD_WRAPPER_SETTER) {
350
+ VALUE wrapper = ruby_wrapper_type(
351
+ field_type_class(self->descriptor->layout, f), argv[1]);
352
+ layout_set(self->descriptor->layout, Message_data(self), f, wrapper);
353
+ return Qnil;
354
+ } else if (accessor_type == METHOD_ENUM_GETTER) {
355
+ VALUE enum_type = field_type_class(self->descriptor->layout, f);
356
+ VALUE method = rb_intern("const_get");
357
+ VALUE raw_value = layout_get(self->descriptor->layout, Message_data(self), f);
358
+
359
+ // Map repeated fields to a new type with ints
360
+ if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
361
+ int array_size = FIX2INT(rb_funcall(raw_value, rb_intern("length"), 0));
362
+ int i;
363
+ VALUE array_args[1] = { ID2SYM(rb_intern("int64")) };
364
+ VALUE array = rb_class_new_instance(1, array_args, CLASS_OF(raw_value));
365
+ for (i = 0; i < array_size; i++) {
366
+ VALUE entry = rb_funcall(enum_type, method, 1, rb_funcall(raw_value,
367
+ rb_intern("at"), 1, INT2NUM(i)));
368
+ rb_funcall(array, rb_intern("push"), 1, entry);
369
+ }
370
+ return array;
371
+ }
372
+ // Convert the value for singular fields
373
+ return rb_funcall(enum_type, method, 1, raw_value);
251
374
  } else {
252
375
  return layout_get(self->descriptor->layout, Message_data(self), f);
253
376
  }
@@ -258,13 +381,14 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
258
381
  MessageHeader* self;
259
382
  const upb_oneofdef* o;
260
383
  const upb_fielddef* f;
384
+ int accessor_type;
261
385
 
262
386
  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
263
387
  if (argc < 1) {
264
388
  rb_raise(rb_eArgError, "Expected method name as first argument.");
265
389
  }
266
390
 
267
- int accessor_type = extract_method_call(argv[0], self, &f, &o);
391
+ accessor_type = extract_method_call(argv[0], self, &f, &o);
268
392
  if (accessor_type == METHOD_UNKNOWN) {
269
393
  return rb_call_super(argc, argv);
270
394
  } else if (o != NULL) {
@@ -274,15 +398,10 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
274
398
  }
275
399
  }
276
400
 
277
- VALUE create_submsg_from_hash(const upb_fielddef *f, VALUE hash) {
278
- const upb_def *d = upb_fielddef_subdef(f);
279
- assert(d != NULL);
280
-
281
- VALUE descriptor = get_def_obj(d);
282
- VALUE msgclass = rb_funcall(descriptor, rb_intern("msgclass"), 0, NULL);
283
-
401
+ VALUE create_submsg_from_hash(const MessageLayout* layout,
402
+ const upb_fielddef* f, VALUE hash) {
284
403
  VALUE args[1] = { hash };
285
- return rb_class_new_instance(1, args, msgclass);
404
+ return rb_class_new_instance(1, args, field_type_class(layout, f));
286
405
  }
287
406
 
288
407
  int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
@@ -315,29 +434,32 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
315
434
 
316
435
  if (TYPE(val) != T_HASH) {
317
436
  rb_raise(rb_eArgError,
318
- "Expected Hash object as initializer value for map field '%s'.", name);
437
+ "Expected Hash object as initializer value for map field '%s' (given %s).",
438
+ name, rb_class2name(CLASS_OF(val)));
319
439
  }
320
440
  map = layout_get(self->descriptor->layout, Message_data(self), f);
321
441
  Map_merge_into_self(map, val);
322
442
  } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
323
443
  VALUE ary;
444
+ int i;
324
445
 
325
446
  if (TYPE(val) != T_ARRAY) {
326
447
  rb_raise(rb_eArgError,
327
- "Expected array as initializer value for repeated field '%s'.", name);
448
+ "Expected array as initializer value for repeated field '%s' (given %s).",
449
+ name, rb_class2name(CLASS_OF(val)));
328
450
  }
329
451
  ary = layout_get(self->descriptor->layout, Message_data(self), f);
330
- for (int i = 0; i < RARRAY_LEN(val); i++) {
452
+ for (i = 0; i < RARRAY_LEN(val); i++) {
331
453
  VALUE entry = rb_ary_entry(val, i);
332
454
  if (TYPE(entry) == T_HASH && upb_fielddef_issubmsg(f)) {
333
- entry = create_submsg_from_hash(f, entry);
455
+ entry = create_submsg_from_hash(self->descriptor->layout, f, entry);
334
456
  }
335
457
 
336
458
  RepeatedField_push(ary, entry);
337
459
  }
338
460
  } else {
339
461
  if (TYPE(val) == T_HASH && upb_fielddef_issubmsg(f)) {
340
- val = create_submsg_from_hash(f, val);
462
+ val = create_submsg_from_hash(self->descriptor->layout, f, val);
341
463
  }
342
464
 
343
465
  layout_set(self->descriptor->layout, Message_data(self), f, val);
@@ -358,7 +480,11 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
358
480
  * Message class are provided on each concrete message class.
359
481
  */
360
482
  VALUE Message_initialize(int argc, VALUE* argv, VALUE _self) {
483
+ MessageHeader* self;
361
484
  VALUE hash_args;
485
+ TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
486
+
487
+ layout_init(self->descriptor->layout, Message_data(self));
362
488
 
363
489
  if (argc == 0) {
364
490
  return Qnil;
@@ -484,38 +610,44 @@ VALUE Message_inspect(VALUE _self) {
484
610
  */
485
611
  VALUE Message_to_h(VALUE _self) {
486
612
  MessageHeader* self;
487
- VALUE hash;
613
+ VALUE hash = rb_hash_new();
488
614
  upb_msg_field_iter it;
615
+ bool is_proto2;
489
616
  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
490
617
 
491
- hash = rb_hash_new();
618
+ // We currently have a few behaviors that are specific to proto2.
619
+ // This is unfortunate, we should key behaviors off field attributes (like
620
+ // whether a field has presence), not proto2 vs. proto3. We should see if we
621
+ // can change this without breaking users.
622
+ is_proto2 =
623
+ upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2;
492
624
 
493
625
  for (upb_msg_field_begin(&it, self->descriptor->msgdef);
494
626
  !upb_msg_field_done(&it);
495
627
  upb_msg_field_next(&it)) {
496
628
  const upb_fielddef* field = upb_msg_iter_field(&it);
629
+ VALUE msg_value;
630
+ VALUE msg_key;
497
631
 
498
- // For proto2, do not include fields which are not set.
499
- if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 &&
500
- field_contains_hasbit(self->descriptor->layout, field) &&
501
- !layout_has(self->descriptor->layout, Message_data(self), field)) {
632
+ // Do not include fields that are not present (oneof or optional fields).
633
+ if (is_proto2 && upb_fielddef_haspresence(field) &&
634
+ !layout_has(self->descriptor->layout, Message_data(self), field)) {
502
635
  continue;
503
636
  }
504
637
 
505
- VALUE msg_value = layout_get(self->descriptor->layout, Message_data(self),
506
- field);
507
- VALUE msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
638
+ msg_value = layout_get(self->descriptor->layout, Message_data(self), field);
639
+ msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
508
640
  if (is_map_field(field)) {
509
641
  msg_value = Map_to_h(msg_value);
510
642
  } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
511
643
  msg_value = RepeatedField_to_ary(msg_value);
512
- if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 &&
513
- RARRAY_LEN(msg_value) == 0) {
644
+ if (is_proto2 && RARRAY_LEN(msg_value) == 0) {
514
645
  continue;
515
646
  }
516
647
 
517
648
  if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
518
- for (int i = 0; i < RARRAY_LEN(msg_value); i++) {
649
+ int i;
650
+ for (i = 0; i < RARRAY_LEN(msg_value); i++) {
519
651
  VALUE elem = rb_ary_entry(msg_value, i);
520
652
  rb_ary_store(msg_value, i, Message_to_h(elem));
521
653
  }
@@ -582,17 +714,11 @@ VALUE Message_descriptor(VALUE klass) {
582
714
  return rb_ivar_get(klass, descriptor_instancevar_interned);
583
715
  }
584
716
 
585
- VALUE build_class_from_descriptor(Descriptor* desc) {
717
+ VALUE build_class_from_descriptor(VALUE descriptor) {
718
+ Descriptor* desc = ruby_to_Descriptor(descriptor);
586
719
  const char *name;
587
720
  VALUE klass;
588
721
 
589
- if (desc->layout == NULL) {
590
- desc->layout = create_layout(desc->msgdef);
591
- }
592
- if (desc->fill_method == NULL) {
593
- desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method);
594
- }
595
-
596
722
  name = upb_msgdef_fullname(desc->msgdef);
597
723
  if (name == NULL) {
598
724
  rb_raise(rb_eRuntimeError, "Descriptor does not have assigned name.");
@@ -603,8 +729,7 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
603
729
  // their own toplevel constant class name.
604
730
  rb_intern("Message"),
605
731
  rb_cObject);
606
- rb_ivar_set(klass, descriptor_instancevar_interned,
607
- get_def_obj(desc->msgdef));
732
+ rb_ivar_set(klass, descriptor_instancevar_interned, descriptor);
608
733
  rb_define_alloc_func(klass, Message_alloc);
609
734
  rb_require("google/protobuf/message_exts");
610
735
  rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts"));
@@ -620,10 +745,11 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
620
745
  // Also define #clone so that we don't inherit Object#clone.
621
746
  rb_define_method(klass, "clone", Message_dup, 0);
622
747
  rb_define_method(klass, "==", Message_eq, 1);
748
+ rb_define_method(klass, "eql?", Message_eq, 1);
623
749
  rb_define_method(klass, "hash", Message_hash, 0);
624
750
  rb_define_method(klass, "to_h", Message_to_h, 0);
625
- rb_define_method(klass, "to_hash", Message_to_h, 0);
626
751
  rb_define_method(klass, "inspect", Message_inspect, 0);
752
+ rb_define_method(klass, "to_s", Message_inspect, 0);
627
753
  rb_define_method(klass, "[]", Message_index, 1);
628
754
  rb_define_method(klass, "[]=", Message_index_set, 2);
629
755
  rb_define_singleton_method(klass, "decode", Message_decode, 1);
@@ -687,7 +813,8 @@ VALUE enum_descriptor(VALUE self) {
687
813
  return rb_ivar_get(self, descriptor_instancevar_interned);
688
814
  }
689
815
 
690
- VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
816
+ VALUE build_module_from_enumdesc(VALUE _enumdesc) {
817
+ EnumDescriptor* enumdesc = ruby_to_EnumDescriptor(_enumdesc);
691
818
  VALUE mod = rb_define_module_id(
692
819
  rb_intern(upb_enumdef_fullname(enumdesc->enumdef)));
693
820
 
@@ -708,8 +835,7 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
708
835
  rb_define_singleton_method(mod, "lookup", enum_lookup, 1);
709
836
  rb_define_singleton_method(mod, "resolve", enum_resolve, 1);
710
837
  rb_define_singleton_method(mod, "descriptor", enum_descriptor, 0);
711
- rb_ivar_set(mod, descriptor_instancevar_interned,
712
- get_def_obj(enumdesc->enumdef));
838
+ rb_ivar_set(mod, descriptor_instancevar_interned, _enumdesc);
713
839
 
714
840
  return mod;
715
841
  }
@@ -30,25 +30,35 @@
30
30
 
31
31
  #include "protobuf.h"
32
32
 
33
- // -----------------------------------------------------------------------------
34
- // Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
35
- // instances.
36
- // -----------------------------------------------------------------------------
37
-
38
- // This is a hash table from def objects (encoded by converting pointers to
39
- // Ruby integers) to MessageDef/EnumDef instances (as Ruby values).
40
- VALUE upb_def_to_ruby_obj_map;
41
-
42
33
  VALUE cError;
43
34
  VALUE cParseError;
44
35
  VALUE cTypeError;
36
+ VALUE c_only_cookie = Qnil;
45
37
 
46
- void add_def_obj(const void* def, VALUE value) {
47
- rb_hash_aset(upb_def_to_ruby_obj_map, ULL2NUM((intptr_t)def), value);
38
+ static VALUE cached_empty_string = Qnil;
39
+ static VALUE cached_empty_bytes = Qnil;
40
+
41
+ static VALUE create_frozen_string(const char* str, size_t size, bool binary) {
42
+ VALUE str_rb = rb_str_new(str, size);
43
+
44
+ rb_enc_associate(str_rb,
45
+ binary ? kRubyString8bitEncoding : kRubyStringUtf8Encoding);
46
+ rb_obj_freeze(str_rb);
47
+ return str_rb;
48
48
  }
49
49
 
50
- VALUE get_def_obj(const void* def) {
51
- return rb_hash_aref(upb_def_to_ruby_obj_map, ULL2NUM((intptr_t)def));
50
+ VALUE get_frozen_string(const char* str, size_t size, bool binary) {
51
+ if (size == 0) {
52
+ return binary ? cached_empty_bytes : cached_empty_string;
53
+ } else {
54
+ // It is harder to memoize non-empty strings. The obvious approach would be
55
+ // to use a Ruby hash keyed by string as memo table, but looking up in such a table
56
+ // requires constructing a string (the very thing we're trying to avoid).
57
+ //
58
+ // Since few fields have defaults, we will just optimize the empty string
59
+ // case for now.
60
+ return create_frozen_string(str, size, binary);
61
+ }
52
62
  }
53
63
 
54
64
  // -----------------------------------------------------------------------------
@@ -116,6 +126,11 @@ void Init_protobuf_c() {
116
126
  kRubyStringASCIIEncoding = rb_usascii_encoding();
117
127
  kRubyString8bitEncoding = rb_ascii8bit_encoding();
118
128
 
119
- rb_gc_register_address(&upb_def_to_ruby_obj_map);
120
- upb_def_to_ruby_obj_map = rb_hash_new();
129
+ rb_gc_register_address(&c_only_cookie);
130
+ c_only_cookie = rb_class_new_instance(0, NULL, rb_cObject);
131
+
132
+ rb_gc_register_address(&cached_empty_string);
133
+ rb_gc_register_address(&cached_empty_bytes);
134
+ cached_empty_string = create_frozen_string("", 0, false);
135
+ cached_empty_bytes = create_frozen_string("", 0, true);
121
136
  }