Merge pull request #6797 from haberman/ruby-lazy-wrappers

Ruby lazy wrappers optimization
pull/6861/head
Joshua Haberman 5 years ago committed by GitHub
commit 0eb9b279e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 237
      ruby/ext/google/protobuf_c/encode_decode.c
  2. 6
      ruby/ext/google/protobuf_c/map.c
  3. 59
      ruby/ext/google/protobuf_c/message.c
  4. 9
      ruby/ext/google/protobuf_c/protobuf.h
  5. 5
      ruby/ext/google/protobuf_c/repeated_field.c
  6. 41
      ruby/ext/google/protobuf_c/storage.c
  7. 42
      ruby/tests/basic.rb
  8. 41
      ruby/tests/basic_test.proto
  9. 29
      ruby/tests/basic_test_proto2.proto
  10. 461
      ruby/tests/common_tests.rb

@ -278,6 +278,17 @@ static void *appendsubmsg_handler(void *closure, const void *hd) {
return submsg;
}
// Appends a wrapper to a repeated field (a regular Ruby array for now).
static void *appendwrapper_handler(void *closure, const void *hd) {
VALUE ary = (VALUE)closure;
int size = RepeatedField_size(ary);
(void)hd;
RepeatedField_push(ary, Qnil);
return RepeatedField_index_native(ary, size);
}
// Sets a non-repeated submessage field in a message.
static void *submsg_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
@ -298,6 +309,15 @@ static void *submsg_handler(void *closure, const void *hd) {
return submsg;
}
static void* startwrapper(void* closure, const void* hd) {
char* msg = closure;
const submsg_handlerdata_t* submsgdata = hd;
set_hasbit(closure, submsgdata->hasbit);
return msg + submsgdata->ofs;
}
// Handler data for startmap/endmap handlers.
typedef struct {
size_t ofs;
@ -486,14 +506,39 @@ static void *oneofsubmsg_handler(void *closure,
// indicating a VALUE is present and expect a valid VALUE. See comment in
// layout_set() for more detail: basically, the change to the value and the
// case must be atomic w.r.t. the Ruby VM.
DEREF(msg, oneofdata->case_ofs, uint32_t) =
oneofdata->oneof_case_num;
DEREF(msg, oneofdata->case_ofs, uint32_t) = oneofdata->oneof_case_num;
submsg_rb = DEREF(msg, oneofdata->ofs, VALUE);
TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg);
return submsg;
}
static void* oneof_startwrapper(void* closure, const void* hd) {
char* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
DEREF(msg, oneofdata->case_ofs, uint32_t) = oneofdata->oneof_case_num;
return msg + oneofdata->ofs;
}
bool is_wrapper(const upb_msgdef* m) {
switch (upb_msgdef_wellknowntype(m)) {
case UPB_WELLKNOWN_DOUBLEVALUE:
case UPB_WELLKNOWN_FLOATVALUE:
case UPB_WELLKNOWN_INT64VALUE:
case UPB_WELLKNOWN_UINT64VALUE:
case UPB_WELLKNOWN_INT32VALUE:
case UPB_WELLKNOWN_UINT32VALUE:
case UPB_WELLKNOWN_STRINGVALUE:
case UPB_WELLKNOWN_BYTESVALUE:
case UPB_WELLKNOWN_BOOLVALUE:
return true;
default:
return false;
}
}
// Set up handlers for a repeated field.
static void add_handlers_for_repeated_field(upb_handlers *h,
const Descriptor* desc,
@ -535,12 +580,96 @@ static void add_handlers_for_repeated_field(upb_handlers *h,
VALUE subklass = field_type_class(desc->layout, f);
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newsubmsghandlerdata(h, 0, -1, subklass);
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
if (is_wrapper(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, appendwrapper_handler, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
}
break;
}
}
}
static bool doublewrapper_handler(void* closure, const void* hd, double val) {
VALUE* rbval = closure;
*rbval = DBL2NUM(val);
return true;
}
static bool floatwrapper_handler(void* closure, const void* hd, float val) {
VALUE* rbval = closure;
*rbval = DBL2NUM(val);
return true;
}
static bool int64wrapper_handler(void* closure, const void* hd, int64_t val) {
VALUE* rbval = closure;
*rbval = LL2NUM(val);
return true;
}
static bool uint64wrapper_handler(void* closure, const void* hd, uint64_t val) {
VALUE* rbval = closure;
*rbval = ULL2NUM(val);
return true;
}
static bool int32wrapper_handler(void* closure, const void* hd, int32_t val) {
VALUE* rbval = closure;
*rbval = INT2NUM(val);
return true;
}
static bool uint32wrapper_handler(void* closure, const void* hd, uint32_t val) {
VALUE* rbval = closure;
*rbval = UINT2NUM(val);
return true;
}
static void* startstringwrapper_handler(void* closure, const void* hd,
size_t size_hint) {
VALUE* rbval = closure;
(void)size_hint;
*rbval = rb_str_new(NULL, 0);
rb_enc_associate(*rbval, kRubyStringUtf8Encoding);
return closure;
}
static size_t stringwrapper_handler(void* closure, const void* hd,
const char* ptr, size_t len,
const upb_bufhandle* handle) {
VALUE* rbval = closure;
*rbval = noleak_rb_str_cat(*rbval, ptr, len);
return len;
}
static void* startbyteswrapper_handler(void* closure, const void* hd,
size_t size_hint) {
VALUE* rbval = closure;
(void)size_hint;
*rbval = rb_str_new(NULL, 0);
rb_enc_associate(*rbval, kRubyString8bitEncoding);
return closure;
}
static size_t byteswrapper_handler(void* closure, const void* hd,
const char* ptr, size_t len,
const upb_bufhandle* handle) {
VALUE* rbval = closure;
*rbval = noleak_rb_str_cat(*rbval, ptr, len);
return len;
}
static bool boolwrapper_handler(void* closure, const void* hd, bool val) {
VALUE* rbval = closure;
if (val) {
*rbval = Qtrue;
} else {
*rbval = Qfalse;
}
return true;
}
// Set up handlers for a singular field.
static void add_handlers_for_singular_field(const Descriptor* desc,
upb_handlers* h,
@ -580,8 +709,11 @@ static void add_handlers_for_singular_field(const Descriptor* desc,
upb_handlerattr attr = UPB_HANDLERATTR_INIT;
attr.handler_data = newsubmsghandlerdata(
h, offset, hasbit, field_type_class(desc->layout, f));
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
break;
if (is_wrapper(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, startwrapper, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
}
}
}
}
@ -623,6 +755,45 @@ static void add_handlers_for_mapentry(const upb_msgdef* msgdef, upb_handlers* h,
MESSAGE_FIELD_NO_HASBIT);
}
static void add_handlers_for_wrapper(const upb_msgdef* msgdef,
upb_handlers* h) {
const upb_fielddef* f = upb_msgdef_itof(msgdef, 1);
switch (upb_msgdef_wellknowntype(msgdef)) {
case UPB_WELLKNOWN_DOUBLEVALUE:
upb_handlers_setdouble(h, f, doublewrapper_handler, NULL);
break;
case UPB_WELLKNOWN_FLOATVALUE:
upb_handlers_setfloat(h, f, floatwrapper_handler, NULL);
break;
case UPB_WELLKNOWN_INT64VALUE:
upb_handlers_setint64(h, f, int64wrapper_handler, NULL);
break;
case UPB_WELLKNOWN_UINT64VALUE:
upb_handlers_setuint64(h, f, uint64wrapper_handler, NULL);
break;
case UPB_WELLKNOWN_INT32VALUE:
upb_handlers_setint32(h, f, int32wrapper_handler, NULL);
break;
case UPB_WELLKNOWN_UINT32VALUE:
upb_handlers_setuint32(h, f, uint32wrapper_handler, NULL);
break;
case UPB_WELLKNOWN_STRINGVALUE:
upb_handlers_setstartstr(h, f, startstringwrapper_handler, NULL);
upb_handlers_setstring(h, f, stringwrapper_handler, NULL);
break;
case UPB_WELLKNOWN_BYTESVALUE:
upb_handlers_setstartstr(h, f, startbyteswrapper_handler, NULL);
upb_handlers_setstring(h, f, byteswrapper_handler, NULL);
break;
case UPB_WELLKNOWN_BOOLVALUE:
upb_handlers_setbool(h, f, boolwrapper_handler, NULL);
return;
default:
rb_raise(rb_eRuntimeError,
"Internal logic error with well-known types.");
}
}
// Set up handlers for a oneof field.
static void add_handlers_for_oneof_field(upb_handlers *h,
const upb_fielddef *f,
@ -662,7 +833,11 @@ static void add_handlers_for_oneof_field(upb_handlers *h,
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
if (is_wrapper(upb_fielddef_msgsubdef(f))) {
upb_handlers_setstartsubmsg(h, f, oneof_startwrapper, &attr);
} else {
upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
}
break;
}
}
@ -683,6 +858,10 @@ static bool unknown_field_handler(void* closure, const void* hd,
return true;
}
size_t get_field_offset(MessageLayout* layout, const upb_fielddef* f) {
return layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader);
}
void add_handlers_for_message(const void *closure, upb_handlers *h) {
const VALUE descriptor_pool = (VALUE)closure;
const upb_msgdef* msgdef = upb_handlers_msgdef(h);
@ -706,6 +885,12 @@ void add_handlers_for_message(const void *closure, upb_handlers *h) {
return;
}
// If this is a wrapper type, use special handlers and bail.
if (is_wrapper(msgdef)) {
add_handlers_for_wrapper(msgdef, h);
return;
}
upb_handlers_setunknown(h, unknown_field_handler, &attr);
for (upb_msg_field_begin(&i, desc->msgdef);
@ -713,8 +898,7 @@ void add_handlers_for_message(const void *closure, upb_handlers *h) {
upb_msg_field_next(&i)) {
const upb_fielddef *f = upb_msg_iter_field(&i);
const upb_oneofdef *oneof = upb_fielddef_containingoneof(f);
size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset +
sizeof(MessageHeader);
size_t offset = get_field_offset(desc->layout, f);
if (oneof) {
size_t oneof_case_offset =
@ -826,17 +1010,28 @@ VALUE Message_decode(VALUE klass, VALUE data) {
{
const upb_pbdecodermethod* method = msgdef_decodermethod(desc);
const upb_handlers* h = upb_pbdecodermethod_desthandlers(method);
const upb_msgdef* m = upb_handlers_msgdef(h);
VALUE wrapper = Qnil;
void* ptr = msg;
stackenv se;
upb_sink sink;
upb_pbdecoder* decoder;
stackenv_init(&se, "Error occurred during parsing: %" PRIsVALUE);
upb_sink_reset(&sink, h, msg);
if (is_wrapper(m)) {
ptr = &wrapper;
}
upb_sink_reset(&sink, h, ptr);
decoder = upb_pbdecoder_create(se.arena, method, sink, &se.status);
upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data),
upb_pbdecoder_input(decoder));
stackenv_uninit(&se);
if (is_wrapper(m)) {
msg_rb = ruby_wrapper_type(msgklass, wrapper);
}
}
return msg_rb;
@ -890,13 +1085,21 @@ VALUE Message_decode_json(int argc, VALUE* argv, VALUE klass) {
{
const upb_json_parsermethod* method = msgdef_jsonparsermethod(desc);
const upb_handlers* h = get_fill_handlers(desc);
const upb_msgdef* m = upb_handlers_msgdef(h);
stackenv se;
upb_sink sink;
upb_json_parser* parser;
DescriptorPool* pool = ruby_to_DescriptorPool(generated_pool);
stackenv_init(&se, "Error occurred during parsing: %" PRIsVALUE);
upb_sink_reset(&sink, get_fill_handlers(desc), msg);
if (is_wrapper(m)) {
rb_raise(
rb_eRuntimeError,
"Parsing a wrapper type from JSON at the top level does not work.");
}
upb_sink_reset(&sink, h, msg);
parser = upb_json_parser_create(se.arena, method, pool->symtab, sink,
&se.status, RTEST(ignore_unknown_fields));
upb_bufsrc_putbuf(RSTRING_PTR(data), RSTRING_LEN(data),
@ -969,6 +1172,7 @@ static void putary(VALUE ary, const upb_fielddef* f, upb_sink sink, int depth,
upb_selector_t sel = 0;
int size;
int i;
VALUE type_class = ruby_to_RepeatedField(ary)->field_type_class;
if (ary == Qnil) return;
if (!emit_defaults && NUM2INT(RepeatedField_length(ary)) == 0) return;
@ -1003,9 +1207,11 @@ static void putary(VALUE ary, const upb_fielddef* f, upb_sink sink, int depth,
case UPB_TYPE_BYTES:
putstr(*((VALUE *)memory), f, subsink);
break;
case UPB_TYPE_MESSAGE:
putsubmsg(*((VALUE*)memory), f, subsink, depth, emit_defaults, is_json);
case UPB_TYPE_MESSAGE: {
VALUE val = native_slot_get(UPB_TYPE_MESSAGE, type_class, memory);
putsubmsg(val, f, subsink, depth, emit_defaults, is_json);
break;
}
#undef T
@ -1306,8 +1512,10 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
putstr(str, f, sink);
}
} else if (upb_fielddef_issubmsg(f)) {
putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth,
emit_defaults, is_json);
// OPT: could try to avoid the layout_get() (which will expand lazy
// wrappers).
VALUE val = layout_get(desc->layout, Message_data(msg), f);
putsubmsg(val, f, sink, depth, emit_defaults, is_json);
} else {
upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
@ -1341,7 +1549,6 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
}
#undef T
}
}

@ -559,7 +559,8 @@ VALUE Map_deep_copy(VALUE _self) {
void* mem = value_memory(&v);
upb_value dup;
void* dup_mem = value_memory(&dup);
native_slot_deep_copy(self->value_type, dup_mem, mem);
native_slot_deep_copy(self->value_type, self->value_type_class, dup_mem,
mem);
if (!upb_strtable_insert2(&new_self->table,
upb_strtable_iter_key(&it),
@ -631,7 +632,8 @@ VALUE Map_eq(VALUE _self, VALUE _other) {
return Qfalse;
}
if (!native_slot_eq(self->value_type, mem, other_mem)) {
if (!native_slot_eq(self->value_type, self->value_type_class, mem,
other_mem)) {
// Present, but value not equal.
return Qfalse;
}

@ -62,13 +62,12 @@ VALUE Message_alloc(VALUE klass) {
Descriptor* desc = ruby_to_Descriptor(descriptor);
MessageHeader* msg;
VALUE ret;
size_t size;
if (desc->layout == NULL) {
create_layout(desc);
}
msg = ALLOC_N(uint8_t, sizeof(MessageHeader) + desc->layout->size);
msg = (void*)ALLOC_N(uint8_t, sizeof(MessageHeader) + desc->layout->size);
msg->descriptor = desc;
msg->unknown_fields = NULL;
memcpy(Message_data(msg), desc->layout->empty_template, desc->layout->size);
@ -109,30 +108,36 @@ enum {
};
// Check if the field is a well known wrapper type
static bool is_wrapper_type_field(const MessageLayout* layout,
const upb_fielddef* field) {
const char* field_type_name = rb_class2name(field_type_class(layout, field));
return strcmp(field_type_name, "Google::Protobuf::DoubleValue") == 0 ||
strcmp(field_type_name, "Google::Protobuf::FloatValue") == 0 ||
strcmp(field_type_name, "Google::Protobuf::Int32Value") == 0 ||
strcmp(field_type_name, "Google::Protobuf::Int64Value") == 0 ||
strcmp(field_type_name, "Google::Protobuf::UInt32Value") == 0 ||
strcmp(field_type_name, "Google::Protobuf::UInt64Value") == 0 ||
strcmp(field_type_name, "Google::Protobuf::BoolValue") == 0 ||
strcmp(field_type_name, "Google::Protobuf::StringValue") == 0 ||
strcmp(field_type_name, "Google::Protobuf::BytesValue") == 0;
bool is_wrapper_type_field(const upb_fielddef* field) {
const upb_msgdef *m;
if (upb_fielddef_type(field) != UPB_TYPE_MESSAGE) {
return false;
}
m = upb_fielddef_msgsubdef(field);
switch (upb_msgdef_wellknowntype(m)) {
case UPB_WELLKNOWN_DOUBLEVALUE:
case UPB_WELLKNOWN_FLOATVALUE:
case UPB_WELLKNOWN_INT64VALUE:
case UPB_WELLKNOWN_UINT64VALUE:
case UPB_WELLKNOWN_INT32VALUE:
case UPB_WELLKNOWN_UINT32VALUE:
case UPB_WELLKNOWN_STRINGVALUE:
case UPB_WELLKNOWN_BYTESVALUE:
case UPB_WELLKNOWN_BOOLVALUE:
return true;
default:
return false;
}
}
// Get a new Ruby wrapper type and set the initial value
static VALUE ruby_wrapper_type(const MessageLayout* layout,
const upb_fielddef* field, const VALUE value) {
if (is_wrapper_type_field(layout, field) && value != Qnil) {
VALUE ruby_wrapper_type(VALUE type_class, VALUE value) {
if (value != Qnil) {
VALUE hash = rb_hash_new();
rb_hash_aset(hash, rb_str_new2("value"), value);
{
VALUE args[1] = {hash};
return rb_class_new_instance(1, args, field_type_class(layout, field));
return rb_class_new_instance(1, args, type_class);
}
}
return Qnil;
@ -193,8 +198,7 @@ static int extract_method_call(VALUE method_name, MessageHeader* self,
// Check if field exists and is a wrapper type
if (upb_msgdef_lookupname(self->descriptor->msgdef, wrapper_field_name,
name_len - 9, &test_f_wrapper, &test_o_wrapper) &&
upb_fielddef_type(test_f_wrapper) == UPB_TYPE_MESSAGE &&
is_wrapper_type_field(self->descriptor->layout, test_f_wrapper)) {
is_wrapper_type_field(test_f_wrapper)) {
// It does exist!
has_field = true;
if (accessor_type == METHOD_SETTER) {
@ -329,12 +333,17 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
return layout_has(self->descriptor->layout, Message_data(self), f);
} else if (accessor_type == METHOD_WRAPPER_GETTER) {
VALUE value = layout_get(self->descriptor->layout, Message_data(self), f);
if (value != Qnil) {
value = rb_funcall(value, rb_intern("value"), 0);
switch (TYPE(value)) {
case T_DATA:
return rb_funcall(value, rb_intern("value"), 0);
case T_NIL:
return Qnil;
default:
return value;
}
return value;
} else if (accessor_type == METHOD_WRAPPER_SETTER) {
VALUE wrapper = ruby_wrapper_type(self->descriptor->layout, f, argv[1]);
VALUE wrapper = ruby_wrapper_type(
field_type_class(self->descriptor->layout, f), argv[1]);
layout_set(self->descriptor->layout, Message_data(self), f, wrapper);
return Qnil;
} else if (accessor_type == METHOD_ENUM_GETTER) {

@ -363,8 +363,10 @@ VALUE native_slot_get(upb_fieldtype_t type,
void native_slot_init(upb_fieldtype_t type, void* memory);
void native_slot_mark(upb_fieldtype_t type, void* memory);
void native_slot_dup(upb_fieldtype_t type, void* to, void* from);
void native_slot_deep_copy(upb_fieldtype_t type, void* to, void* from);
bool native_slot_eq(upb_fieldtype_t type, void* mem1, void* mem2);
void native_slot_deep_copy(upb_fieldtype_t type, VALUE type_class, void* to,
void* from);
bool native_slot_eq(upb_fieldtype_t type, VALUE type_class, void* mem1,
void* mem2);
VALUE native_slot_encode_and_freeze_string(upb_fieldtype_t type, VALUE value);
void native_slot_check_int_range_precision(const char* name, upb_fieldtype_t type, VALUE value);
@ -556,6 +558,9 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2);
VALUE layout_hash(MessageLayout* layout, void* storage);
VALUE layout_inspect(MessageLayout* layout, void* storage);
bool is_wrapper_type_field(const upb_fielddef* field);
VALUE ruby_wrapper_type(VALUE type_class, VALUE value);
// -----------------------------------------------------------------------------
// Message class creation.
// -----------------------------------------------------------------------------

@ -378,7 +378,7 @@ VALUE RepeatedField_deep_copy(VALUE _self) {
for (i = 0; i < self->size; i++, off += elem_size) {
void* to_mem = (uint8_t *)new_rptfield_self->elements + off;
void* from_mem = (uint8_t *)self->elements + off;
native_slot_deep_copy(field_type, to_mem, from_mem);
native_slot_deep_copy(field_type, self->field_type_class, to_mem, from_mem);
new_rptfield_self->size++;
}
@ -451,7 +451,8 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
for (i = 0; i < self->size; i++, off += elem_size) {
void* self_mem = ((uint8_t *)self->elements) + off;
void* other_mem = ((uint8_t *)other->elements) + off;
if (!native_slot_eq(field_type, self_mem, other_mem)) {
if (!native_slot_eq(field_type, self->field_type_class, self_mem,
other_mem)) {
return Qfalse;
}
}

@ -294,8 +294,20 @@ VALUE native_slot_get(upb_fieldtype_t type,
return DEREF(memory, int8_t) ? Qtrue : Qfalse;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
return DEREF(memory, VALUE);
case UPB_TYPE_MESSAGE: {
VALUE val = DEREF(memory, VALUE);
// Lazily expand wrapper type if necessary.
int type = TYPE(val);
if (type != T_DATA && type != T_NIL) {
// This must be a wrapper type.
val = ruby_wrapper_type(type_class, val);
DEREF(memory, VALUE) = val;
}
return val;
}
case UPB_TYPE_ENUM: {
int32_t val = DEREF(memory, int32_t);
VALUE symbol = enum_lookup(type_class, INT2NUM(val));
@ -372,7 +384,8 @@ void native_slot_dup(upb_fieldtype_t type, void* to, void* from) {
memcpy(to, from, native_slot_size(type));
}
void native_slot_deep_copy(upb_fieldtype_t type, void* to, void* from) {
void native_slot_deep_copy(upb_fieldtype_t type, VALUE type_class, void* to,
void* from) {
switch (type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
@ -382,7 +395,7 @@ void native_slot_deep_copy(upb_fieldtype_t type, void* to, void* from) {
break;
}
case UPB_TYPE_MESSAGE: {
VALUE from_val = DEREF(from, VALUE);
VALUE from_val = native_slot_get(type, type_class, from);
DEREF(to, VALUE) = (from_val != Qnil) ?
Message_deep_copy(from_val) : Qnil;
break;
@ -392,13 +405,14 @@ void native_slot_deep_copy(upb_fieldtype_t type, void* to, void* from) {
}
}
bool native_slot_eq(upb_fieldtype_t type, void* mem1, void* mem2) {
bool native_slot_eq(upb_fieldtype_t type, VALUE type_class, void* mem1,
void* mem2) {
switch (type) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE: {
VALUE val1 = DEREF(mem1, VALUE);
VALUE val2 = DEREF(mem2, VALUE);
VALUE val1 = native_slot_get(type, type_class, mem1);
VALUE val2 = native_slot_get(type, type_class, mem2);
VALUE ret = rb_funcall(val1, rb_intern("=="), 1, val2);
return ret == Qtrue;
}
@ -1025,7 +1039,9 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) {
if (slot_read_oneof_case(layout, from, oneof) ==
upb_fielddef_number(field)) {
*to_oneof_case = *from_oneof_case;
native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory);
native_slot_deep_copy(upb_fielddef_type(field),
field_type_class(layout, field), to_memory,
from_memory);
}
} else if (is_map_field(field)) {
DEREF(to_memory, VALUE) =
@ -1039,7 +1055,9 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) {
slot_set_hasbit(layout, to, field);
}
native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory);
native_slot_deep_copy(upb_fielddef_type(field),
field_type_class(layout, field), to_memory,
from_memory);
}
}
}
@ -1061,7 +1079,8 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) {
if (*msg1_oneof_case != *msg2_oneof_case ||
(slot_read_oneof_case(layout, msg1, oneof) ==
upb_fielddef_number(field) &&
!native_slot_eq(upb_fielddef_type(field), msg1_memory,
!native_slot_eq(upb_fielddef_type(field),
field_type_class(layout, field), msg1_memory,
msg2_memory))) {
return Qfalse;
}
@ -1078,7 +1097,9 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) {
} else {
if (slot_is_hasbit_set(layout, msg1, field) !=
slot_is_hasbit_set(layout, msg2, field) ||
!native_slot_eq(upb_fielddef_type(field), msg1_memory, msg2_memory)) {
!native_slot_eq(upb_fielddef_type(field),
field_type_class(layout, field), msg1_memory,
msg2_memory)) {
return Qfalse;
}
}

@ -234,6 +234,48 @@ module BasicTest
m.map_string_int32['aaa'] = 3
end
def test_map_wrappers
run_asserts = ->(m) {
assert_equal 2.0, m.map_double[0].value
assert_equal 4.0, m.map_float[0].value
assert_equal 3, m.map_int32[0].value
assert_equal 4, m.map_int64[0].value
assert_equal 5, m.map_uint32[0].value
assert_equal 6, m.map_uint64[0].value
assert_equal true, m.map_bool[0].value
assert_equal 'str', m.map_string[0].value
assert_equal 'fun', m.map_bytes[0].value
}
m = proto_module::Wrapper.new(
map_double: {0 => Google::Protobuf::DoubleValue.new(value: 2.0)},
map_float: {0 => Google::Protobuf::FloatValue.new(value: 4.0)},
map_int32: {0 => Google::Protobuf::Int32Value.new(value: 3)},
map_int64: {0 => Google::Protobuf::Int64Value.new(value: 4)},
map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 5)},
map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 6)},
map_bool: {0 => Google::Protobuf::BoolValue.new(value: true)},
map_string: {0 => Google::Protobuf::StringValue.new(value: 'str')},
map_bytes: {0 => Google::Protobuf::BytesValue.new(value: 'fun')},
)
run_asserts.call(m)
serialized = proto_module::Wrapper::encode(m)
m2 = proto_module::Wrapper::decode(serialized)
run_asserts.call(m2)
# Test the case where we are serializing directly from the parsed form
# (before anything lazy is materialized).
m3 = proto_module::Wrapper::decode(serialized)
serialized2 = proto_module::Wrapper::encode(m3)
m4 = proto_module::Wrapper::decode(serialized2)
run_asserts.call(m4)
# Test that the lazy form compares equal to the expanded form.
m5 = proto_module::Wrapper::decode(serialized2)
assert_equal m5, m
end
def test_concurrent_decoding
o = Outer.new
o.items[0] = Inner.new

@ -126,7 +126,46 @@ message Wrapper {
google.protobuf.BytesValue bytes = 9;
string real_string = 100;
oneof a_oneof {
string oneof_string = 10;
string string_in_oneof = 10;
}
// Repeated wrappers don't make sense, but we still need to make sure they
// work and don't crash.
repeated google.protobuf.DoubleValue repeated_double = 11;
repeated google.protobuf.FloatValue repeated_float = 12;
repeated google.protobuf.Int32Value repeated_int32 = 13;
repeated google.protobuf.Int64Value repeated_int64 = 14;
repeated google.protobuf.UInt32Value repeated_uint32 = 15;
repeated google.protobuf.UInt64Value repeated_uint64 = 16;
repeated google.protobuf.BoolValue repeated_bool = 17;
repeated google.protobuf.StringValue repeated_string = 18;
repeated google.protobuf.BytesValue repeated_bytes = 19;
// Wrappers as map keys don't make sense, but we still need to make sure they
// work and don't crash.
map<int32, google.protobuf.DoubleValue> map_double = 21;
map<int32, google.protobuf.FloatValue> map_float = 22;
map<int32, google.protobuf.Int32Value> map_int32 = 23;
map<int32, google.protobuf.Int64Value> map_int64 = 24;
map<int32, google.protobuf.UInt32Value> map_uint32 = 25;
map<int32, google.protobuf.UInt64Value> map_uint64 = 26;
map<int32, google.protobuf.BoolValue> map_bool = 27;
map<int32, google.protobuf.StringValue> map_string = 28;
map<int32, google.protobuf.BytesValue> map_bytes = 29;
// Wrappers in oneofs don't make sense, but we still need to make sure they
// work and don't crash.
oneof wrapper_oneof {
google.protobuf.DoubleValue oneof_double = 31;
google.protobuf.FloatValue oneof_float = 32;
google.protobuf.Int32Value oneof_int32 = 33;
google.protobuf.Int64Value oneof_int64 = 34;
google.protobuf.UInt32Value oneof_uint32 = 35;
google.protobuf.UInt64Value oneof_uint64 = 36;
google.protobuf.BoolValue oneof_bool = 37;
google.protobuf.StringValue oneof_string = 38;
google.protobuf.BytesValue oneof_bytes = 39;
string oneof_plain_string = 101;
}
}

@ -133,7 +133,34 @@ message Wrapper {
optional google.protobuf.BytesValue bytes = 9;
optional string real_string = 100;
oneof a_oneof {
string oneof_string = 10;
string string_in_oneof = 10;
}
// Repeated wrappers don't really make sense, but we still need to make sure
// they work and don't crash.
repeated google.protobuf.DoubleValue repeated_double = 11;
repeated google.protobuf.FloatValue repeated_float = 12;
repeated google.protobuf.Int32Value repeated_int32 = 13;
repeated google.protobuf.Int64Value repeated_int64 = 14;
repeated google.protobuf.UInt32Value repeated_uint32 = 15;
repeated google.protobuf.UInt64Value repeated_uint64 = 16;
repeated google.protobuf.BoolValue repeated_bool = 17;
repeated google.protobuf.StringValue repeated_string = 18;
repeated google.protobuf.BytesValue repeated_bytes = 19;
// Wrappers in oneofs don't make sense, but we still need to make sure they
// work and don't crash.
oneof wrapper_oneof {
google.protobuf.DoubleValue oneof_double = 31;
google.protobuf.FloatValue oneof_float = 32;
google.protobuf.Int32Value oneof_int32 = 33;
google.protobuf.Int64Value oneof_int64 = 34;
google.protobuf.UInt32Value oneof_uint32 = 35;
google.protobuf.UInt64Value oneof_uint64 = 36;
google.protobuf.BoolValue oneof_bool = 37;
google.protobuf.StringValue oneof_string = 38;
google.protobuf.BytesValue oneof_bytes = 39;
string oneof_plain_string = 101;
}
}

@ -1266,6 +1266,44 @@ module CommonTests
end
def test_wrapper_getters
run_asserts = ->(m) {
assert_equal 2.0, m.double_as_value
assert_equal 2.0, m.double.value
assert_equal 2.0, m.double_as_value
assert_equal 4.0, m.float_as_value
assert_equal 4.0, m.float.value
assert_equal 4.0, m.float_as_value
assert_equal 3, m.int32_as_value
assert_equal 3, m.int32.value
assert_equal 3, m.int32_as_value
assert_equal 4, m.int64_as_value
assert_equal 4, m.int64.value
assert_equal 4, m.int64_as_value
assert_equal 5, m.uint32_as_value
assert_equal 5, m.uint32.value
assert_equal 5, m.uint32_as_value
assert_equal 6, m.uint64_as_value
assert_equal 6, m.uint64.value
assert_equal 6, m.uint64_as_value
assert_equal true, m.bool_as_value
assert_equal true, m.bool.value
assert_equal true, m.bool_as_value
assert_equal "st\nr", m.string_as_value
assert_equal "st\nr", m.string.value
assert_equal "st\nr", m.string_as_value
assert_equal 'fun', m.bytes_as_value
assert_equal 'fun', m.bytes.value
assert_equal 'fun', m.bytes_as_value
}
m = proto_module::Wrapper.new(
double: Google::Protobuf::DoubleValue.new(value: 2.0),
float: Google::Protobuf::FloatValue.new(value: 4.0),
@ -1274,158 +1312,319 @@ module CommonTests
uint32: Google::Protobuf::UInt32Value.new(value: 5),
uint64: Google::Protobuf::UInt64Value.new(value: 6),
bool: Google::Protobuf::BoolValue.new(value: true),
string: Google::Protobuf::StringValue.new(value: 'str'),
string: Google::Protobuf::StringValue.new(value: "st\nr"),
bytes: Google::Protobuf::BytesValue.new(value: 'fun'),
real_string: '100'
)
assert_equal 2.0, m.double_as_value
assert_equal 2.0, m.double.value
assert_equal 4.0, m.float_as_value
assert_equal 4.0, m.float.value
assert_equal 3, m.int32_as_value
assert_equal 3, m.int32.value
assert_equal 4, m.int64_as_value
assert_equal 4, m.int64.value
assert_equal 5, m.uint32_as_value
assert_equal 5, m.uint32.value
assert_equal 6, m.uint64_as_value
assert_equal 6, m.uint64.value
assert_equal true, m.bool_as_value
assert_equal true, m.bool.value
assert_equal 'str', m.string_as_value
assert_equal 'str', m.string.value
assert_equal 'fun', m.bytes_as_value
assert_equal 'fun', m.bytes.value
run_asserts.call(m)
serialized = proto_module::Wrapper::encode(m)
m2 = proto_module::Wrapper::decode(serialized)
run_asserts.call(m2)
# Test the case where we are serializing directly from the parsed form
# (before anything lazy is materialized).
m3 = proto_module::Wrapper::decode(serialized)
serialized2 = proto_module::Wrapper::encode(m3)
m4 = proto_module::Wrapper::decode(serialized2)
run_asserts.call(m4)
# Test that the lazy form compares equal to the expanded form.
m5 = proto_module::Wrapper::decode(serialized2)
assert_equal m5, m
serialized_json = proto_module::Wrapper::encode_json(m)
m6 = proto_module::Wrapper::decode_json(serialized_json)
assert_equal m6, m
end
def test_repeated_wrappers
run_asserts = ->(m) {
assert_equal 2.0, m.repeated_double[0].value
assert_equal 4.0, m.repeated_float[0].value
assert_equal 3, m.repeated_int32[0].value
assert_equal 4, m.repeated_int64[0].value
assert_equal 5, m.repeated_uint32[0].value
assert_equal 6, m.repeated_uint64[0].value
assert_equal true, m.repeated_bool[0].value
assert_equal 'str', m.repeated_string[0].value
assert_equal 'fun', m.repeated_bytes[0].value
}
m = proto_module::Wrapper.new(
repeated_double: [Google::Protobuf::DoubleValue.new(value: 2.0)],
repeated_float: [Google::Protobuf::FloatValue.new(value: 4.0)],
repeated_int32: [Google::Protobuf::Int32Value.new(value: 3)],
repeated_int64: [Google::Protobuf::Int64Value.new(value: 4)],
repeated_uint32: [Google::Protobuf::UInt32Value.new(value: 5)],
repeated_uint64: [Google::Protobuf::UInt64Value.new(value: 6)],
repeated_bool: [Google::Protobuf::BoolValue.new(value: true)],
repeated_string: [Google::Protobuf::StringValue.new(value: 'str')],
repeated_bytes: [Google::Protobuf::BytesValue.new(value: 'fun')],
)
run_asserts.call(m)
serialized = proto_module::Wrapper::encode(m)
m2 = proto_module::Wrapper::decode(serialized)
run_asserts.call(m2)
# Test the case where we are serializing directly from the parsed form
# (before anything lazy is materialized).
m3 = proto_module::Wrapper::decode(serialized)
serialized2 = proto_module::Wrapper::encode(m3)
m4 = proto_module::Wrapper::decode(serialized2)
run_asserts.call(m4)
# Test that the lazy form compares equal to the expanded form.
m5 = proto_module::Wrapper::decode(serialized2)
assert_equal m5, m
# Test JSON.
serialized_json = proto_module::Wrapper::encode_json(m5)
m6 = proto_module::Wrapper::decode_json(serialized_json)
run_asserts.call(m6)
assert_equal m6, m
end
def test_oneof_wrappers
run_test = ->(m) {
serialized = proto_module::Wrapper::encode(m)
m2 = proto_module::Wrapper::decode(serialized)
# Encode directly from lazy form.
serialized2 = proto_module::Wrapper::encode(m2)
assert_equal m, m2
assert_equal serialized, serialized2
serialized_json = proto_module::Wrapper::encode_json(m)
m3 = proto_module::Wrapper::decode_json(serialized_json)
assert_equal m, m3
}
m = proto_module::Wrapper.new()
run_test.call(m)
m.oneof_double_as_value = 2.0
run_test.call(m)
m.oneof_float_as_value = 4.0
run_test.call(m)
m.oneof_int32_as_value = 3
run_test.call(m)
m.oneof_int64_as_value = 5
run_test.call(m)
m.oneof_uint32_as_value = 6
run_test.call(m)
m.oneof_uint64_as_value = 7
run_test.call(m)
m.oneof_string_as_value = 'str'
run_test.call(m)
m.oneof_bytes_as_value = 'fun'
run_test.call(m)
end
def test_top_level_wrappers
# We don't expect anyone to do this, but we should also make sure it does
# the right thing.
run_test = ->(klass, val) {
m = klass.new(value: val)
serialized = klass::encode(m)
m2 = klass::decode(serialized)
# Encode directly from lazy form.
serialized2 = klass::encode(m2)
assert_equal m, m2
assert_equal serialized, serialized2
serialized_json = klass::encode_json(m)
# This is nonsensical to do and does not work. There is no good reason
# to parse a wrapper type directly.
assert_raise(RuntimeError) { klass::decode_json(serialized_json) }
}
run_test.call(Google::Protobuf::DoubleValue, 2.0)
run_test.call(Google::Protobuf::FloatValue, 4.0)
run_test.call(Google::Protobuf::Int32Value, 3)
run_test.call(Google::Protobuf::Int64Value, 4)
run_test.call(Google::Protobuf::UInt32Value, 5)
run_test.call(Google::Protobuf::UInt64Value, 6)
run_test.call(Google::Protobuf::BoolValue, true)
run_test.call(Google::Protobuf::StringValue, 'str')
run_test.call(Google::Protobuf::BytesValue, 'fun')
end
def test_wrapper_setters_as_value
run_asserts = ->(m) {
m.double_as_value = 4.8
assert_equal 4.8, m.double_as_value
assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
m.float_as_value = 2.4
assert_in_delta 2.4, m.float_as_value
assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
m.int32_as_value = 5
assert_equal 5, m.int32_as_value
assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
m.int64_as_value = 15
assert_equal 15, m.int64_as_value
assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
m.uint32_as_value = 50
assert_equal 50, m.uint32_as_value
assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
m.uint64_as_value = 500
assert_equal 500, m.uint64_as_value
assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
m.bool_as_value = false
assert_equal false, m.bool_as_value
assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
m.string_as_value = 'xy'
assert_equal 'xy', m.string_as_value
assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
m.bytes_as_value = '123'
assert_equal '123', m.bytes_as_value
assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
m.double_as_value = nil
assert_nil m.double
assert_nil m.double_as_value
m.float_as_value = nil
assert_nil m.float
assert_nil m.float_as_value
m.int32_as_value = nil
assert_nil m.int32
assert_nil m.int32_as_value
m.int64_as_value = nil
assert_nil m.int64
assert_nil m.int64_as_value
m.uint32_as_value = nil
assert_nil m.uint32
assert_nil m.uint32_as_value
m.uint64_as_value = nil
assert_nil m.uint64
assert_nil m.uint64_as_value
m.bool_as_value = nil
assert_nil m.bool
assert_nil m.bool_as_value
m.string_as_value = nil
assert_nil m.string
assert_nil m.string_as_value
m.bytes_as_value = nil
assert_nil m.bytes
assert_nil m.bytes_as_value
}
m = proto_module::Wrapper.new
m.double_as_value = 4.8
assert_equal 4.8, m.double_as_value
assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
m.float_as_value = 2.4
assert_in_delta 2.4, m.float_as_value
assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
m.int32_as_value = 5
assert_equal 5, m.int32_as_value
assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
m.int64_as_value = 15
assert_equal 15, m.int64_as_value
assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
m.uint32_as_value = 50
assert_equal 50, m.uint32_as_value
assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
m.uint64_as_value = 500
assert_equal 500, m.uint64_as_value
assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
m.bool_as_value = false
assert_equal false, m.bool_as_value
assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
m.string_as_value = 'xy'
assert_equal 'xy', m.string_as_value
assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
m.bytes_as_value = '123'
assert_equal '123', m.bytes_as_value
assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
m.double_as_value = nil
assert_nil m.double
assert_nil m.double_as_value
m.float_as_value = nil
assert_nil m.float
assert_nil m.float_as_value
m.int32_as_value = nil
assert_nil m.int32
assert_nil m.int32_as_value
m.int64_as_value = nil
assert_nil m.int64
assert_nil m.int64_as_value
m.uint32_as_value = nil
assert_nil m.uint32
assert_nil m.uint32_as_value
m.uint64_as_value = nil
assert_nil m.uint64
assert_nil m.uint64_as_value
m.bool_as_value = nil
assert_nil m.bool
assert_nil m.bool_as_value
m.string_as_value = nil
assert_nil m.string
assert_nil m.string_as_value
m.bytes_as_value = nil
assert_nil m.bytes
assert_nil m.bytes_as_value
m2 = proto_module::Wrapper.new(
double: Google::Protobuf::DoubleValue.new(value: 2.0),
float: Google::Protobuf::FloatValue.new(value: 4.0),
int32: Google::Protobuf::Int32Value.new(value: 3),
int64: Google::Protobuf::Int64Value.new(value: 4),
uint32: Google::Protobuf::UInt32Value.new(value: 5),
uint64: Google::Protobuf::UInt64Value.new(value: 6),
bool: Google::Protobuf::BoolValue.new(value: true),
string: Google::Protobuf::StringValue.new(value: 'str'),
bytes: Google::Protobuf::BytesValue.new(value: 'fun'),
real_string: '100'
)
run_asserts.call(m2)
serialized = proto_module::Wrapper::encode(m2)
m3 = proto_module::Wrapper::decode(serialized)
run_asserts.call(m3)
end
def test_wrapper_setters
run_asserts = ->(m) {
m.double = Google::Protobuf::DoubleValue.new(value: 4.8)
assert_equal 4.8, m.double_as_value
assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
m.float = Google::Protobuf::FloatValue.new(value: 2.4)
assert_in_delta 2.4, m.float_as_value
assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
m.int32 = Google::Protobuf::Int32Value.new(value: 5)
assert_equal 5, m.int32_as_value
assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
m.int64 = Google::Protobuf::Int64Value.new(value: 15)
assert_equal 15, m.int64_as_value
assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
m.uint32 = Google::Protobuf::UInt32Value.new(value: 50)
assert_equal 50, m.uint32_as_value
assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
m.uint64 = Google::Protobuf::UInt64Value.new(value: 500)
assert_equal 500, m.uint64_as_value
assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
m.bool = Google::Protobuf::BoolValue.new(value: false)
assert_equal false, m.bool_as_value
assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
m.string = Google::Protobuf::StringValue.new(value: 'xy')
assert_equal 'xy', m.string_as_value
assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
m.bytes = Google::Protobuf::BytesValue.new(value: '123')
assert_equal '123', m.bytes_as_value
assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
m.double = nil
assert_nil m.double
assert_nil m.double_as_value
m.float = nil
assert_nil m.float
assert_nil m.float_as_value
m.int32 = nil
assert_nil m.int32
assert_nil m.int32_as_value
m.int64 = nil
assert_nil m.int64
assert_nil m.int64_as_value
m.uint32 = nil
assert_nil m.uint32
assert_nil m.uint32_as_value
m.uint64 = nil
assert_nil m.uint64
assert_nil m.uint64_as_value
m.bool = nil
assert_nil m.bool
assert_nil m.bool_as_value
m.string = nil
assert_nil m.string
assert_nil m.string_as_value
m.bytes = nil
assert_nil m.bytes
assert_nil m.bytes_as_value
}
m = proto_module::Wrapper.new
run_asserts.call(m)
m2 = proto_module::Wrapper.new(
double: Google::Protobuf::DoubleValue.new(value: 2.0),
float: Google::Protobuf::FloatValue.new(value: 4.0),
int32: Google::Protobuf::Int32Value.new(value: 3),
int64: Google::Protobuf::Int64Value.new(value: 4),
uint32: Google::Protobuf::UInt32Value.new(value: 5),
uint64: Google::Protobuf::UInt64Value.new(value: 6),
bool: Google::Protobuf::BoolValue.new(value: true),
string: Google::Protobuf::StringValue.new(value: 'str'),
bytes: Google::Protobuf::BytesValue.new(value: 'fun'),
real_string: '100'
)
run_asserts.call(m2)
m.double = Google::Protobuf::DoubleValue.new(value: 4.8)
assert_equal 4.8, m.double_as_value
assert_equal Google::Protobuf::DoubleValue.new(value: 4.8), m.double
m.float = Google::Protobuf::FloatValue.new(value: 2.4)
assert_in_delta 2.4, m.float_as_value
assert_in_delta Google::Protobuf::FloatValue.new(value: 2.4).value, m.float.value
m.int32 = Google::Protobuf::Int32Value.new(value: 5)
assert_equal 5, m.int32_as_value
assert_equal Google::Protobuf::Int32Value.new(value: 5), m.int32
m.int64 = Google::Protobuf::Int64Value.new(value: 15)
assert_equal 15, m.int64_as_value
assert_equal Google::Protobuf::Int64Value.new(value: 15), m.int64
m.uint32 = Google::Protobuf::UInt32Value.new(value: 50)
assert_equal 50, m.uint32_as_value
assert_equal Google::Protobuf::UInt32Value.new(value: 50), m.uint32
m.uint64 = Google::Protobuf::UInt64Value.new(value: 500)
assert_equal 500, m.uint64_as_value
assert_equal Google::Protobuf::UInt64Value.new(value: 500), m.uint64
m.bool = Google::Protobuf::BoolValue.new(value: false)
assert_equal false, m.bool_as_value
assert_equal Google::Protobuf::BoolValue.new(value: false), m.bool
m.string = Google::Protobuf::StringValue.new(value: 'xy')
assert_equal 'xy', m.string_as_value
assert_equal Google::Protobuf::StringValue.new(value: 'xy'), m.string
m.bytes = Google::Protobuf::BytesValue.new(value: '123')
assert_equal '123', m.bytes_as_value
assert_equal Google::Protobuf::BytesValue.new(value: '123'), m.bytes
m.double = nil
assert_nil m.double
assert_nil m.double_as_value
m.float = nil
assert_nil m.float
assert_nil m.float_as_value
m.int32 = nil
assert_nil m.int32
assert_nil m.int32_as_value
m.int64 = nil
assert_nil m.int64
assert_nil m.int64_as_value
m.uint32 = nil
assert_nil m.uint32
assert_nil m.uint32_as_value
m.uint64 = nil
assert_nil m.uint64
assert_nil m.uint64_as_value
m.bool = nil
assert_nil m.bool
assert_nil m.bool_as_value
m.string = nil
assert_nil m.string
assert_nil m.string_as_value
m.bytes = nil
assert_nil m.bytes
assert_nil m.bytes_as_value
serialized = proto_module::Wrapper::encode(m2)
m3 = proto_module::Wrapper::decode(serialized)
run_asserts.call(m3)
end
def test_wrappers_only
m = proto_module::Wrapper.new(real_string: 'hi', oneof_string: 'there')
m = proto_module::Wrapper.new(real_string: 'hi', string_in_oneof: 'there')
assert_raise(NoMethodError) { m.real_string_as_value }
assert_raise(NoMethodError) { m.as_value }
assert_raise(NoMethodError) { m._as_value }
assert_raise(NoMethodError) { m.oneof_string_as_value }
assert_raise(NoMethodError) { m.string_in_oneof_as_value }
m = proto_module::Wrapper.new
m.string_as_value = 'you'

Loading…
Cancel
Save