Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for maps in the MRI C Ruby extension. #155

Merged
merged 6 commits into from Jan 13, 2015

Conversation

cfallin
Copy link
Contributor

@cfallin cfallin commented Jan 7, 2015

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.

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.
@cfallin
Copy link
Contributor Author

cfallin commented Jan 7, 2015

@haberman to review.

* MessageBuilderContext.map(name, key_type, value_type, number,
* value_type_class = nil)
*
* Defines a new map field on this message type with the given key and value types, tag
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

80 char

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@cfallin
Copy link
Contributor Author

cfallin commented Jan 7, 2015

Thanks! Updated based on comments.

* type_class must be a string, if present (as accepted by
* FieldDescriptor#submsg_name=).
*/
VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It strikes me that this function is the equivalent of 3 or 4 lines of Ruby code, plus the special call to set the new message type as a map entry. I wonder if there is a way to implement it this way?

I envision something like:

VALUE message_type = rb_eval(
"if key_type == :float or key_type == :double or key_type == :enum or key_type == :message\n"
" raise 'Cannot add a map...'\n"
"end\n"
"add_message :MapEntry {\n"
" optional key_type, :key, 1\n"
" optional value_type, :value, 2\n"
"}");
upb_msgdef_setmapentry(ruby_to_Descriptor(message_type), true);

Don't invest too much effort if this looks tricky, but if this were possible it would make the code a lot shorter, clearer, and more robust, since it would be expressed in terms of the DSL you already previous defined.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I poked a bit at this but it seemed more complex and error-prone to construct a string of Ruby code than to just do the work in C. Totally agree that in principle it should be a simple layering, but I think in practice this is not too bad.

@cfallin
Copy link
Contributor Author

cfallin commented Jan 9, 2015

Addressed comments, thanks!


// Replace any existing value by issuing a 'remove' operation first.
upb_value oldv;
upb_strtable_remove2(&self->table, keyval, length, &oldv);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last parameter can be NULL when you don't care about it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


// For each member of other, check that a member exists at the same key in
// self. We don't need to compare values here -- if the key exists in both, we
// compared values above; if not, we already know that the maps are not equal.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary? If the two maps are the same size and all keys in m1 are known to be in m2, how could there be a key in m2 not in m1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent point! Done.

@cfallin
Copy link
Contributor Author

cfallin commented Jan 13, 2015

Addressed comments, thanks!


m = Google::Protobuf::Map.new(:string, :message, TestMessage,
{ "a" => TestMessage.new(:optional_int32 => 42),
"b" => TestMessage.new(:optional_int32 => 84) })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

80 char

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (and others in this file). Sorry about that!

haberman added a commit that referenced this pull request Jan 13, 2015
Support for maps in the MRI C Ruby extension.
@haberman haberman merged commit 5446dea into protocolbuffers:master Jan 13, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants