aboutsummaryrefslogtreecommitdiff
path: root/ruby/ext/google/protobuf_c/storage.c
diff options
context:
space:
mode:
authorHarshit Chopra <harshit@squareup.com>2017-08-25 12:11:15 -0700
committerHarshit Chopra <harshit@squareup.com>2018-09-27 14:21:16 -0400
commitd0535cc09e6eac1bddddd51c20b5738c0e841765 (patch)
treef712febe270ba656970f65b952114e14650103b6 /ruby/ext/google/protobuf_c/storage.c
parent048f5c26a783f5f92061aec3aab19986e5c8d435 (diff)
downloadprotobuf-d0535cc09e6eac1bddddd51c20b5738c0e841765.tar.gz
protobuf-d0535cc09e6eac1bddddd51c20b5738c0e841765.tar.bz2
protobuf-d0535cc09e6eac1bddddd51c20b5738c0e841765.zip
Adds support for proto2 syntax for Ruby gem.
This change only adds basic proto2 support without advanced features like extensions, custom options, maps, etc. The protoc binary now generates ruby code for proto2 syntax. However, for now, it is restricted to proto2 files without advanced features like extensions, in which case it still errors out. This change also modifies the DSL to add proto messages to the DescriptorPool. There is a new DSL Builder#add_file to create a new FileDescriptor. With this, the generated ruby DSL looks something like: Google::Protobuf::DescriptorPool.generated_pool.build do add_file "test.proto" do add_message "foo" do optional :val, :int32, 1 end end end
Diffstat (limited to 'ruby/ext/google/protobuf_c/storage.c')
-rw-r--r--ruby/ext/google/protobuf_c/storage.c281
1 files changed, 194 insertions, 87 deletions
diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c
index 163b2f81..6cf4158b 100644
--- a/ruby/ext/google/protobuf_c/storage.c
+++ b/ruby/ext/google/protobuf_c/storage.c
@@ -38,6 +38,8 @@
// Ruby <-> native slot management.
// -----------------------------------------------------------------------------
+#define CHARPTR_AT(msg, ofs) ((char*)msg + ofs)
+#define DEREF_OFFSET(msg, ofs, type) *(type*)CHARPTR_AT(msg, ofs)
#define DEREF(memory, type) *(type*)(memory)
size_t native_slot_size(upb_fieldtype_t type) {
@@ -57,37 +59,6 @@ size_t native_slot_size(upb_fieldtype_t type) {
}
}
-static VALUE value_from_default(const upb_fielddef *field) {
- switch (upb_fielddef_type(field)) {
- case UPB_TYPE_FLOAT: return DBL2NUM(upb_fielddef_defaultfloat(field));
- case UPB_TYPE_DOUBLE: return DBL2NUM(upb_fielddef_defaultdouble(field));
- case UPB_TYPE_BOOL:
- return upb_fielddef_defaultbool(field) ? Qtrue : Qfalse;
- case UPB_TYPE_MESSAGE: return Qnil;
- case UPB_TYPE_ENUM: {
- const upb_enumdef *enumdef = upb_fielddef_enumsubdef(field);
- int32_t num = upb_fielddef_defaultint32(field);
- const char *label = upb_enumdef_iton(enumdef, num);
- if (label) {
- return ID2SYM(rb_intern(label));
- } else {
- return INT2NUM(num);
- }
- }
- case UPB_TYPE_INT32: return INT2NUM(upb_fielddef_defaultint32(field));
- case UPB_TYPE_INT64: return LL2NUM(upb_fielddef_defaultint64(field));;
- case UPB_TYPE_UINT32: return UINT2NUM(upb_fielddef_defaultuint32(field));
- case UPB_TYPE_UINT64: return ULL2NUM(upb_fielddef_defaultuint64(field));
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES: {
- size_t size;
- const char *str = upb_fielddef_defaultstr(field, &size);
- return rb_str_new(str, size);
- }
- default: return Qnil;
- }
-}
-
static bool is_ruby_num(VALUE value) {
return (TYPE(value) == T_FLOAT ||
TYPE(value) == T_FIXNUM ||
@@ -404,7 +375,12 @@ const upb_msgdef *map_entry_msgdef(const upb_fielddef* field) {
}
bool is_map_field(const upb_fielddef *field) {
- return tryget_map_entry_msgdef(field) != NULL;
+ const upb_msgdef* subdef = tryget_map_entry_msgdef(field);
+ if (subdef == NULL) return false;
+
+ // Map fields are a proto3 feature.
+ // If we're using proto2 syntax we need to fallback to the repeated field.
+ return upb_msgdef_syntax(subdef) == UPB_SYNTAX_PROTO3;
}
const upb_fielddef* map_field_key(const upb_fielddef* field) {
@@ -433,6 +409,12 @@ const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) {
// Memory layout management.
// -----------------------------------------------------------------------------
+bool field_contains_hasbit(MessageLayout* layout,
+ const upb_fielddef* field) {
+ return layout->fields[upb_fielddef_index(field)].hasbit !=
+ MESSAGE_FIELD_NO_HASBIT;
+}
+
static size_t align_up_to(size_t offset, size_t granularity) {
// Granularity must be a power of two.
return (offset + granularity - 1) & ~(granularity - 1);
@@ -447,6 +429,23 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
layout->fields = ALLOC_N(MessageField, nfields);
+ size_t hasbit = 0;
+ for (upb_msg_field_begin(&it, msgdef);
+ !upb_msg_field_done(&it);
+ upb_msg_field_next(&it)) {
+ const upb_fielddef* field = upb_msg_iter_field(&it);
+ if (upb_fielddef_haspresence(field)) {
+ layout->fields[upb_fielddef_index(field)].hasbit = hasbit++;
+ } else {
+ layout->fields[upb_fielddef_index(field)].hasbit =
+ MESSAGE_FIELD_NO_HASBIT;
+ }
+ }
+
+ if (hasbit != 0) {
+ off += (hasbit + 8 - 1) / 8;
+ }
+
for (upb_msg_field_begin(&it, msgdef);
!upb_msg_field_done(&it);
upb_msg_field_next(&it)) {
@@ -569,6 +568,136 @@ static uint32_t* slot_oneof_case(MessageLayout* layout,
layout->fields[upb_fielddef_index(field)].case_offset);
}
+static void slot_set_hasbit(MessageLayout* layout,
+ const void* storage,
+ const upb_fielddef* field) {
+ size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit;
+ assert(hasbit != MESSAGE_FIELD_NO_HASBIT);
+
+ ((uint8_t*)storage)[hasbit / 8] |= 1 << (hasbit % 8);
+}
+
+static void slot_clear_hasbit(MessageLayout* layout,
+ const void* storage,
+ const upb_fielddef* field) {
+ size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit;
+ assert(hasbit != MESSAGE_FIELD_NO_HASBIT);
+ ((uint8_t*)storage)[hasbit / 8] &= ~(1 << (hasbit % 8));
+}
+
+static bool slot_is_hasbit_set(MessageLayout* layout,
+ const void* storage,
+ const upb_fielddef* field) {
+ size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit;
+ if (hasbit == MESSAGE_FIELD_NO_HASBIT) {
+ return false;
+ }
+
+ return DEREF_OFFSET(
+ (uint8_t*)storage, hasbit / 8, char) & (1 << (hasbit % 8));
+}
+
+VALUE layout_has(MessageLayout* layout,
+ const void* storage,
+ const upb_fielddef* field) {
+ assert(field_contains_hasbit(layout, field));
+ return slot_is_hasbit_set(layout, storage, field) ? Qtrue : Qfalse;
+}
+
+void layout_clear(MessageLayout* layout,
+ const void* storage,
+ const upb_fielddef* field) {
+ void* memory = slot_memory(layout, storage, field);
+ uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
+
+ if (field_contains_hasbit(layout, field)) {
+ slot_clear_hasbit(layout, storage, field);
+ }
+
+ if (upb_fielddef_containingoneof(field)) {
+ memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
+ *oneof_case = ONEOF_CASE_NONE;
+ } else 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 = field_type_class(field);
+
+ if (type_class != Qnil) {
+ VALUE args[2] = {
+ fieldtype_to_ruby(upb_fielddef_type(field)),
+ type_class,
+ };
+ ary = rb_class_new_instance(2, args, cRepeatedField);
+ } else {
+ VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) };
+ ary = rb_class_new_instance(1, args, cRepeatedField);
+ }
+
+ DEREF(memory, VALUE) = ary;
+ } else {
+ native_slot_set(upb_fielddef_type(field), field_type_class(field),
+ memory, layout_get_default(field));
+ }
+}
+
+VALUE layout_get_default(const upb_fielddef *field) {
+ switch (upb_fielddef_type(field)) {
+ case UPB_TYPE_FLOAT: return DBL2NUM(upb_fielddef_defaultfloat(field));
+ case UPB_TYPE_DOUBLE: return DBL2NUM(upb_fielddef_defaultdouble(field));
+ case UPB_TYPE_BOOL:
+ return upb_fielddef_defaultbool(field) ? Qtrue : Qfalse;
+ case UPB_TYPE_MESSAGE: return Qnil;
+ case UPB_TYPE_ENUM: {
+ const upb_enumdef *enumdef = upb_fielddef_enumsubdef(field);
+ int32_t num = upb_fielddef_defaultint32(field);
+ const char *label = upb_enumdef_iton(enumdef, num);
+ if (label) {
+ return ID2SYM(rb_intern(label));
+ } else {
+ return INT2NUM(num);
+ }
+ }
+ case UPB_TYPE_INT32: return INT2NUM(upb_fielddef_defaultint32(field));
+ case UPB_TYPE_INT64: return LL2NUM(upb_fielddef_defaultint64(field));;
+ case UPB_TYPE_UINT32: return UINT2NUM(upb_fielddef_defaultuint32(field));
+ case UPB_TYPE_UINT64: return ULL2NUM(upb_fielddef_defaultuint64(field));
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ size_t size;
+ const char *str = upb_fielddef_defaultstr(field, &size);
+ VALUE str_rb = rb_str_new(str, size);
+
+ rb_enc_associate(str_rb, (upb_fielddef_type(field) == UPB_TYPE_BYTES) ?
+ kRubyString8bitEncoding : kRubyStringUtf8Encoding);
+ rb_obj_freeze(str_rb);
+ return str_rb;
+ }
+ default: return Qnil;
+ }
+}
VALUE layout_get(MessageLayout* layout,
const void* storage,
@@ -576,15 +705,24 @@ VALUE layout_get(MessageLayout* layout,
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
+ bool field_set;
+ if (field_contains_hasbit(layout, field)) {
+ field_set = slot_is_hasbit_set(layout, storage, field);
+ } else {
+ field_set = true;
+ }
+
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case != upb_fielddef_number(field)) {
- return value_from_default(field);
+ return layout_get_default(field);
}
return native_slot_get(upb_fielddef_type(field),
field_type_class(field),
memory);
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
return *((VALUE *)memory);
+ } else if (!field_set) {
+ return layout_get_default(field);
} else {
return native_slot_get(upb_fielddef_type(field),
field_type_class(field),
@@ -689,67 +827,24 @@ void layout_set(MessageLayout* layout,
check_repeated_field_type(val, field);
DEREF(memory, VALUE) = val;
} else {
- native_slot_set(upb_fielddef_type(field), field_type_class(field),
- memory, val);
+ native_slot_set(upb_fielddef_type(field), field_type_class(field), memory,
+ val);
+ }
+
+ if (layout->fields[upb_fielddef_index(field)].hasbit !=
+ MESSAGE_FIELD_NO_HASBIT) {
+ slot_set_hasbit(layout, storage, field);
}
}
void layout_init(MessageLayout* layout,
void* storage) {
+
upb_msg_field_iter it;
for (upb_msg_field_begin(&it, layout->msgdef);
!upb_msg_field_done(&it);
upb_msg_field_next(&it)) {
- const upb_fielddef* field = upb_msg_iter_field(&it);
- void* memory = slot_memory(layout, storage, field);
- uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
-
- if (upb_fielddef_containingoneof(field)) {
- memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
- *oneof_case = ONEOF_CASE_NONE;
- } else 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 = field_type_class(field);
-
- if (type_class != Qnil) {
- VALUE args[2] = {
- fieldtype_to_ruby(upb_fielddef_type(field)),
- type_class,
- };
- ary = rb_class_new_instance(2, args, cRepeatedField);
- } else {
- VALUE args[1] = { fieldtype_to_ruby(upb_fielddef_type(field)) };
- ary = rb_class_new_instance(1, args, cRepeatedField);
- }
-
- DEREF(memory, VALUE) = ary;
- } else {
- native_slot_init(upb_fielddef_type(field), memory);
- }
+ layout_clear(layout, storage, upb_msg_iter_field(&it));
}
}
@@ -796,6 +891,11 @@ void layout_dup(MessageLayout* layout, void* to, void* from) {
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
DEREF(to_memory, VALUE) = RepeatedField_dup(DEREF(from_memory, VALUE));
} else {
+ if (field_contains_hasbit(layout, field)) {
+ if (!slot_is_hasbit_set(layout, from, field)) continue;
+ slot_set_hasbit(layout, to, field);
+ }
+
native_slot_dup(upb_fielddef_type(field), to_memory, from_memory);
}
}
@@ -825,6 +925,11 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) {
DEREF(to_memory, VALUE) =
RepeatedField_deep_copy(DEREF(from_memory, VALUE));
} else {
+ if (field_contains_hasbit(layout, field)) {
+ if (!slot_is_hasbit_set(layout, from, field)) continue;
+ slot_set_hasbit(layout, to, field);
+ }
+
native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory);
}
}
@@ -861,8 +966,10 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) {
return Qfalse;
}
} else {
- if (!native_slot_eq(upb_fielddef_type(field),
- msg1_memory, msg2_memory)) {
+ 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)) {
return Qfalse;
}
}