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.
- checksums.yaml +4 -4
- data/ext/google/protobuf_c/defs.c +942 -833
- data/ext/google/protobuf_c/encode_decode.c +521 -300
- data/ext/google/protobuf_c/extconf.rb +2 -4
- data/ext/google/protobuf_c/map.c +41 -54
- data/ext/google/protobuf_c/message.c +212 -86
- data/ext/google/protobuf_c/protobuf.c +30 -15
- data/ext/google/protobuf_c/protobuf.h +114 -60
- data/ext/google/protobuf_c/repeated_field.c +55 -23
- data/ext/google/protobuf_c/storage.c +350 -171
- data/ext/google/protobuf_c/upb.c +5630 -9131
- data/ext/google/protobuf_c/upb.h +4590 -7568
- data/lib/google/protobuf/any_pb.rb +1 -1
- data/lib/google/protobuf/api_pb.rb +3 -3
- data/lib/google/protobuf/duration_pb.rb +1 -1
- data/lib/google/protobuf/empty_pb.rb +1 -1
- data/lib/google/protobuf/field_mask_pb.rb +1 -1
- data/lib/google/protobuf/source_context_pb.rb +1 -1
- data/lib/google/protobuf/struct_pb.rb +4 -4
- data/lib/google/protobuf/timestamp_pb.rb +1 -1
- data/lib/google/protobuf/type_pb.rb +8 -8
- data/lib/google/protobuf/well_known_types.rb +8 -2
- data/lib/google/protobuf/wrappers_pb.rb +9 -9
- data/lib/google/protobuf.rb +70 -0
- data/tests/basic.rb +250 -68
- data/tests/generated_code_test.rb +0 -0
- data/tests/stress.rb +0 -0
- metadata +18 -12
@@ -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
|
64
|
-
uint8_t, sizeof(MessageHeader) + desc->layout->size);
|
63
|
+
MessageHeader* msg;
|
65
64
|
VALUE ret;
|
66
65
|
|
67
|
-
|
66
|
+
if (desc->layout == NULL) {
|
67
|
+
create_layout(desc);
|
68
|
+
}
|
68
69
|
|
69
|
-
|
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
|
-
|
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
|
-
|
90
|
-
|
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
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
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
|
-
|
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
|
-
|
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 (!
|
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
|
-
|
167
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
278
|
-
|
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,
|
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'.",
|
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'.",
|
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 (
|
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
|
-
|
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
|
-
//
|
499
|
-
if (
|
500
|
-
|
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
|
-
|
506
|
-
|
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 (
|
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
|
-
|
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(
|
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(
|
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
|
-
|
47
|
-
|
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
|
51
|
-
|
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(&
|
120
|
-
|
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
|
}
|