diff options
author | Chris Fallin <cfallin@c1f.net> | 2015-01-06 15:44:09 -0800 |
---|---|---|
committer | Chris Fallin <cfallin@c1f.net> | 2015-01-06 15:44:09 -0800 |
commit | fd1a3ff11d5854c34ba66c63598cdc5fd234e399 (patch) | |
tree | bac4a2445906d444a132004761f6b8e1693a9124 /ruby/ext/google/protobuf_c/storage.c | |
parent | 644a6a1da71385e9d7a7a26b3476c93fdd71788c (diff) | |
download | protobuf-fd1a3ff11d5854c34ba66c63598cdc5fd234e399.tar.gz protobuf-fd1a3ff11d5854c34ba66c63598cdc5fd234e399.tar.bz2 protobuf-fd1a3ff11d5854c34ba66c63598cdc5fd234e399.zip |
Support for maps in the MRI C Ruby extension.
This adds the Map container and support for parsing and serializing maps
in the protobuf wire format (as defined by the C++ implementation, with
MapEntry submessages in a repeated field). JSON map
serialization/parsing are not yet supported as these will require some
changes to upb as well.
Diffstat (limited to 'ruby/ext/google/protobuf_c/storage.c')
-rw-r--r-- | ruby/ext/google/protobuf_c/storage.c | 173 |
1 files changed, 135 insertions, 38 deletions
diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index c4d801af..f20ddec2 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -57,7 +57,17 @@ size_t native_slot_size(upb_fieldtype_t type) { } } -static void check_int_range_precision(upb_fieldtype_t type, VALUE val) { +static bool is_ruby_num(VALUE value) { + return (TYPE(value) == T_FLOAT || + TYPE(value) == T_FIXNUM || + TYPE(value) == T_BIGNUM); +} + +void native_slot_check_int_range_precision(upb_fieldtype_t type, VALUE val) { + if (!is_ruby_num(val)) { + rb_raise(rb_eTypeError, "Expected number type for integral field."); + } + // NUM2{INT,UINT,LL,ULL} macros do the appropriate range checks on upper // bound; we just need to do precision checks (i.e., disallow rounding) and // check for < 0 on unsigned types. @@ -76,12 +86,6 @@ static void check_int_range_precision(upb_fieldtype_t type, VALUE val) { } } -static bool is_ruby_num(VALUE value) { - return (TYPE(value) == T_FLOAT || - TYPE(value) == T_FIXNUM || - TYPE(value) == T_BIGNUM); -} - void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value) { bool bad_encoding = false; rb_encoding* string_encoding = rb_enc_from_index(ENCODING_GET(value)); @@ -156,14 +160,14 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, int32_t int_val = 0; if (TYPE(value) == T_SYMBOL) { // Ensure that the given symbol exists in the enum module. - VALUE lookup = rb_const_get(type_class, SYM2ID(value)); + VALUE lookup = rb_funcall(type_class, rb_intern("resolve"), 1, value); if (lookup == Qnil) { rb_raise(rb_eRangeError, "Unknown symbol value for enum field."); } else { int_val = NUM2INT(lookup); } } else { - check_int_range_precision(UPB_TYPE_INT32, value); + native_slot_check_int_range_precision(UPB_TYPE_INT32, value); int_val = NUM2INT(value); } DEREF(memory, int32_t) = int_val; @@ -173,10 +177,7 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class, case UPB_TYPE_INT64: case UPB_TYPE_UINT32: case UPB_TYPE_UINT64: - if (!is_ruby_num(value)) { - rb_raise(rb_eTypeError, "Expected number type for integral field."); - } - check_int_range_precision(type, value); + native_slot_check_int_range_precision(type, value); switch (type) { case UPB_TYPE_INT32: DEREF(memory, int32_t) = NUM2INT(value); @@ -246,8 +247,9 @@ void native_slot_init(upb_fieldtype_t type, void* memory) { break; case UPB_TYPE_STRING: case UPB_TYPE_BYTES: - // TODO(cfallin): set encoding appropriately DEREF(memory, VALUE) = rb_str_new2(""); + rb_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES) ? + kRubyString8bitEncoding : kRubyStringUtf8Encoding); break; case UPB_TYPE_MESSAGE: DEREF(memory, VALUE) = Qnil; @@ -322,6 +324,35 @@ bool native_slot_eq(upb_fieldtype_t type, void* mem1, void* mem2) { } // ----------------------------------------------------------------------------- +// Map field utilities. +// ----------------------------------------------------------------------------- + +bool is_map_field(const upb_fielddef* field) { + if (upb_fielddef_label(field) != UPB_LABEL_REPEATED || + upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { + return false; + } + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + return upb_msgdef_mapentry(subdef); +} + +const upb_fielddef* map_field_key(const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_fielddef* key_field = upb_msgdef_itof(subdef, 1); + assert(key_field != NULL); + return key_field; +} + +const upb_fielddef* map_field_value(const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_msgdef* subdef = (const upb_msgdef*)upb_fielddef_subdef(field); + const upb_fielddef* value_field = upb_msgdef_itof(subdef, 2); + assert(value_field != NULL); + return value_field; +} + +// ----------------------------------------------------------------------------- // Memory layout management. // ----------------------------------------------------------------------------- @@ -334,9 +365,12 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) { size_t off = 0; for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - size_t field_size = - (upb_fielddef_label(field) == UPB_LABEL_REPEATED) ? - sizeof(VALUE) : native_slot_size(upb_fielddef_type(field)); + size_t field_size = 0; + if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + field_size = sizeof(VALUE); + } else { + field_size = native_slot_size(upb_fielddef_type(field)); + } // align current offset off = (off + field_size - 1) & ~(field_size - 1); layout->offsets[upb_fielddef_index(field)] = off; @@ -357,7 +391,7 @@ void free_layout(MessageLayout* layout) { xfree(layout); } -static VALUE get_type_class(const upb_fielddef* field) { +VALUE field_type_class(const upb_fielddef* field) { VALUE type_class = Qnil; if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) { VALUE submsgdesc = @@ -380,7 +414,7 @@ VALUE layout_get(MessageLayout* layout, return *((VALUE *)memory); } else { return native_slot_get(upb_fielddef_type(field), - get_type_class(field), + field_type_class(field), memory); } } @@ -398,9 +432,8 @@ static void check_repeated_field_type(VALUE val, const upb_fielddef* field) { rb_raise(rb_eTypeError, "Repeated field array has wrong element type"); } - if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE || - upb_fielddef_type(field) == UPB_TYPE_ENUM) { - RepeatedField* self = ruby_to_RepeatedField(val); + if (self->field_type == UPB_TYPE_MESSAGE || + self->field_type == UPB_TYPE_ENUM) { if (self->field_type_class != get_def_obj(upb_fielddef_subdef(field))) { rb_raise(rb_eTypeError, @@ -409,17 +442,48 @@ static void check_repeated_field_type(VALUE val, const upb_fielddef* field) { } } +static void check_map_field_type(VALUE val, const upb_fielddef* field) { + assert(is_map_field(field)); + const upb_fielddef* key_field = map_field_key(field); + const upb_fielddef* value_field = map_field_value(field); + + if (!RB_TYPE_P(val, T_DATA) || !RTYPEDDATA_P(val) || + RTYPEDDATA_TYPE(val) != &Map_type) { + rb_raise(rb_eTypeError, "Expected Map instance"); + } + + Map* self = ruby_to_Map(val); + if (self->key_type != upb_fielddef_type(key_field)) { + rb_raise(rb_eTypeError, "Map key type does not match field's key type"); + } + if (self->value_type != upb_fielddef_type(value_field)) { + rb_raise(rb_eTypeError, "Map value type does not match field's value type"); + } + if (upb_fielddef_type(value_field) == UPB_TYPE_MESSAGE || + upb_fielddef_type(value_field) == UPB_TYPE_ENUM) { + if (self->value_type_class != + get_def_obj(upb_fielddef_subdef(value_field))) { + rb_raise(rb_eTypeError, + "Map value type has wrong message/enum class"); + } + } +} + + void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field, VALUE val) { void* memory = ((uint8_t *)storage) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + if (is_map_field(field)) { + check_map_field_type(val, field); + DEREF(memory, VALUE) = val; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { check_repeated_field_type(val, field); - *((VALUE *)memory) = val; + DEREF(memory, VALUE) = val; } else { - native_slot_set(upb_fielddef_type(field), get_type_class(field), + native_slot_set(upb_fielddef_type(field), field_type_class(field), memory, val); } } @@ -434,9 +498,34 @@ void layout_init(MessageLayout* layout, void* memory = ((uint8_t *)storage) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + if (is_map_field(field)) { + VALUE map = Qnil; + + const upb_fielddef* key_field = map_field_key(field); + const upb_fielddef* value_field = map_field_value(field); + VALUE type_class = field_type_class(value_field); + + if (type_class != Qnil) { + VALUE args[3] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + type_class, + }; + map = rb_class_new_instance(3, args, cMap); + } else { + VALUE args[2] = { + fieldtype_to_ruby(upb_fielddef_type(key_field)), + fieldtype_to_ruby(upb_fielddef_type(value_field)), + }; + map = rb_class_new_instance(2, args, cMap); + } + + DEREF(memory, VALUE) = map; + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { VALUE ary = Qnil; - VALUE type_class = get_type_class(field); + + VALUE type_class = field_type_class(field); + if (type_class != Qnil) { VALUE args[2] = { fieldtype_to_ruby(upb_fielddef_type(field)), @@ -447,7 +536,8 @@ void layout_init(MessageLayout* layout, VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) }; ary = rb_class_new_instance(1, args, cRepeatedField); } - *((VALUE *)memory) = ary; + + DEREF(memory, VALUE) = ary; } else { native_slot_init(upb_fielddef_type(field), memory); } @@ -464,7 +554,7 @@ void layout_mark(MessageLayout* layout, void* storage) { layout->offsets[upb_fielddef_index(field)]; if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - rb_gc_mark(*((VALUE *)memory)); + rb_gc_mark(DEREF(memory, VALUE)); } else { native_slot_mark(upb_fielddef_type(field), memory); } @@ -482,8 +572,10 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { void* from_memory = ((uint8_t *)from) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - *((VALUE *)to_memory) = RepeatedField_dup(*((VALUE *)from_memory)); + if (is_map_field(field)) { + DEREF(to_memory, VALUE) = Map_dup(DEREF(from_memory, VALUE)); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + DEREF(to_memory, VALUE) = RepeatedField_dup(DEREF(from_memory, VALUE)); } else { native_slot_dup(upb_fielddef_type(field), to_memory, from_memory); } @@ -501,8 +593,12 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { void* from_memory = ((uint8_t *)from) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - *((VALUE *)to_memory) = RepeatedField_deep_copy(*((VALUE *)from_memory)); + if (is_map_field(field)) { + DEREF(to_memory, VALUE) = + Map_deep_copy(DEREF(from_memory, VALUE)); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + DEREF(to_memory, VALUE) = + RepeatedField_deep_copy(DEREF(from_memory, VALUE)); } else { native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory); } @@ -520,11 +616,12 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { void* msg2_memory = ((uint8_t *)msg2) + layout->offsets[upb_fielddef_index(field)]; - if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { - if (RepeatedField_eq(*((VALUE *)msg1_memory), - *((VALUE *)msg2_memory)) == Qfalse) { - return Qfalse; - } + if (is_map_field(field)) { + return Map_eq(DEREF(msg1_memory, VALUE), + DEREF(msg2_memory, VALUE)); + } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { + return RepeatedField_eq(DEREF(msg1_memory, VALUE), + DEREF(msg2_memory, VALUE)); } else { if (!native_slot_eq(upb_fielddef_type(field), msg1_memory, msg2_memory)) { |