google-protobuf 3.7.0 → 3.11.0

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.

@@ -3,11 +3,9 @@
3
3
  require 'mkmf'
4
4
 
5
5
  if RUBY_PLATFORM =~ /darwin/ || RUBY_PLATFORM =~ /linux/
6
- # XOPEN_SOURCE needed for strptime:
7
- # https://stackoverflow.com/questions/35234152/strptime-giving-implicit-declaration-and-undefined-reference
8
- $CFLAGS += " -std=c99 -O3 -DNDEBUG -D_XOPEN_SOURCE=700"
6
+ $CFLAGS += " -std=gnu90 -O3 -DNDEBUG -Wall -Wdeclaration-after-statement -Wsign-compare"
9
7
  else
10
- $CFLAGS += " -std=c99 -O3 -DNDEBUG"
8
+ $CFLAGS += " -std=gnu90 -O3 -DNDEBUG"
11
9
  end
12
10
 
13
11
 
@@ -71,6 +71,9 @@ static VALUE table_key(Map* self, VALUE key,
71
71
  case UPB_TYPE_BYTES:
72
72
  case UPB_TYPE_STRING:
73
73
  // Strings: use string content directly.
74
+ if (TYPE(key) == T_SYMBOL) {
75
+ key = rb_id2str(SYM2ID(key));
76
+ }
74
77
  Check_Type(key, T_STRING);
75
78
  key = native_slot_encode_and_freeze_string(self->key_type, key);
76
79
  *out_key = RSTRING_PTR(key);
@@ -82,7 +85,7 @@ static VALUE table_key(Map* self, VALUE key,
82
85
  case UPB_TYPE_INT64:
83
86
  case UPB_TYPE_UINT32:
84
87
  case UPB_TYPE_UINT64:
85
- native_slot_set(self->key_type, Qnil, buf, key);
88
+ native_slot_set("", self->key_type, Qnil, buf, key);
86
89
  *out_key = buf;
87
90
  *out_length = native_slot_size(self->key_type);
88
91
  break;
@@ -387,7 +390,6 @@ VALUE Map_index(VALUE _self, VALUE key) {
387
390
  */
388
391
  VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
389
392
  Map* self = ruby_to_Map(_self);
390
-
391
393
  char keybuf[TABLE_KEY_BUF_LENGTH];
392
394
  const char* keyval = NULL;
393
395
  size_t length = 0;
@@ -395,8 +397,15 @@ VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
395
397
  void* mem;
396
398
  key = table_key(self, key, keybuf, &keyval, &length);
397
399
 
400
+ rb_check_frozen(_self);
401
+
402
+ if (TYPE(value) == T_HASH) {
403
+ VALUE args[1] = { value };
404
+ value = rb_class_new_instance(1, args, self->value_type_class);
405
+ }
406
+
398
407
  mem = value_memory(&v);
399
- native_slot_set(self->value_type, self->value_type_class, mem, value);
408
+ native_slot_set("", self->value_type, self->value_type_class, mem, value);
400
409
 
401
410
  // Replace any existing value by issuing a 'remove' operation first.
402
411
  upb_strtable_remove2(&self->table, keyval, length, NULL);
@@ -439,13 +448,14 @@ VALUE Map_has_key(VALUE _self, VALUE key) {
439
448
  */
440
449
  VALUE Map_delete(VALUE _self, VALUE key) {
441
450
  Map* self = ruby_to_Map(_self);
442
-
443
451
  char keybuf[TABLE_KEY_BUF_LENGTH];
444
452
  const char* keyval = NULL;
445
453
  size_t length = 0;
446
454
  upb_value v;
447
455
  key = table_key(self, key, keybuf, &keyval, &length);
448
456
 
457
+ rb_check_frozen(_self);
458
+
449
459
  if (upb_strtable_remove2(&self->table, keyval, length, &v)) {
450
460
  void* mem = value_memory(&v);
451
461
  return native_slot_get(self->value_type, self->value_type_class, mem);
@@ -463,6 +473,8 @@ VALUE Map_delete(VALUE _self, VALUE key) {
463
473
  VALUE Map_clear(VALUE _self) {
464
474
  Map* self = ruby_to_Map(_self);
465
475
 
476
+ rb_check_frozen(_self);
477
+
466
478
  // Uninit and reinit the table -- this is faster than iterating and doing a
467
479
  // delete-lookup on each key.
468
480
  upb_strtable_uninit(&self->table);
@@ -483,7 +495,7 @@ VALUE Map_length(VALUE _self) {
483
495
  return ULL2NUM(upb_strtable_count(&self->table));
484
496
  }
485
497
 
486
- static VALUE Map_new_this_type(VALUE _self) {
498
+ VALUE Map_new_this_type(VALUE _self) {
487
499
  Map* self = ruby_to_Map(_self);
488
500
  VALUE new_map = Qnil;
489
501
  VALUE key_type = fieldtype_to_ruby(self->key_type);
@@ -547,7 +559,8 @@ VALUE Map_deep_copy(VALUE _self) {
547
559
  void* mem = value_memory(&v);
548
560
  upb_value dup;
549
561
  void* dup_mem = value_memory(&dup);
550
- native_slot_deep_copy(self->value_type, dup_mem, mem);
562
+ native_slot_deep_copy(self->value_type, self->value_type_class, dup_mem,
563
+ mem);
551
564
 
552
565
  if (!upb_strtable_insert2(&new_self->table,
553
566
  upb_strtable_iter_key(&it),
@@ -619,7 +632,8 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
619
632
  return Qfalse;
620
633
  }
621
634
 
622
- if (!native_slot_eq(self->value_type, mem, other_mem)) {
635
+ if (!native_slot_eq(self->value_type, self->value_type_class, mem,
636
+ other_mem)) {
623
637
  // Present, but value not equal.
624
638
  return Qfalse;
625
639
  }
@@ -841,7 +855,6 @@ void Map_register(VALUE module) {
841
855
  rb_define_method(klass, "dup", Map_dup, 0);
842
856
  rb_define_method(klass, "==", Map_eq, 1);
843
857
  rb_define_method(klass, "hash", Map_hash, 0);
844
- rb_define_method(klass, "to_hash", Map_to_h, 0);
845
858
  rb_define_method(klass, "to_h", Map_to_h, 0);
846
859
  rb_define_method(klass, "inspect", Map_inspect, 0);
847
860
  rb_define_method(klass, "merge", Map_merge, 1);
@@ -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
+ }
132
+
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
+ }
127
145
 
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);
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,9 +180,62 @@ 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
 
@@ -203,41 +283,44 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
203
283
  MessageHeader* self;
204
284
  const upb_oneofdef* o;
205
285
  const upb_fielddef* f;
286
+ int accessor_type;
206
287
 
207
288
  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
208
289
  if (argc < 1) {
209
290
  rb_raise(rb_eArgError, "Expected method name as first argument.");
210
291
  }
211
292
 
212
- int accessor_type = extract_method_call(argv[0], self, &f, &o);
293
+ accessor_type = extract_method_call(argv[0], self, &f, &o);
213
294
  if (accessor_type == METHOD_UNKNOWN || (o == NULL && f == NULL) ) {
214
295
  return rb_call_super(argc, argv);
215
- } else if (accessor_type == METHOD_SETTER) {
296
+ } else if (accessor_type == METHOD_SETTER || accessor_type == METHOD_WRAPPER_SETTER) {
216
297
  if (argc != 2) {
217
298
  rb_raise(rb_eArgError, "Expected 2 arguments, received %d", argc);
218
299
  }
300
+ rb_check_frozen(_self);
219
301
  } else if (argc != 1) {
220
302
  rb_raise(rb_eArgError, "Expected 1 argument, received %d", argc);
221
303
  }
222
304
 
223
305
  // Return which of the oneof fields are set
224
306
  if (o != NULL) {
307
+ const upb_fielddef* oneof_field = which_oneof_field(self, o);
308
+
225
309
  if (accessor_type == METHOD_SETTER) {
226
310
  rb_raise(rb_eRuntimeError, "Oneof accessors are read-only.");
227
311
  }
228
312
 
229
- const upb_fielddef* oneof_field = which_oneof_field(self, o);
230
313
  if (accessor_type == METHOD_PRESENCE) {
231
314
  return oneof_field == NULL ? Qfalse : Qtrue;
232
315
  } else if (accessor_type == METHOD_CLEAR) {
233
316
  if (oneof_field != NULL) {
234
- layout_clear(self->descriptor->layout, Message_data(self), oneof_field);
317
+ layout_clear(self->descriptor->layout, Message_data(self), oneof_field);
235
318
  }
236
319
  return Qnil;
237
320
  } else {
238
321
  // METHOD_ACCESSOR
239
322
  return oneof_field == NULL ? Qnil :
240
- ID2SYM(rb_intern(upb_fielddef_name(oneof_field)));
323
+ ID2SYM(rb_intern(upb_fielddef_name(oneof_field)));
241
324
  }
242
325
  // Otherwise we're operating on a single proto field
243
326
  } else if (accessor_type == METHOD_SETTER) {
@@ -248,6 +331,41 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
248
331
  return Qnil;
249
332
  } else if (accessor_type == METHOD_PRESENCE) {
250
333
  return layout_has(self->descriptor->layout, Message_data(self), f);
334
+ } else if (accessor_type == METHOD_WRAPPER_GETTER) {
335
+ VALUE value = layout_get(self->descriptor->layout, Message_data(self), f);
336
+ switch (TYPE(value)) {
337
+ case T_DATA:
338
+ return rb_funcall(value, rb_intern("value"), 0);
339
+ case T_NIL:
340
+ return Qnil;
341
+ default:
342
+ return value;
343
+ }
344
+ } else if (accessor_type == METHOD_WRAPPER_SETTER) {
345
+ VALUE wrapper = ruby_wrapper_type(
346
+ field_type_class(self->descriptor->layout, f), argv[1]);
347
+ layout_set(self->descriptor->layout, Message_data(self), f, wrapper);
348
+ return Qnil;
349
+ } else if (accessor_type == METHOD_ENUM_GETTER) {
350
+ VALUE enum_type = field_type_class(self->descriptor->layout, f);
351
+ VALUE method = rb_intern("const_get");
352
+ VALUE raw_value = layout_get(self->descriptor->layout, Message_data(self), f);
353
+
354
+ // Map repeated fields to a new type with ints
355
+ if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
356
+ int array_size = FIX2INT(rb_funcall(raw_value, rb_intern("length"), 0));
357
+ int i;
358
+ VALUE array_args[1] = { ID2SYM(rb_intern("int64")) };
359
+ VALUE array = rb_class_new_instance(1, array_args, CLASS_OF(raw_value));
360
+ for (i = 0; i < array_size; i++) {
361
+ VALUE entry = rb_funcall(enum_type, method, 1, rb_funcall(raw_value,
362
+ rb_intern("at"), 1, INT2NUM(i)));
363
+ rb_funcall(array, rb_intern("push"), 1, entry);
364
+ }
365
+ return array;
366
+ }
367
+ // Convert the value for singular fields
368
+ return rb_funcall(enum_type, method, 1, raw_value);
251
369
  } else {
252
370
  return layout_get(self->descriptor->layout, Message_data(self), f);
253
371
  }
@@ -258,13 +376,14 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
258
376
  MessageHeader* self;
259
377
  const upb_oneofdef* o;
260
378
  const upb_fielddef* f;
379
+ int accessor_type;
261
380
 
262
381
  TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
263
382
  if (argc < 1) {
264
383
  rb_raise(rb_eArgError, "Expected method name as first argument.");
265
384
  }
266
385
 
267
- int accessor_type = extract_method_call(argv[0], self, &f, &o);
386
+ accessor_type = extract_method_call(argv[0], self, &f, &o);
268
387
  if (accessor_type == METHOD_UNKNOWN) {
269
388
  return rb_call_super(argc, argv);
270
389
  } else if (o != NULL) {
@@ -274,15 +393,10 @@ VALUE Message_respond_to_missing(int argc, VALUE* argv, VALUE _self) {
274
393
  }
275
394
  }
276
395
 
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
-
396
+ VALUE create_submsg_from_hash(const MessageLayout* layout,
397
+ const upb_fielddef* f, VALUE hash) {
284
398
  VALUE args[1] = { hash };
285
- return rb_class_new_instance(1, args, msgclass);
399
+ return rb_class_new_instance(1, args, field_type_class(layout, f));
286
400
  }
287
401
 
288
402
  int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
@@ -315,29 +429,32 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
315
429
 
316
430
  if (TYPE(val) != T_HASH) {
317
431
  rb_raise(rb_eArgError,
318
- "Expected Hash object as initializer value for map field '%s'.", name);
432
+ "Expected Hash object as initializer value for map field '%s' (given %s).",
433
+ name, rb_class2name(CLASS_OF(val)));
319
434
  }
320
435
  map = layout_get(self->descriptor->layout, Message_data(self), f);
321
436
  Map_merge_into_self(map, val);
322
437
  } else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
323
438
  VALUE ary;
439
+ int i;
324
440
 
325
441
  if (TYPE(val) != T_ARRAY) {
326
442
  rb_raise(rb_eArgError,
327
- "Expected array as initializer value for repeated field '%s'.", name);
443
+ "Expected array as initializer value for repeated field '%s' (given %s).",
444
+ name, rb_class2name(CLASS_OF(val)));
328
445
  }
329
446
  ary = layout_get(self->descriptor->layout, Message_data(self), f);
330
- for (int i = 0; i < RARRAY_LEN(val); i++) {
447
+ for (i = 0; i < RARRAY_LEN(val); i++) {
331
448
  VALUE entry = rb_ary_entry(val, i);
332
449
  if (TYPE(entry) == T_HASH && upb_fielddef_issubmsg(f)) {
333
- entry = create_submsg_from_hash(f, entry);
450
+ entry = create_submsg_from_hash(self->descriptor->layout, f, entry);
334
451
  }
335
452
 
336
453
  RepeatedField_push(ary, entry);
337
454
  }
338
455
  } else {
339
456
  if (TYPE(val) == T_HASH && upb_fielddef_issubmsg(f)) {
340
- val = create_submsg_from_hash(f, val);
457
+ val = create_submsg_from_hash(self->descriptor->layout, f, val);
341
458
  }
342
459
 
343
460
  layout_set(self->descriptor->layout, Message_data(self), f, val);
@@ -358,7 +475,11 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
358
475
  * Message class are provided on each concrete message class.
359
476
  */
360
477
  VALUE Message_initialize(int argc, VALUE* argv, VALUE _self) {
478
+ MessageHeader* self;
361
479
  VALUE hash_args;
480
+ TypedData_Get_Struct(_self, MessageHeader, &Message_type, self);
481
+
482
+ layout_init(self->descriptor->layout, Message_data(self));
362
483
 
363
484
  if (argc == 0) {
364
485
  return Qnil;
@@ -494,17 +615,18 @@ VALUE Message_to_h(VALUE _self) {
494
615
  !upb_msg_field_done(&it);
495
616
  upb_msg_field_next(&it)) {
496
617
  const upb_fielddef* field = upb_msg_iter_field(&it);
618
+ VALUE msg_value;
619
+ VALUE msg_key;
497
620
 
498
621
  // For proto2, do not include fields which are not set.
499
622
  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)) {
623
+ field_contains_hasbit(self->descriptor->layout, field) &&
624
+ !layout_has(self->descriptor->layout, Message_data(self), field)) {
502
625
  continue;
503
626
  }
504
627
 
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)));
628
+ msg_value = layout_get(self->descriptor->layout, Message_data(self), field);
629
+ msg_key = ID2SYM(rb_intern(upb_fielddef_name(field)));
508
630
  if (is_map_field(field)) {
509
631
  msg_value = Map_to_h(msg_value);
510
632
  } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
@@ -515,7 +637,8 @@ VALUE Message_to_h(VALUE _self) {
515
637
  }
516
638
 
517
639
  if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
518
- for (int i = 0; i < RARRAY_LEN(msg_value); i++) {
640
+ int i;
641
+ for (i = 0; i < RARRAY_LEN(msg_value); i++) {
519
642
  VALUE elem = rb_ary_entry(msg_value, i);
520
643
  rb_ary_store(msg_value, i, Message_to_h(elem));
521
644
  }
@@ -582,17 +705,11 @@ VALUE Message_descriptor(VALUE klass) {
582
705
  return rb_ivar_get(klass, descriptor_instancevar_interned);
583
706
  }
584
707
 
585
- VALUE build_class_from_descriptor(Descriptor* desc) {
708
+ VALUE build_class_from_descriptor(VALUE descriptor) {
709
+ Descriptor* desc = ruby_to_Descriptor(descriptor);
586
710
  const char *name;
587
711
  VALUE klass;
588
712
 
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
713
  name = upb_msgdef_fullname(desc->msgdef);
597
714
  if (name == NULL) {
598
715
  rb_raise(rb_eRuntimeError, "Descriptor does not have assigned name.");
@@ -603,8 +720,7 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
603
720
  // their own toplevel constant class name.
604
721
  rb_intern("Message"),
605
722
  rb_cObject);
606
- rb_ivar_set(klass, descriptor_instancevar_interned,
607
- get_def_obj(desc->msgdef));
723
+ rb_ivar_set(klass, descriptor_instancevar_interned, descriptor);
608
724
  rb_define_alloc_func(klass, Message_alloc);
609
725
  rb_require("google/protobuf/message_exts");
610
726
  rb_include_module(klass, rb_eval_string("::Google::Protobuf::MessageExts"));
@@ -620,10 +736,11 @@ VALUE build_class_from_descriptor(Descriptor* desc) {
620
736
  // Also define #clone so that we don't inherit Object#clone.
621
737
  rb_define_method(klass, "clone", Message_dup, 0);
622
738
  rb_define_method(klass, "==", Message_eq, 1);
739
+ rb_define_method(klass, "eql?", Message_eq, 1);
623
740
  rb_define_method(klass, "hash", Message_hash, 0);
624
741
  rb_define_method(klass, "to_h", Message_to_h, 0);
625
- rb_define_method(klass, "to_hash", Message_to_h, 0);
626
742
  rb_define_method(klass, "inspect", Message_inspect, 0);
743
+ rb_define_method(klass, "to_s", Message_inspect, 0);
627
744
  rb_define_method(klass, "[]", Message_index, 1);
628
745
  rb_define_method(klass, "[]=", Message_index_set, 2);
629
746
  rb_define_singleton_method(klass, "decode", Message_decode, 1);
@@ -687,7 +804,8 @@ VALUE enum_descriptor(VALUE self) {
687
804
  return rb_ivar_get(self, descriptor_instancevar_interned);
688
805
  }
689
806
 
690
- VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
807
+ VALUE build_module_from_enumdesc(VALUE _enumdesc) {
808
+ EnumDescriptor* enumdesc = ruby_to_EnumDescriptor(_enumdesc);
691
809
  VALUE mod = rb_define_module_id(
692
810
  rb_intern(upb_enumdef_fullname(enumdesc->enumdef)));
693
811
 
@@ -708,8 +826,7 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
708
826
  rb_define_singleton_method(mod, "lookup", enum_lookup, 1);
709
827
  rb_define_singleton_method(mod, "resolve", enum_resolve, 1);
710
828
  rb_define_singleton_method(mod, "descriptor", enum_descriptor, 0);
711
- rb_ivar_set(mod, descriptor_instancevar_interned,
712
- get_def_obj(enumdesc->enumdef));
829
+ rb_ivar_set(mod, descriptor_instancevar_interned, _enumdesc);
713
830
 
714
831
  return mod;
715
832
  }
@@ -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
  }