Merge branch 'master' of github.com:google/protobuf

pull/212/head
Josh Haberman 10 years ago
commit 55cf8abecb
  1. 11
      .gitignore
  2. 3
      .gitmodules
  3. 4
      .travis.yml
  4. 30
      Makefile.am
  5. 2
      README.md
  6. 6
      autogen.sh
  7. 1
      javanano/pom.xml
  8. 40
      javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java
  9. 124
      javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java
  10. 73
      javanano/src/main/java/com/google/protobuf/nano/Extension.java
  11. 223
      javanano/src/main/java/com/google/protobuf/nano/InternalNano.java
  12. 67
      javanano/src/main/java/com/google/protobuf/nano/MapFactories.java
  13. 340
      javanano/src/test/java/com/google/protobuf/nano/NanoTest.java
  14. 70
      javanano/src/test/java/com/google/protobuf/nano/map_test.proto
  15. 4
      python/README.txt
  16. 35
      python/google/protobuf/__init__.py
  17. 27
      python/google/protobuf/internal/import_test_package/BUILD
  18. 12
      ruby/README.md
  19. 22
      ruby/Rakefile
  20. 420
      ruby/ext/google/protobuf_c/defs.c
  21. 604
      ruby/ext/google/protobuf_c/encode_decode.c
  22. 10
      ruby/ext/google/protobuf_c/extconf.rb
  23. 805
      ruby/ext/google/protobuf_c/map.c
  24. 59
      ruby/ext/google/protobuf_c/message.c
  25. 3
      ruby/ext/google/protobuf_c/protobuf.c
  26. 157
      ruby/ext/google/protobuf_c/protobuf.h
  27. 65
      ruby/ext/google/protobuf_c/repeated_field.c
  28. 461
      ruby/ext/google/protobuf_c/storage.c
  29. 11220
      ruby/ext/google/protobuf_c/upb.c
  30. 8050
      ruby/ext/google/protobuf_c/upb.h
  31. 23
      ruby/ext/protobuf_c/extconf.rb
  32. 24
      ruby/google-protobuf.gemspec
  33. 2
      ruby/lib/google/protobuf.rb
  34. 381
      ruby/tests/basic.rb
  35. 67
      ruby/tests/generated_code.proto
  36. 74
      ruby/tests/generated_code.rb
  37. 17
      ruby/tests/generated_code_test.rb
  38. 2
      ruby/tests/stress.rb
  39. 27
      src/Makefile.am
  40. 2
      src/google/protobuf/compiler/cpp/cpp_service.cc
  41. 87
      src/google/protobuf/compiler/java/java_names.h
  42. 7
      src/google/protobuf/compiler/javanano/javanano_field.cc
  43. 11
      src/google/protobuf/compiler/javanano/javanano_helpers.cc
  44. 8
      src/google/protobuf/compiler/javanano/javanano_helpers.h
  45. 186
      src/google/protobuf/compiler/javanano/javanano_map_field.cc
  46. 70
      src/google/protobuf/compiler/javanano/javanano_map_field.h
  47. 8
      src/google/protobuf/compiler/javanano/javanano_message.cc
  48. 97
      src/google/protobuf/compiler/ruby/ruby_generator.cc
  49. 3
      src/google/protobuf/compiler/ruby/ruby_generator.h
  50. 119
      src/google/protobuf/compiler/ruby/ruby_generator_unittest.cc
  51. 3
      src/google/protobuf/io/coded_stream.h
  52. 4
      src/google/protobuf/stubs/atomicops_internals_mips_gcc.h
  53. 1
      upb
  54. 8
      vsprojects/libprotoc.vcproj

11
.gitignore vendored

@ -16,6 +16,7 @@ m4/ltoptions.m4
m4/ltsugar.m4
m4/ltversion.m4
m4/lt~obsolete.m4
autom4te.cache
# downloaded files
gtest
@ -61,3 +62,13 @@ src/test_plugin
src/testzip.*
src/zcg*zip
ar-lib
test-driver
compile
src/**/*.log
src/**/*.trs
# JavaBuild output.
java/target
javanano/target

3
.gitmodules vendored

@ -1,3 +0,0 @@
[submodule "upb"]
path = upb
url = https://github.com/haberman/upb

@ -0,0 +1,4 @@
language: cpp
script: ./autogen.sh && ./configure && make distcheck
notifications:
email: false

@ -195,7 +195,6 @@ python_EXTRA_DIST= \
python/google/protobuf/internal/wire_format.py \
python/google/protobuf/internal/wire_format_test.py \
python/google/protobuf/internal/__init__.py \
python/google/protobuf/internal/import_test_package/BUILD \
python/google/protobuf/internal/import_test_package/__init__.py \
python/google/protobuf/internal/import_test_package/inner.proto \
python/google/protobuf/internal/import_test_package/outer.proto \
@ -240,15 +239,26 @@ python_EXTRA_DIST= \
python/README.txt
ruby_EXTRA_DIST= \
ruby/ext/defs.c \
ruby/ext/encode_decode.c \
ruby/ext/extconf.rb \
ruby/ext/message.c \
ruby/ext/protobuf.c \
ruby/ext/protobuf.h \
ruby/ext/repeated_field.c \
ruby/ext/storage.c \
ruby/ext/test.rb
ruby/README.md \
ruby/Rakefile \
ruby/ext/google/protobuf_c/defs.c \
ruby/ext/google/protobuf_c/encode_decode.c \
ruby/ext/google/protobuf_c/extconf.rb \
ruby/ext/google/protobuf_c/map.c \
ruby/ext/google/protobuf_c/message.c \
ruby/ext/google/protobuf_c/protobuf.c \
ruby/ext/google/protobuf_c/protobuf.h \
ruby/ext/google/protobuf_c/repeated_field.c \
ruby/ext/google/protobuf_c/storage.c \
ruby/ext/google/protobuf_c/upb.c \
ruby/ext/google/protobuf_c/upb.h \
ruby/google-protobuf.gemspec \
ruby/lib/google/protobuf.rb \
ruby/tests/basic.rb \
ruby/tests/stress.rb \
ruby/tests/generated_code.proto \
ruby/tests/generated_code.rb \
ruby/tests/generated_code_test.rb
all_EXTRA_DIST=$(java_EXTRA_DIST) $(python_EXTRA_DIST) $(ruby_EXTRA_DIST)

@ -1,6 +1,8 @@
Protocol Buffers - Google's data interchange format
===================================================
[![Build Status](https://travis-ci.org/google/protobuf.svg?branch=master)](https://travis-ci.org/google/protobuf)
Copyright 2008 Google Inc.
https://developers.google.com/protocol-buffers/

@ -15,12 +15,6 @@ __EOF__
exit 1
fi
if test -e .git; then
# pull down git submodules.
git submodule init
git submodule update
fi
# Check that gtest is present. Usually it is already there since the
# directory is set up as an SVN external.
if test ! -e gtest; then

@ -94,6 +94,7 @@
<arg value="src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto" />
<arg value="src/test/java/com/google/protobuf/nano/map_test.proto" />
</exec>
<exec executable="../src/protoc">
<arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" />

@ -638,4 +638,44 @@ public final class CodedInputByteBufferNano {
throw InvalidProtocolBufferNanoException.truncatedMessage();
}
}
// Read a primitive type.
Object readPrimitiveField(int type) throws IOException {
switch (type) {
case InternalNano.TYPE_DOUBLE:
return readDouble();
case InternalNano.TYPE_FLOAT:
return readFloat();
case InternalNano.TYPE_INT64:
return readInt64();
case InternalNano.TYPE_UINT64:
return readUInt64();
case InternalNano.TYPE_INT32:
return readInt32();
case InternalNano.TYPE_FIXED64:
return readFixed64();
case InternalNano.TYPE_FIXED32:
return readFixed32();
case InternalNano.TYPE_BOOL:
return readBool();
case InternalNano.TYPE_STRING:
return readString();
case InternalNano.TYPE_BYTES:
return readBytes();
case InternalNano.TYPE_UINT32:
return readUInt32();
case InternalNano.TYPE_ENUM:
return readEnum();
case InternalNano.TYPE_SFIXED32:
return readSFixed32();
case InternalNano.TYPE_SFIXED64:
return readSFixed64();
case InternalNano.TYPE_SINT32:
return readSInt32();
case InternalNano.TYPE_SINT64:
return readSInt64();
default:
throw new IllegalArgumentException("Unknown type " + type);
}
}
}

@ -876,4 +876,128 @@ public final class CodedOutputByteBufferNano {
// Note: the right-shift must be arithmetic
return (n << 1) ^ (n >> 63);
}
static int computeFieldSize(int number, int type, Object object) {
switch (type) {
case InternalNano.TYPE_BOOL:
return computeBoolSize(number, (Boolean) object);
case InternalNano.TYPE_BYTES:
return computeBytesSize(number, (byte[]) object);
case InternalNano.TYPE_STRING:
return computeStringSize(number, (String) object);
case InternalNano.TYPE_FLOAT:
return computeFloatSize(number, (Float) object);
case InternalNano.TYPE_DOUBLE:
return computeDoubleSize(number, (Double) object);
case InternalNano.TYPE_ENUM:
return computeEnumSize(number, (Integer) object);
case InternalNano.TYPE_FIXED32:
return computeFixed32Size(number, (Integer) object);
case InternalNano.TYPE_INT32:
return computeInt32Size(number, (Integer) object);
case InternalNano.TYPE_UINT32:
return computeUInt32Size(number, (Integer) object);
case InternalNano.TYPE_SINT32:
return computeSInt32Size(number, (Integer) object);
case InternalNano.TYPE_SFIXED32:
return computeSFixed32Size(number, (Integer) object);
case InternalNano.TYPE_INT64:
return computeInt64Size(number, (Long) object);
case InternalNano.TYPE_UINT64:
return computeUInt64Size(number, (Long) object);
case InternalNano.TYPE_SINT64:
return computeSInt64Size(number, (Long) object);
case InternalNano.TYPE_FIXED64:
return computeFixed64Size(number, (Long) object);
case InternalNano.TYPE_SFIXED64:
return computeSFixed64Size(number, (Long) object);
case InternalNano.TYPE_MESSAGE:
return computeMessageSize(number, (MessageNano) object);
case InternalNano.TYPE_GROUP:
return computeGroupSize(number, (MessageNano) object);
default:
throw new IllegalArgumentException("Unknown type: " + type);
}
}
void writeField(int number, int type, Object value)
throws IOException {
switch (type) {
case InternalNano.TYPE_DOUBLE:
Double doubleValue = (Double) value;
writeDouble(number, doubleValue);
break;
case InternalNano.TYPE_FLOAT:
Float floatValue = (Float) value;
writeFloat(number, floatValue);
break;
case InternalNano.TYPE_INT64:
Long int64Value = (Long) value;
writeInt64(number, int64Value);
break;
case InternalNano.TYPE_UINT64:
Long uint64Value = (Long) value;
writeUInt64(number, uint64Value);
break;
case InternalNano.TYPE_INT32:
Integer int32Value = (Integer) value;
writeInt32(number, int32Value);
break;
case InternalNano.TYPE_FIXED64:
Long fixed64Value = (Long) value;
writeFixed64(number, fixed64Value);
break;
case InternalNano.TYPE_FIXED32:
Integer fixed32Value = (Integer) value;
writeFixed32(number, fixed32Value);
break;
case InternalNano.TYPE_BOOL:
Boolean boolValue = (Boolean) value;
writeBool(number, boolValue);
break;
case InternalNano.TYPE_STRING:
String stringValue = (String) value;
writeString(number, stringValue);
break;
case InternalNano.TYPE_BYTES:
byte[] bytesValue = (byte[]) value;
writeBytes(number, bytesValue);
break;
case InternalNano.TYPE_UINT32:
Integer uint32Value = (Integer) value;
writeUInt32(number, uint32Value);
break;
case InternalNano.TYPE_ENUM:
Integer enumValue = (Integer) value;
writeEnum(number, enumValue);
break;
case InternalNano.TYPE_SFIXED32:
Integer sfixed32Value = (Integer) value;
writeSFixed32(number, sfixed32Value);
break;
case InternalNano.TYPE_SFIXED64:
Long sfixed64Value = (Long) value;
writeSFixed64(number, sfixed64Value);
break;
case InternalNano.TYPE_SINT32:
Integer sint32Value = (Integer) value;
writeSInt32(number, sint32Value);
break;
case InternalNano.TYPE_SINT64:
Long sint64Value = (Long) value;
writeSInt64(number, sint64Value);
break;
case InternalNano.TYPE_MESSAGE:
MessageNano messageValue = (MessageNano) value;
writeMessage(number, messageValue);
break;
case InternalNano.TYPE_GROUP:
MessageNano groupValue = (MessageNano) value;
writeGroup(number, groupValue);
break;
default:
throw new IOException("Unknown type: " + type);
}
}
}

@ -55,24 +55,24 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
* PrimitiveExtension // for primitive/enum typed extensions
*/
public static final int TYPE_DOUBLE = 1;
public static final int TYPE_FLOAT = 2;
public static final int TYPE_INT64 = 3;
public static final int TYPE_UINT64 = 4;
public static final int TYPE_INT32 = 5;
public static final int TYPE_FIXED64 = 6;
public static final int TYPE_FIXED32 = 7;
public static final int TYPE_BOOL = 8;
public static final int TYPE_STRING = 9;
public static final int TYPE_GROUP = 10;
public static final int TYPE_MESSAGE = 11;
public static final int TYPE_BYTES = 12;
public static final int TYPE_UINT32 = 13;
public static final int TYPE_ENUM = 14;
public static final int TYPE_SFIXED32 = 15;
public static final int TYPE_SFIXED64 = 16;
public static final int TYPE_SINT32 = 17;
public static final int TYPE_SINT64 = 18;
public static final int TYPE_DOUBLE = InternalNano.TYPE_DOUBLE;
public static final int TYPE_FLOAT = InternalNano.TYPE_FLOAT;
public static final int TYPE_INT64 = InternalNano.TYPE_INT64;
public static final int TYPE_UINT64 = InternalNano.TYPE_UINT64;
public static final int TYPE_INT32 = InternalNano.TYPE_INT32;
public static final int TYPE_FIXED64 = InternalNano.TYPE_FIXED64;
public static final int TYPE_FIXED32 = InternalNano.TYPE_FIXED32;
public static final int TYPE_BOOL = InternalNano.TYPE_BOOL;
public static final int TYPE_STRING = InternalNano.TYPE_STRING;
public static final int TYPE_GROUP = InternalNano.TYPE_GROUP;
public static final int TYPE_MESSAGE = InternalNano.TYPE_MESSAGE;
public static final int TYPE_BYTES = InternalNano.TYPE_BYTES;
public static final int TYPE_UINT32 = InternalNano.TYPE_UINT32;
public static final int TYPE_ENUM = InternalNano.TYPE_ENUM;
public static final int TYPE_SFIXED32 = InternalNano.TYPE_SFIXED32;
public static final int TYPE_SFIXED64 = InternalNano.TYPE_SFIXED64;
public static final int TYPE_SINT32 = InternalNano.TYPE_SINT32;
public static final int TYPE_SINT64 = InternalNano.TYPE_SINT64;
/**
* Creates an {@code Extension} of the given message type and tag number.
@ -338,42 +338,7 @@ public class Extension<M extends ExtendableMessageNano<M>, T> {
@Override
protected Object readData(CodedInputByteBufferNano input) {
try {
switch (type) {
case TYPE_DOUBLE:
return input.readDouble();
case TYPE_FLOAT:
return input.readFloat();
case TYPE_INT64:
return input.readInt64();
case TYPE_UINT64:
return input.readUInt64();
case TYPE_INT32:
return input.readInt32();
case TYPE_FIXED64:
return input.readFixed64();
case TYPE_FIXED32:
return input.readFixed32();
case TYPE_BOOL:
return input.readBool();
case TYPE_STRING:
return input.readString();
case TYPE_BYTES:
return input.readBytes();
case TYPE_UINT32:
return input.readUInt32();
case TYPE_ENUM:
return input.readEnum();
case TYPE_SFIXED32:
return input.readSFixed32();
case TYPE_SFIXED64:
return input.readSFixed64();
case TYPE_SINT32:
return input.readSInt32();
case TYPE_SINT64:
return input.readSInt64();
default:
throw new IllegalArgumentException("Unknown type " + type);
}
return input.readPrimitiveField(type);
} catch (IOException e) {
throw new IllegalArgumentException("Error reading extension field", e);
}

@ -30,8 +30,13 @@
package com.google.protobuf.nano;
import com.google.protobuf.nano.MapFactories.MapFactory;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
/**
* The classes contained within are used internally by the Protocol Buffer
@ -43,6 +48,26 @@ import java.util.Arrays;
*/
public final class InternalNano {
public static final int TYPE_DOUBLE = 1;
public static final int TYPE_FLOAT = 2;
public static final int TYPE_INT64 = 3;
public static final int TYPE_UINT64 = 4;
public static final int TYPE_INT32 = 5;
public static final int TYPE_FIXED64 = 6;
public static final int TYPE_FIXED32 = 7;
public static final int TYPE_BOOL = 8;
public static final int TYPE_STRING = 9;
public static final int TYPE_GROUP = 10;
public static final int TYPE_MESSAGE = 11;
public static final int TYPE_BYTES = 12;
public static final int TYPE_UINT32 = 13;
public static final int TYPE_ENUM = 14;
public static final int TYPE_SFIXED32 = 15;
public static final int TYPE_SFIXED64 = 16;
public static final int TYPE_SINT32 = 17;
public static final int TYPE_SINT64 = 18;
private InternalNano() {}
/**
@ -329,5 +354,203 @@ public final class InternalNano {
}
return result;
}
private static final byte[] EMPTY_BYTES = new byte[0];
private static Object primitiveDefaultValue(int type) {
switch (type) {
case TYPE_BOOL:
return Boolean.FALSE;
case TYPE_BYTES:
return EMPTY_BYTES;
case TYPE_STRING:
return "";
case TYPE_FLOAT:
return Float.valueOf(0);
case TYPE_DOUBLE:
return Double.valueOf(0);
case TYPE_ENUM:
case TYPE_FIXED32:
case TYPE_INT32:
case TYPE_UINT32:
case TYPE_SINT32:
case TYPE_SFIXED32:
return Integer.valueOf(0);
case TYPE_INT64:
case TYPE_UINT64:
case TYPE_SINT64:
case TYPE_FIXED64:
case TYPE_SFIXED64:
return Long.valueOf(0L);
case TYPE_MESSAGE:
case TYPE_GROUP:
default:
throw new IllegalArgumentException(
"Type: " + type + " is not a primitive type.");
}
}
/**
* Merges the map entry into the map field. Note this is only supposed to
* be called by generated messages.
*
* @param map the map field; may be null, in which case a map will be
* instantiated using the {@link MapFactories.MapFactory}
* @param input the input byte buffer
* @param keyType key type, as defined in InternalNano.TYPE_*
* @param valueType value type, as defined in InternalNano.TYPE_*
* @param value an new instance of the value, if the value is a TYPE_MESSAGE;
* otherwise this parameter can be null and will be ignored.
* @param keyTag wire tag for the key
* @param valueTag wire tag for the value
* @return the map field
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static final <K, V> Map<K, V> mergeMapEntry(
CodedInputByteBufferNano input,
Map<K, V> map,
MapFactory mapFactory,
int keyType,
int valueType,
V value,
int keyTag,
int valueTag) throws IOException {
map = mapFactory.forMap(map);
final int length = input.readRawVarint32();
final int oldLimit = input.pushLimit(length);
K key = null;
while (true) {
int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == keyTag) {
key = (K) input.readPrimitiveField(keyType);
} else if (tag == valueTag) {
if (valueType == TYPE_MESSAGE) {
input.readMessage((MessageNano) value);
} else {
value = (V) input.readPrimitiveField(valueType);
}
} else {
if (!input.skipField(tag)) {
break;
}
}
}
input.checkLastTagWas(0);
input.popLimit(oldLimit);
if (key == null) {
// key can only be primitive types.
key = (K) primitiveDefaultValue(keyType);
}
if (value == null) {
// message type value will be initialized by code-gen.
value = (V) primitiveDefaultValue(valueType);
}
map.put(key, value);
return map;
}
public static <K, V> void serializeMapField(
CodedOutputByteBufferNano output,
Map<K, V> map, int number, int keyType, int valueType)
throws IOException {
for (Entry<K, V> entry: map.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
if (key == null || value == null) {
throw new IllegalStateException(
"keys and values in maps cannot be null");
}
int entrySize =
CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
output.writeTag(number, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(entrySize);
output.writeField(1, keyType, key);
output.writeField(2, valueType, value);
}
}
public static <K, V> int computeMapFieldSize(
Map<K, V> map, int number, int keyType, int valueType) {
int size = 0;
int tagSize = CodedOutputByteBufferNano.computeTagSize(number);
for (Entry<K, V> entry: map.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
if (key == null || value == null) {
throw new IllegalStateException(
"keys and values in maps cannot be null");
}
int entrySize =
CodedOutputByteBufferNano.computeFieldSize(1, keyType, key) +
CodedOutputByteBufferNano.computeFieldSize(2, valueType, value);
size += tagSize + entrySize
+ CodedOutputByteBufferNano.computeRawVarint32Size(entrySize);
}
return size;
}
/**
* Checks whether two {@link Map} are equal. We don't use the default equals
* method of {@link Map} because it compares by identity not by content for
* byte arrays.
*/
public static <K, V> boolean equals(Map<K, V> a, Map<K, V> b) {
if (a == b) {
return true;
}
if (a == null) {
return b.size() == 0;
}
if (b == null) {
return a.size() == 0;
}
if (a.size() != b.size()) {
return false;
}
for (Entry<K, V> entry : a.entrySet()) {
if (!b.containsKey(entry.getKey())) {
return false;
}
if (!equalsMapValue(entry.getValue(), b.get(entry.getKey()))) {
return false;
}
}
return true;
}
private static boolean equalsMapValue(Object a, Object b) {
if (a == null || b == null) {
throw new IllegalStateException(
"keys and values in maps cannot be null");
}
if (a instanceof byte[] && b instanceof byte[]) {
return Arrays.equals((byte[]) a, (byte[]) b);
}
return a.equals(b);
}
public static <K, V> int hashCode(Map<K, V> map) {
if (map == null) {
return 0;
}
int result = 0;
for (Entry<K, V> entry : map.entrySet()) {
result += hashCodeForMap(entry.getKey())
^ hashCodeForMap(entry.getValue());
}
return result;
}
private static int hashCodeForMap(Object o) {
if (o instanceof byte[]) {
return Arrays.hashCode((byte[]) o);
}
return o.hashCode();
}
}

@ -0,0 +1,67 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2013 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf.nano;
import java.util.HashMap;
import java.util.Map;
/**
* Utility class for maps support.
*/
public final class MapFactories {
public static interface MapFactory {
<K, V> Map<K, V> forMap(Map<K, V> oldMap);
}
// NOTE(liujisi): The factory setter is temporarily marked as package private.
// The way to provide customized implementations of maps for different
// platforms are still under discussion. Mark it as private to avoid exposing
// the API in proto3 alpha release.
/* public */ static void setMapFactory(MapFactory newMapFactory) {
mapFactory = newMapFactory;
}
public static MapFactory getMapFactory() {
return mapFactory;
}
private static class DefaultMapFactory implements MapFactory {
public <K, V> Map<K, V> forMap(Map<K, V> oldMap) {
if (oldMap == null) {
return new HashMap<K, V>();
}
return oldMap;
}
}
private static volatile MapFactory mapFactory = new DefaultMapFactory();
private MapFactories() {}
}

@ -30,31 +30,11 @@
package com.google.protobuf.nano;
import com.google.protobuf.nano.CodedInputByteBufferNano;
import com.google.protobuf.nano.EnumClassNanoMultiple;
import com.google.protobuf.nano.EnumClassNanos;
import com.google.protobuf.nano.EnumValidity;
import com.google.protobuf.nano.EnumValidityAccessors;
import com.google.protobuf.nano.FileScopeEnumMultiple;
import com.google.protobuf.nano.FileScopeEnumRefNano;
import com.google.protobuf.nano.InternalNano;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import com.google.protobuf.nano.MessageNano;
import com.google.protobuf.nano.MessageScopeEnumRefNano;
import com.google.protobuf.nano.MultipleImportingNonMultipleNano1;
import com.google.protobuf.nano.MultipleImportingNonMultipleNano2;
import com.google.protobuf.nano.MultipleNameClashNano;
import com.google.protobuf.nano.MapTestProto.TestMap;
import com.google.protobuf.nano.MapTestProto.TestMap.MessageValue;
import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors;
import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas;
import com.google.protobuf.nano.NanoOuterClass;
import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.NanoReferenceTypes;
import com.google.protobuf.nano.NanoRepeatedPackables;
import com.google.protobuf.nano.PackedExtensions;
import com.google.protobuf.nano.RepeatedExtensions;
import com.google.protobuf.nano.SingularExtensions;
import com.google.protobuf.nano.TestRepeatedMergeNano;
import com.google.protobuf.nano.UnittestMultipleNano;
import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano;
import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano;
@ -67,6 +47,8 @@ import junit.framework.TestCase;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
/**
* Test nano runtime.
@ -3754,6 +3736,320 @@ public class NanoTest extends TestCase {
assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools));
}
public void testMapsSerializeAndParse() throws Exception {
TestMap origin = new TestMap();
setMapMessage(origin);
assertMapMessageSet(origin);
byte[] output = MessageNano.toByteArray(origin);
TestMap parsed = new TestMap();
MessageNano.mergeFrom(parsed, output);
}
public void testMapSerializeRejectNull() throws Exception {
TestMap primitiveMap = new TestMap();
primitiveMap.int32ToInt32Field = new HashMap<Integer, Integer>();
primitiveMap.int32ToInt32Field.put(null, 1);
try {
MessageNano.toByteArray(primitiveMap);
fail("should reject null keys");
} catch (IllegalStateException e) {
// pass.
}
TestMap messageMap = new TestMap();
messageMap.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
messageMap.int32ToMessageField.put(0, null);
try {
MessageNano.toByteArray(messageMap);
fail("should reject null values");
} catch (IllegalStateException e) {
// pass.
}
}
/**
* Tests that merging bytes containing conflicting keys with override the
* message value instead of merging the message value into the existing entry.
*/
public void testMapMergeOverrideMessageValues() throws Exception {
TestMap.MessageValue origValue = new TestMap.MessageValue();
origValue.value = 1;
origValue.value2 = 2;
TestMap.MessageValue newValue = new TestMap.MessageValue();
newValue.value = 3;
TestMap origMessage = new TestMap();
origMessage.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
origMessage.int32ToMessageField.put(1, origValue);
TestMap newMessage = new TestMap();
newMessage.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
newMessage.int32ToMessageField.put(1, newValue);
MessageNano.mergeFrom(origMessage,
MessageNano.toByteArray(newMessage));
TestMap.MessageValue mergedValue = origMessage.int32ToMessageField.get(1);
assertEquals(3, mergedValue.value);
assertEquals(0, mergedValue.value2);
}
/**
* Tests that when merging with empty entries,
* we will use default for the key and value, instead of null.
*/
public void testMapMergeEmptyEntry() throws Exception {
TestMap testMap = new TestMap();
byte[] buffer = new byte[1024];
CodedOutputByteBufferNano output =
CodedOutputByteBufferNano.newInstance(buffer);
// An empty entry for int32_to_int32 map.
output.writeTag(1, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(0);
// An empty entry for int32_to_message map.
output.writeTag(5, WireFormatNano.WIRETYPE_LENGTH_DELIMITED);
output.writeRawVarint32(0);
CodedInputByteBufferNano input = CodedInputByteBufferNano.newInstance(
buffer, 0, buffer.length - output.spaceLeft());
testMap.mergeFrom(input);
assertNotNull(testMap.int32ToInt32Field);;
assertEquals(1, testMap.int32ToInt32Field.size());
assertEquals(Integer.valueOf(0), testMap.int32ToInt32Field.get(0));
assertNotNull(testMap.int32ToMessageField);
assertEquals(1, testMap.int32ToMessageField.size());
TestMap.MessageValue messageValue = testMap.int32ToMessageField.get(0);
assertNotNull(messageValue);
assertEquals(0, messageValue.value);
assertEquals(0, messageValue.value2);
}
public void testMapEquals() throws Exception {
TestMap a = new TestMap();
TestMap b = new TestMap();
// empty and null map fields are equal.
assertTestMapEqual(a, b);
a.int32ToBytesField = new HashMap<Integer, byte[]>();
assertTestMapEqual(a, b);
a.int32ToInt32Field = new HashMap<Integer, Integer>();
b.int32ToInt32Field = new HashMap<Integer, Integer>();
setMap(a.int32ToInt32Field, deepCopy(int32Values), deepCopy(int32Values));
setMap(b.int32ToInt32Field, deepCopy(int32Values), deepCopy(int32Values));
assertTestMapEqual(a, b);
a.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
b.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
setMap(a.int32ToMessageField,
deepCopy(int32Values), deepCopy(messageValues));
setMap(b.int32ToMessageField,
deepCopy(int32Values), deepCopy(messageValues));
assertTestMapEqual(a, b);
a.stringToInt32Field = new HashMap<String, Integer>();
b.stringToInt32Field = new HashMap<String, Integer>();
setMap(a.stringToInt32Field, deepCopy(stringValues), deepCopy(int32Values));
setMap(b.stringToInt32Field, deepCopy(stringValues), deepCopy(int32Values));
assertTestMapEqual(a, b);
a.int32ToBytesField = new HashMap<Integer, byte[]>();
b.int32ToBytesField = new HashMap<Integer, byte[]>();
setMap(a.int32ToBytesField, deepCopy(int32Values), deepCopy(bytesValues));
setMap(b.int32ToBytesField, deepCopy(int32Values), deepCopy(bytesValues));
assertTestMapEqual(a, b);
// Make sure the map implementation does not matter.
a.int32ToStringField = new TreeMap<Integer, String>();
b.int32ToStringField = new HashMap<Integer, String>();
setMap(a.int32ToStringField, deepCopy(int32Values), deepCopy(stringValues));
setMap(b.int32ToStringField, deepCopy(int32Values), deepCopy(stringValues));
assertTestMapEqual(a, b);
a.clear();
b.clear();
// unequal cases: different value
a.int32ToInt32Field = new HashMap<Integer, Integer>();
b.int32ToInt32Field = new HashMap<Integer, Integer>();
a.int32ToInt32Field.put(1, 1);
b.int32ToInt32Field.put(1, 2);
assertTestMapUnequal(a, b);
// unequal case: additional entry
b.int32ToInt32Field.put(1, 1);
b.int32ToInt32Field.put(2, 1);
assertTestMapUnequal(a, b);
a.int32ToInt32Field.put(2, 1);
assertTestMapEqual(a, b);
// unequal case: different message value.
a.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
b.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
MessageValue va = new MessageValue();
va.value = 1;
MessageValue vb = new MessageValue();
vb.value = 1;
a.int32ToMessageField.put(1, va);
b.int32ToMessageField.put(1, vb);
assertTestMapEqual(a, b);
vb.value = 2;
assertTestMapUnequal(a, b);
}
private static void assertTestMapEqual(TestMap a, TestMap b)
throws Exception {
assertEquals(a.hashCode(), b.hashCode());
assertTrue(a.equals(b));
assertTrue(b.equals(a));
}
private static void assertTestMapUnequal(TestMap a, TestMap b)
throws Exception {
assertFalse(a.equals(b));
assertFalse(b.equals(a));
}
private static final Integer[] int32Values = new Integer[] {
0, 1, -1, Integer.MAX_VALUE, Integer.MIN_VALUE,
};
private static final Long[] int64Values = new Long[] {
0L, 1L, -1L, Long.MAX_VALUE, Long.MIN_VALUE,
};
private static final String[] stringValues = new String[] {
"", "hello", "world", "foo", "bar",
};
private static final byte[][] bytesValues = new byte[][] {
new byte[] {},
new byte[] {0},
new byte[] {1, -1},
new byte[] {127, -128},
new byte[] {'a', 'b', '0', '1'},
};
private static final Boolean[] boolValues = new Boolean[] {
false, true,
};
private static final Integer[] enumValues = new Integer[] {
TestMap.FOO, TestMap.BAR, TestMap.BAZ, TestMap.QUX,
Integer.MAX_VALUE /* unknown */,
};
private static final TestMap.MessageValue[] messageValues =
new TestMap.MessageValue[] {
newMapValueMessage(0),
newMapValueMessage(1),
newMapValueMessage(-1),
newMapValueMessage(Integer.MAX_VALUE),
newMapValueMessage(Integer.MIN_VALUE),
};
private static TestMap.MessageValue newMapValueMessage(int value) {
TestMap.MessageValue result = new TestMap.MessageValue();
result.value = value;
return result;
}
@SuppressWarnings("unchecked")
private static <T> T[] deepCopy(T[] orig) throws Exception {
if (orig instanceof MessageValue[]) {
MessageValue[] result = new MessageValue[orig.length];
for (int i = 0; i < orig.length; i++) {
result[i] = new MessageValue();
MessageNano.mergeFrom(
result[i], MessageNano.toByteArray((MessageValue) orig[i]));
}
return (T[]) result;
}
if (orig instanceof byte[][]) {
byte[][] result = new byte[orig.length][];
for (int i = 0; i < orig.length; i++) {
byte[] origBytes = (byte[]) orig[i];
result[i] = Arrays.copyOf(origBytes, origBytes.length);
}
}
return Arrays.copyOf(orig, orig.length);
}
private <K, V> void setMap(Map<K, V> map, K[] keys, V[] values) {
assert(keys.length == values.length);
for (int i = 0; i < keys.length; i++) {
map.put(keys[i], values[i]);
}
}
private <K, V> void assertMapSet(
Map<K, V> map, K[] keys, V[] values) throws Exception {
assert(keys.length == values.length);
for (int i = 0; i < values.length; i++) {
assertEquals(values[i], map.get(keys[i]));
}
assertEquals(keys.length, map.size());
}
private void setMapMessage(TestMap testMap) {
testMap.int32ToInt32Field = new HashMap<Integer, Integer>();
testMap.int32ToBytesField = new HashMap<Integer, byte[]>();
testMap.int32ToEnumField = new HashMap<Integer, Integer>();
testMap.int32ToMessageField =
new HashMap<Integer, MapTestProto.TestMap.MessageValue>();
testMap.int32ToStringField = new HashMap<Integer, String>();
testMap.stringToInt32Field = new HashMap<String, Integer>();
testMap.boolToBoolField = new HashMap<Boolean, Boolean>();
testMap.uint32ToUint32Field = new HashMap<Integer, Integer>();
testMap.sint32ToSint32Field = new HashMap<Integer, Integer>();
testMap.fixed32ToFixed32Field = new HashMap<Integer, Integer>();
testMap.sfixed32ToSfixed32Field = new HashMap<Integer, Integer>();
testMap.int64ToInt64Field = new HashMap<Long, Long>();
testMap.uint64ToUint64Field = new HashMap<Long, Long>();
testMap.sint64ToSint64Field = new HashMap<Long, Long>();
testMap.fixed64ToFixed64Field = new HashMap<Long, Long>();
testMap.sfixed64ToSfixed64Field = new HashMap<Long, Long>();
setMap(testMap.int32ToInt32Field, int32Values, int32Values);
setMap(testMap.int32ToBytesField, int32Values, bytesValues);
setMap(testMap.int32ToEnumField, int32Values, enumValues);
setMap(testMap.int32ToMessageField, int32Values, messageValues);
setMap(testMap.int32ToStringField, int32Values, stringValues);
setMap(testMap.stringToInt32Field, stringValues, int32Values);
setMap(testMap.boolToBoolField, boolValues, boolValues);
setMap(testMap.uint32ToUint32Field, int32Values, int32Values);
setMap(testMap.sint32ToSint32Field, int32Values, int32Values);
setMap(testMap.fixed32ToFixed32Field, int32Values, int32Values);
setMap(testMap.sfixed32ToSfixed32Field, int32Values, int32Values);
setMap(testMap.int64ToInt64Field, int64Values, int64Values);
setMap(testMap.uint64ToUint64Field, int64Values, int64Values);
setMap(testMap.sint64ToSint64Field, int64Values, int64Values);
setMap(testMap.fixed64ToFixed64Field, int64Values, int64Values);
setMap(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
}
private void assertMapMessageSet(TestMap testMap) throws Exception {
assertMapSet(testMap.int32ToInt32Field, int32Values, int32Values);
assertMapSet(testMap.int32ToBytesField, int32Values, bytesValues);
assertMapSet(testMap.int32ToEnumField, int32Values, enumValues);
assertMapSet(testMap.int32ToMessageField, int32Values, messageValues);
assertMapSet(testMap.int32ToStringField, int32Values, stringValues);
assertMapSet(testMap.stringToInt32Field, stringValues, int32Values);
assertMapSet(testMap.boolToBoolField, boolValues, boolValues);
assertMapSet(testMap.uint32ToUint32Field, int32Values, int32Values);
assertMapSet(testMap.sint32ToSint32Field, int32Values, int32Values);
assertMapSet(testMap.fixed32ToFixed32Field, int32Values, int32Values);
assertMapSet(testMap.sfixed32ToSfixed32Field, int32Values, int32Values);
assertMapSet(testMap.int64ToInt64Field, int64Values, int64Values);
assertMapSet(testMap.uint64ToUint64Field, int64Values, int64Values);
assertMapSet(testMap.sint64ToSint64Field, int64Values, int64Values);
assertMapSet(testMap.fixed64ToFixed64Field, int64Values, int64Values);
assertMapSet(testMap.sfixed64ToSfixed64Field, int64Values, int64Values);
}
private void assertRepeatedPackablesEqual(
NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) {
// Not using MessageNano.equals() -- that belongs to a separate test.

@ -0,0 +1,70 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package map_test;
option java_package = "com.google.protobuf.nano";
option java_outer_classname = "MapTestProto";
message TestMap {
message MessageValue {
int32 value = 1;
int32 value2 = 2;
}
enum EnumValue {
FOO = 0;
BAR = 1;
BAZ = 2;
QUX = 3;
}
map<int32, int32> int32_to_int32_field = 1;
map<int32, string> int32_to_string_field = 2;
map<int32, bytes> int32_to_bytes_field = 3;
map<int32, EnumValue> int32_to_enum_field = 4;
map<int32, MessageValue> int32_to_message_field = 5;
map<string, int32> string_to_int32_field = 6;
map<bool, bool> bool_to_bool_field = 7;
// Test all the other primitive types. As the key and value are not coupled in
// the implementation, we do not test all the combinations of key/value pairs,
// so that we can keep the number of test cases manageable
map<uint32, uint32> uint32_to_uint32_field = 11;
map<sint32, sint32> sint32_to_sint32_field = 12;
map<fixed32, fixed32> fixed32_to_fixed32_field = 13;
map<sfixed32, sfixed32> sfixed32_to_sfixed32_field = 14;
map<int64, int64> int64_to_int64_field = 15;
map<uint64, uint64> uint64_to_uint64_field = 16;
map<sint64, sint64> sint64_to_sint64_field = 17;
map<fixed64, fixed64> fixed64_to_fixed64_field = 18;
map<sfixed64, sfixed64> sfixed64_to_sfixed64_field = 19;
}

@ -26,7 +26,7 @@ join the Protocol Buffers discussion list and let us know!
Installation
============
1) Make sure you have Python 2.4 or newer. If in doubt, run:
1) Make sure you have Python 2.6 or newer. If in doubt, run:
$ python -V
@ -35,7 +35,7 @@ Installation
If you would rather install it manually, you may do so by following
the instructions on this page:
http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions
https://packaging.python.org/en/latest/installing.html#setup-for-installing-packages
3) Build the C++ code, or install a binary distribution of protoc. If
you install a binary distribution, make sure that it is the same

@ -0,0 +1,35 @@
# Protocol Buffers - Google's data interchange format
# Copyright 2008 Google Inc. All rights reserved.
# https://developers.google.com/protocol-buffers/
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# Needs to stay compatible with Python 2.5 due to GAE.
#
# Copyright 2007 Google Inc. All Rights Reserved.
__version__ = '3.0.0-pre'

@ -1,27 +0,0 @@
# Description:
# An example package that contains nested protos that are imported from
# __init__.py. See testPackageInitializationImport in reflection_test.py for
# details.
package(
default_visibility = ["//net/proto2/python/internal:__pkg__"],
)
proto_library(
name = "inner_proto",
srcs = ["inner.proto"],
py_api_version = 2,
)
proto_library(
name = "outer_proto",
srcs = ["outer.proto"],
py_api_version = 2,
deps = [":inner_proto"],
)
py_library(
name = "import_test_package",
srcs = ["__init__.py"],
deps = [":outer_proto"],
)

@ -18,13 +18,7 @@ To build this Ruby extension, you will need:
* a C compiler
* the upb submodule
First, ensure that upb/ is checked out:
$ cd .. # top level protobuf directory
$ git submodule init
$ git submodule update
Then install the required Ruby gems:
First, install the required Ruby gems:
$ sudo gem install bundler rake rake-compiler rspec rubygems-tasks
@ -32,3 +26,7 @@ Then build the Gem:
$ rake gem
$ gem install pkg/protobuf-$VERSION.gem
This gem includes the upb parsing and serialization library as a single-file
amalgamation. It is up-to-date with upb git commit
`535bc2fe2f2b467f59347ffc9449e11e47791257`.

@ -1,35 +1,19 @@
require "rake/extensiontask"
require "rake/testtask"
spec = Gem::Specification.new do |s|
s.name = "protobuf"
s.version = "2.6.2"
s.licenses = ["BSD"]
s.summary = "Protocol Buffers"
s.description = "Protocol Buffers are Google's data interchange format."
s.authors = ["Protobuf Authors"]
s.email = "protobuf@googlegroups.com"
s.files = ["lib/protobuf_c.so", "lib/protobuf.rb"]
end
spec = Gem::Specification.load("google-protobuf.gemspec")
Rake::ExtensionTask.new("protobuf_c", spec) do |ext|
ext.lib_dir = "lib"
ext.config_script = "extconf.rb"
ext.ext_dir = "ext/google/protobuf_c"
ext.lib_dir = "lib/google"
end
Rake::TestTask.new(:test => :build) do |t|
t.test_files = FileList["tests/*.rb"]
end
task :chmod do
File.chmod(0755, "lib/protobuf_c.so")
end
Gem::PackageTask.new(spec) do |pkg|
end
task :package => :chmod
task :gem => :chmod
task :build => [:clean, :compile]
task :default => [:build]

@ -58,11 +58,15 @@ static upb_def* check_notfrozen(const upb_def* def) {
}
static upb_msgdef* check_msg_notfrozen(const upb_msgdef* def) {
return (upb_msgdef*)check_notfrozen((const upb_def*)def);
return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def*)def));
}
static upb_fielddef* check_field_notfrozen(const upb_fielddef* def) {
return (upb_fielddef*)check_notfrozen((const upb_def*)def);
return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def*)def));
}
static upb_oneofdef* check_oneof_notfrozen(const upb_oneofdef* def) {
return (upb_oneofdef*)check_notfrozen((const upb_def*)def);
}
static upb_enumdef* check_enum_notfrozen(const upb_enumdef* def) {
@ -226,6 +230,7 @@ DEFINE_CLASS(Descriptor, "Google::Protobuf::Descriptor");
void Descriptor_mark(void* _self) {
Descriptor* self = _self;
rb_gc_mark(self->klass);
rb_gc_mark(self->typeclass_references);
}
void Descriptor_free(void* _self) {
@ -270,6 +275,7 @@ VALUE Descriptor_alloc(VALUE klass) {
self->fill_method = NULL;
self->pb_serialize_handlers = NULL;
self->json_serialize_handlers = NULL;
self->typeclass_references = rb_ary_new();
return ret;
}
@ -280,6 +286,9 @@ void Descriptor_register(VALUE module) {
rb_define_method(klass, "each", Descriptor_each, 0);
rb_define_method(klass, "lookup", Descriptor_lookup, 1);
rb_define_method(klass, "add_field", Descriptor_add_field, 1);
rb_define_method(klass, "add_oneof", Descriptor_add_oneof, 1);
rb_define_method(klass, "each_oneof", Descriptor_each_oneof, 0);
rb_define_method(klass, "lookup_oneof", Descriptor_lookup_oneof, 1);
rb_define_method(klass, "msgclass", Descriptor_msgclass, 0);
rb_define_method(klass, "name", Descriptor_name, 0);
rb_define_method(klass, "name=", Descriptor_name_set, 1);
@ -326,10 +335,10 @@ VALUE Descriptor_name_set(VALUE _self, VALUE str) {
VALUE Descriptor_each(VALUE _self) {
DEFINE_SELF(Descriptor, self, _self);
upb_msg_iter it;
for (upb_msg_begin(&it, self->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
upb_msg_field_iter it;
for (upb_msg_field_begin(&it, self->msgdef);
!upb_msg_field_done(&it);
upb_msg_field_next(&it)) {
const upb_fielddef* field = upb_msg_iter_field(&it);
VALUE obj = get_def_obj(field);
rb_yield(obj);
@ -358,7 +367,7 @@ VALUE Descriptor_lookup(VALUE _self, VALUE name) {
* call-seq:
* Descriptor.add_field(field) => nil
*
* Adds the given FieldDescriptor to this message type. The descriptor must not
* Adds the given FieldDescriptor to this message type. This descriptor must not
* have been added to a pool yet. Raises an exception if a field with the same
* name or number already exists. Sub-type references (e.g. for fields of type
* message) are not resolved at this point.
@ -375,6 +384,67 @@ VALUE Descriptor_add_field(VALUE _self, VALUE obj) {
return Qnil;
}
/*
* call-seq:
* Descriptor.add_oneof(oneof) => nil
*
* Adds the given OneofDescriptor to this message type. This descriptor must not
* have been added to a pool yet. Raises an exception if a oneof with the same
* name already exists, or if any of the oneof's fields' names or numbers
* conflict with an existing field in this message type. All fields in the oneof
* are added to the message descriptor. Sub-type references (e.g. for fields of
* type message) are not resolved at this point.
*/
VALUE Descriptor_add_oneof(VALUE _self, VALUE obj) {
DEFINE_SELF(Descriptor, self, _self);
upb_msgdef* mut_def = check_msg_notfrozen(self->msgdef);
OneofDescriptor* def = ruby_to_OneofDescriptor(obj);
upb_oneofdef* mut_oneof_def = check_oneof_notfrozen(def->oneofdef);
CHECK_UPB(
upb_msgdef_addoneof(mut_def, mut_oneof_def, NULL, &status),
"Adding oneof to Descriptor failed");
add_def_obj(def->oneofdef, obj);
return Qnil;
}
/*
* call-seq:
* Descriptor.each_oneof(&block) => nil
*
* Invokes the given block for each oneof in this message type, passing the
* corresponding OneofDescriptor.
*/
VALUE Descriptor_each_oneof(VALUE _self) {
DEFINE_SELF(Descriptor, self, _self);
upb_msg_oneof_iter it;
for (upb_msg_oneof_begin(&it, self->msgdef);
!upb_msg_oneof_done(&it);
upb_msg_oneof_next(&it)) {
const upb_oneofdef* oneof = upb_msg_iter_oneof(&it);
VALUE obj = get_def_obj(oneof);
rb_yield(obj);
}
return Qnil;
}
/*
* call-seq:
* Descriptor.lookup_oneof(name) => OneofDescriptor
*
* Returns the oneof descriptor for the oneof with the given name, if present,
* or nil if none.
*/
VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name) {
DEFINE_SELF(Descriptor, self, _self);
const char* s = get_str(name);
const upb_oneofdef* oneof = upb_msgdef_ntooz(self->msgdef, s);
if (oneof == NULL) {
return Qnil;
}
return get_def_obj(oneof);
}
/*
* call-seq:
* Descriptor.msgclass => message_klass
@ -741,6 +811,120 @@ VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value) {
return Qnil;
}
// -----------------------------------------------------------------------------
// OneofDescriptor.
// -----------------------------------------------------------------------------
DEFINE_CLASS(OneofDescriptor, "Google::Protobuf::OneofDescriptor");
void OneofDescriptor_mark(void* _self) {
}
void OneofDescriptor_free(void* _self) {
OneofDescriptor* self = _self;
upb_oneofdef_unref(self->oneofdef, &self->oneofdef);
xfree(self);
}
/*
* call-seq:
* OneofDescriptor.new => oneof_descriptor
*
* Creates a new, empty, oneof descriptor. The oneof may only be modified prior
* to being added to a message descriptor which is subsequently added to a pool.
*/
VALUE OneofDescriptor_alloc(VALUE klass) {
OneofDescriptor* self = ALLOC(OneofDescriptor);
VALUE ret = TypedData_Wrap_Struct(klass, &_OneofDescriptor_type, self);
self->oneofdef = upb_oneofdef_new(&self->oneofdef);
return ret;
}
void OneofDescriptor_register(VALUE module) {
VALUE klass = rb_define_class_under(
module, "OneofDescriptor", rb_cObject);
rb_define_alloc_func(klass, OneofDescriptor_alloc);
rb_define_method(klass, "name", OneofDescriptor_name, 0);
rb_define_method(klass, "name=", OneofDescriptor_name_set, 1);
rb_define_method(klass, "add_field", OneofDescriptor_add_field, 1);
rb_define_method(klass, "each", OneofDescriptor_each, 0);
rb_include_module(klass, rb_mEnumerable);
cOneofDescriptor = klass;
rb_gc_register_address(&cOneofDescriptor);
}
/*
* call-seq:
* OneofDescriptor.name => name
*
* Returns the name of this oneof.
*/
VALUE OneofDescriptor_name(VALUE _self) {
DEFINE_SELF(OneofDescriptor, self, _self);
return rb_str_maybe_null(upb_oneofdef_name(self->oneofdef));
}
/*
* call-seq:
* OneofDescriptor.name = name
*
* Sets a new name for this oneof. The oneof must not have been added to a
* message descriptor yet.
*/
VALUE OneofDescriptor_name_set(VALUE _self, VALUE value) {
DEFINE_SELF(OneofDescriptor, self, _self);
upb_oneofdef* mut_def = check_oneof_notfrozen(self->oneofdef);
const char* str = get_str(value);
CHECK_UPB(upb_oneofdef_setname(mut_def, str, &status),
"Error setting oneof name");
return Qnil;
}
/*
* call-seq:
* OneofDescriptor.add_field(field) => nil
*
* Adds a field to this oneof. The field may have been added to this oneof in
* the past, or the message to which this oneof belongs (if any), but may not
* have already been added to any other oneof or message. Otherwise, an
* exception is raised.
*
* All fields added to the oneof via this method will be automatically added to
* the message to which this oneof belongs, if it belongs to one currently, or
* else will be added to any message to which the oneof is later added at the
* time that it is added.
*/
VALUE OneofDescriptor_add_field(VALUE _self, VALUE obj) {
DEFINE_SELF(OneofDescriptor, self, _self);
upb_oneofdef* mut_def = check_oneof_notfrozen(self->oneofdef);
FieldDescriptor* def = ruby_to_FieldDescriptor(obj);
upb_fielddef* mut_field_def = check_field_notfrozen(def->fielddef);
CHECK_UPB(
upb_oneofdef_addfield(mut_def, mut_field_def, NULL, &status),
"Adding field to OneofDescriptor failed");
add_def_obj(def->fielddef, obj);
return Qnil;
}
/*
* call-seq:
* OneofDescriptor.each(&block) => nil
*
* Iterates through fields in this oneof, yielding to the block on each one.
*/
VALUE OneofDescriptor_each(VALUE _self, VALUE field) {
DEFINE_SELF(OneofDescriptor, self, _self);
upb_oneof_iter it;
for (upb_oneof_begin(&it, self->oneofdef);
!upb_oneof_done(&it);
upb_oneof_next(&it)) {
const upb_fielddef* f = upb_oneof_iter_field(&it);
VALUE obj = get_def_obj(f);
rb_yield(obj);
}
return Qnil;
}
// -----------------------------------------------------------------------------
// EnumDescriptor.
// -----------------------------------------------------------------------------
@ -923,6 +1107,7 @@ DEFINE_CLASS(MessageBuilderContext,
void MessageBuilderContext_mark(void* _self) {
MessageBuilderContext* self = _self;
rb_gc_mark(self->descriptor);
rb_gc_mark(self->builder);
}
void MessageBuilderContext_free(void* _self) {
@ -935,6 +1120,7 @@ VALUE MessageBuilderContext_alloc(VALUE klass) {
VALUE ret = TypedData_Wrap_Struct(
klass, &_MessageBuilderContext_type, self);
self->descriptor = Qnil;
self->builder = Qnil;
return ret;
}
@ -943,24 +1129,30 @@ void MessageBuilderContext_register(VALUE module) {
module, "MessageBuilderContext", rb_cObject);
rb_define_alloc_func(klass, MessageBuilderContext_alloc);
rb_define_method(klass, "initialize",
MessageBuilderContext_initialize, 1);
MessageBuilderContext_initialize, 2);
rb_define_method(klass, "optional", MessageBuilderContext_optional, -1);
rb_define_method(klass, "required", MessageBuilderContext_required, -1);
rb_define_method(klass, "repeated", MessageBuilderContext_repeated, -1);
rb_define_method(klass, "map", MessageBuilderContext_map, -1);
rb_define_method(klass, "oneof", MessageBuilderContext_oneof, 1);
cMessageBuilderContext = klass;
rb_gc_register_address(&cMessageBuilderContext);
}
/*
* call-seq:
* MessageBuilderContext.new(desc) => context
* MessageBuilderContext.new(desc, builder) => context
*
* Create a new builder context around the given message descriptor. This class
* is intended to serve as a DSL context to be used with #instance_eval.
* Create a new message builder context around the given message descriptor and
* builder context. This class is intended to serve as a DSL context to be used
* with #instance_eval.
*/
VALUE MessageBuilderContext_initialize(VALUE _self, VALUE msgdef) {
VALUE MessageBuilderContext_initialize(VALUE _self,
VALUE msgdef,
VALUE builder) {
DEFINE_SELF(MessageBuilderContext, self, _self);
self->descriptor = msgdef;
self->builder = builder;
return Qnil;
}
@ -1065,6 +1257,201 @@ VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) {
name, type, number, type_class);
}
/*
* call-seq:
* 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 number, and type class (for message and enum value types). The key
* type must be :int32/:uint32/:int64/:uint64, :bool, or :string. The value type
* type must be a Ruby symbol (as accepted by FieldDescriptor#type=) and the
* type_class must be a string, if present (as accepted by
* FieldDescriptor#submsg_name=).
*/
VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self) {
DEFINE_SELF(MessageBuilderContext, self, _self);
if (argc < 4) {
rb_raise(rb_eArgError, "Expected at least 4 arguments.");
}
VALUE name = argv[0];
VALUE key_type = argv[1];
VALUE value_type = argv[2];
VALUE number = argv[3];
VALUE type_class = (argc > 4) ? argv[4] : Qnil;
// Validate the key type. We can't accept enums, messages, or floats/doubles
// as map keys. (We exclude these explicitly, and the field-descriptor setter
// below then ensures that the type is one of the remaining valid options.)
if (SYM2ID(key_type) == rb_intern("float") ||
SYM2ID(key_type) == rb_intern("double") ||
SYM2ID(key_type) == rb_intern("enum") ||
SYM2ID(key_type) == rb_intern("message")) {
rb_raise(rb_eArgError,
"Cannot add a map field with a float, double, enum, or message "
"type.");
}
// Create a new message descriptor for the map entry message, and create a
// repeated submessage field here with that type.
VALUE mapentry_desc = rb_class_new_instance(0, NULL, cDescriptor);
VALUE mapentry_desc_name = rb_funcall(self->descriptor, rb_intern("name"), 0);
mapentry_desc_name = rb_str_cat2(mapentry_desc_name, "_MapEntry_");
mapentry_desc_name = rb_str_cat2(mapentry_desc_name,
rb_id2name(SYM2ID(name)));
Descriptor_name_set(mapentry_desc, mapentry_desc_name);
// The 'mapentry' attribute has no Ruby setter because we do not want the user
// attempting to DIY the setup below; we want to ensure that the fields are
// correct. So we reach into the msgdef here to set the bit manually.
Descriptor* mapentry_desc_self = ruby_to_Descriptor(mapentry_desc);
upb_msgdef_setmapentry((upb_msgdef*)mapentry_desc_self->msgdef, true);
// optional <type> key = 1;
VALUE key_field = rb_class_new_instance(0, NULL, cFieldDescriptor);
FieldDescriptor_name_set(key_field, rb_str_new2("key"));
FieldDescriptor_label_set(key_field, ID2SYM(rb_intern("optional")));
FieldDescriptor_number_set(key_field, INT2NUM(1));
FieldDescriptor_type_set(key_field, key_type);
Descriptor_add_field(mapentry_desc, key_field);
// optional <type> value = 2;
VALUE value_field = rb_class_new_instance(0, NULL, cFieldDescriptor);
FieldDescriptor_name_set(value_field, rb_str_new2("value"));
FieldDescriptor_label_set(value_field, ID2SYM(rb_intern("optional")));
FieldDescriptor_number_set(value_field, INT2NUM(2));
FieldDescriptor_type_set(value_field, value_type);
if (type_class != Qnil) {
VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute.
submsg_name = rb_str_append(submsg_name, type_class);
FieldDescriptor_submsg_name_set(value_field, submsg_name);
}
Descriptor_add_field(mapentry_desc, value_field);
// Add the map-entry message type to the current builder, and use the type to
// create the map field itself.
Builder* builder_self = ruby_to_Builder(self->builder);
rb_ary_push(builder_self->pending_list, mapentry_desc);
VALUE map_field = rb_class_new_instance(0, NULL, cFieldDescriptor);
VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name)));
FieldDescriptor_name_set(map_field, name_str);
FieldDescriptor_number_set(map_field, number);
FieldDescriptor_label_set(map_field, ID2SYM(rb_intern("repeated")));
FieldDescriptor_type_set(map_field, ID2SYM(rb_intern("message")));
VALUE submsg_name = rb_str_new2("."); // prepend '.' to make name absolute.
submsg_name = rb_str_append(submsg_name, mapentry_desc_name);
FieldDescriptor_submsg_name_set(map_field, submsg_name);
Descriptor_add_field(self->descriptor, map_field);
return Qnil;
}
/*
* call-seq:
* MessageBuilderContext.oneof(name, &block) => nil
*
* Creates a new OneofDescriptor with the given name, creates a
* OneofBuilderContext attached to that OneofDescriptor, evaluates the given
* block in the context of that OneofBuilderContext with #instance_eval, and
* then adds the oneof to the message.
*
* This is the recommended, idiomatic way to build oneof definitions.
*/
VALUE MessageBuilderContext_oneof(VALUE _self, VALUE name) {
DEFINE_SELF(MessageBuilderContext, self, _self);
VALUE oneofdef = rb_class_new_instance(0, NULL, cOneofDescriptor);
VALUE args[2] = { oneofdef, self->builder };
VALUE ctx = rb_class_new_instance(2, args, cOneofBuilderContext);
VALUE block = rb_block_proc();
VALUE name_str = rb_str_new2(rb_id2name(SYM2ID(name)));
rb_funcall(oneofdef, rb_intern("name="), 1, name_str);
rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block);
Descriptor_add_oneof(self->descriptor, oneofdef);
return Qnil;
}
// -----------------------------------------------------------------------------
// OneofBuilderContext.
// -----------------------------------------------------------------------------
DEFINE_CLASS(OneofBuilderContext,
"Google::Protobuf::Internal::OneofBuilderContext");
void OneofBuilderContext_mark(void* _self) {
OneofBuilderContext* self = _self;
rb_gc_mark(self->descriptor);
rb_gc_mark(self->builder);
}
void OneofBuilderContext_free(void* _self) {
OneofBuilderContext* self = _self;
xfree(self);
}
VALUE OneofBuilderContext_alloc(VALUE klass) {
OneofBuilderContext* self = ALLOC(OneofBuilderContext);
VALUE ret = TypedData_Wrap_Struct(
klass, &_OneofBuilderContext_type, self);
self->descriptor = Qnil;
self->builder = Qnil;
return ret;
}
void OneofBuilderContext_register(VALUE module) {
VALUE klass = rb_define_class_under(
module, "OneofBuilderContext", rb_cObject);
rb_define_alloc_func(klass, OneofBuilderContext_alloc);
rb_define_method(klass, "initialize",
OneofBuilderContext_initialize, 2);
rb_define_method(klass, "optional", OneofBuilderContext_optional, -1);
cOneofBuilderContext = klass;
rb_gc_register_address(&cOneofBuilderContext);
}
/*
* call-seq:
* OneofBuilderContext.new(desc, builder) => context
*
* Create a new oneof builder context around the given oneof descriptor and
* builder context. This class is intended to serve as a DSL context to be used
* with #instance_eval.
*/
VALUE OneofBuilderContext_initialize(VALUE _self,
VALUE oneofdef,
VALUE builder) {
DEFINE_SELF(OneofBuilderContext, self, _self);
self->descriptor = oneofdef;
self->builder = builder;
return Qnil;
}
/*
* call-seq:
* OneofBuilderContext.optional(name, type, number, type_class = nil)
*
* Defines a new optional field in this oneof with the given type, tag number,
* and type class (for message and enum fields). The type must be a Ruby symbol
* (as accepted by FieldDescriptor#type=) and the type_class must be a string,
* if present (as accepted by FieldDescriptor#submsg_name=).
*/
VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self) {
DEFINE_SELF(OneofBuilderContext, self, _self);
if (argc < 3) {
rb_raise(rb_eArgError, "Expected at least 3 arguments.");
}
VALUE name = argv[0];
VALUE type = argv[1];
VALUE number = argv[2];
VALUE type_class = (argc > 3) ? argv[3] : Qnil;
return msgdef_add_field(self->descriptor, "optional",
name, type, number, type_class);
}
// -----------------------------------------------------------------------------
// EnumBuilderContext.
// -----------------------------------------------------------------------------
@ -1190,7 +1577,8 @@ void Builder_register(VALUE module) {
VALUE Builder_add_message(VALUE _self, VALUE name) {
DEFINE_SELF(Builder, self, _self);
VALUE msgdef = rb_class_new_instance(0, NULL, cDescriptor);
VALUE ctx = rb_class_new_instance(1, &msgdef, cMessageBuilderContext);
VALUE args[2] = { msgdef, _self };
VALUE ctx = rb_class_new_instance(2, args, cMessageBuilderContext);
VALUE block = rb_block_proc();
rb_funcall(msgdef, rb_intern("name="), 1, name);
rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block);
@ -1221,8 +1609,10 @@ VALUE Builder_add_enum(VALUE _self, VALUE name) {
static void validate_msgdef(const upb_msgdef* msgdef) {
// Verify that no required fields exist. proto3 does not support these.
upb_msg_iter it;
for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) {
upb_msg_field_iter it;
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_label(field) == UPB_LABEL_REQUIRED) {
rb_raise(rb_eTypeError, "Required fields are unsupported in proto3.");

@ -59,16 +59,45 @@ static const void *newsubmsghandlerdata(upb_handlers* h, uint32_t ofs,
return hd;
}
typedef struct {
size_t ofs; // union data slot
size_t case_ofs; // oneof_case field
uint32_t oneof_case_num; // oneof-case number to place in oneof_case field
const upb_msgdef *md; // msgdef, for oneof submessage handler
} oneof_handlerdata_t;
static const void *newoneofhandlerdata(upb_handlers *h,
uint32_t ofs,
uint32_t case_ofs,
const upb_fielddef *f) {
oneof_handlerdata_t *hd = ALLOC(oneof_handlerdata_t);
hd->ofs = ofs;
hd->case_ofs = case_ofs;
// We reuse the field tag number as a oneof union discriminant tag. Note that
// we don't expose these numbers to the user, so the only requirement is that
// we have some unique ID for each union case/possibility. The field tag
// numbers are already present and are easy to use so there's no reason to
// create a separate ID space. In addition, using the field tag number here
// lets us easily look up the field in the oneof accessor.
hd->oneof_case_num = upb_fielddef_number(f);
if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
hd->md = upb_fielddef_msgsubdef(f);
} else {
hd->md = NULL;
}
upb_handlers_addcleanup(h, hd, free);
return hd;
}
// A handler that starts a repeated field. Gets the Repeated*Field instance for
// this field (such an instance always exists even in an empty message).
static void *startseq_handler(void* closure, const void* hd) {
MessageHeader* msg = closure;
const size_t *ofs = hd;
return (void*)DEREF(Message_data(msg), *ofs, VALUE);
return (void*)DEREF(msg, *ofs, VALUE);
}
// Handlers that append primitive values to a repeated field (a regular Ruby
// array for now).
// Handlers that append primitive values to a repeated field.
#define DEFINE_APPEND_HANDLER(type, ctype) \
static bool append##type##_handler(void *closure, const void *hd, \
ctype val) { \
@ -85,7 +114,7 @@ DEFINE_APPEND_HANDLER(int64, int64_t)
DEFINE_APPEND_HANDLER(uint64, uint64_t)
DEFINE_APPEND_HANDLER(double, double)
// Appends a string to a repeated field (a regular Ruby array for now).
// Appends a string to a repeated field.
static void* appendstr_handler(void *closure,
const void *hd,
size_t size_hint) {
@ -96,7 +125,7 @@ static void* appendstr_handler(void *closure,
return (void*)str;
}
// Appends a 'bytes' string to a repeated field (a regular Ruby array for now).
// Appends a 'bytes' string to a repeated field.
static void* appendbytes_handler(void *closure,
const void *hd,
size_t size_hint) {
@ -115,7 +144,7 @@ static void* str_handler(void *closure,
const size_t *ofs = hd;
VALUE str = rb_str_new2("");
rb_enc_associate(str, kRubyStringUtf8Encoding);
DEREF(Message_data(msg), *ofs, VALUE) = str;
DEREF(msg, *ofs, VALUE) = str;
return (void*)str;
}
@ -127,7 +156,7 @@ static void* bytes_handler(void *closure,
const size_t *ofs = hd;
VALUE str = rb_str_new2("");
rb_enc_associate(str, kRubyString8bitEncoding);
DEREF(Message_data(msg), *ofs, VALUE) = str;
DEREF(msg, *ofs, VALUE) = str;
return (void*)str;
}
@ -163,110 +192,391 @@ static void *submsg_handler(void *closure, const void *hd) {
get_def_obj((void*)submsgdata->md);
VALUE subklass = Descriptor_msgclass(subdesc);
if (DEREF(Message_data(msg), submsgdata->ofs, VALUE) == Qnil) {
DEREF(Message_data(msg), submsgdata->ofs, VALUE) =
if (DEREF(msg, submsgdata->ofs, VALUE) == Qnil) {
DEREF(msg, submsgdata->ofs, VALUE) =
rb_class_new_instance(0, NULL, subklass);
}
VALUE submsg_rb = DEREF(Message_data(msg), submsgdata->ofs, VALUE);
VALUE submsg_rb = DEREF(msg, submsgdata->ofs, VALUE);
MessageHeader* submsg;
TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg);
return submsg;
}
static void add_handlers_for_message(const void *closure, upb_handlers *h) {
Descriptor* desc = ruby_to_Descriptor(
get_def_obj((void*)upb_handlers_msgdef(h)));
// Ensure layout exists. We may be invoked to create handlers for a given
// message if we are included as a submsg of another message type before our
// class is actually built, so to work around this, we just create the layout
// (and handlers, in the class-building function) on-demand.
if (desc->layout == NULL) {
desc->layout = create_layout(desc->msgdef);
// Handler data for startmap/endmap handlers.
typedef struct {
size_t ofs;
upb_fieldtype_t key_field_type;
upb_fieldtype_t value_field_type;
// We know that we can hold this reference because the handlerdata has the
// same lifetime as the upb_handlers struct, and the upb_handlers struct holds
// a reference to the upb_msgdef, which in turn has references to its subdefs.
const upb_def* value_field_subdef;
} map_handlerdata_t;
// Temporary frame for map parsing: at the beginning of a map entry message, a
// submsg handler allocates a frame to hold (i) a reference to the Map object
// into which this message will be inserted and (ii) storage slots to
// temporarily hold the key and value for this map entry until the end of the
// submessage. When the submessage ends, another handler is called to insert the
// value into the map.
typedef struct {
VALUE map;
char key_storage[NATIVE_SLOT_MAX_SIZE];
char value_storage[NATIVE_SLOT_MAX_SIZE];
} map_parse_frame_t;
// Handler to begin a map entry: allocates a temporary frame. This is the
// 'startsubmsg' handler on the msgdef that contains the map field.
static void *startmapentry_handler(void *closure, const void *hd) {
MessageHeader* msg = closure;
const map_handlerdata_t* mapdata = hd;
VALUE map_rb = DEREF(msg, mapdata->ofs, VALUE);
map_parse_frame_t* frame = ALLOC(map_parse_frame_t);
frame->map = map_rb;
native_slot_init(mapdata->key_field_type, &frame->key_storage);
native_slot_init(mapdata->value_field_type, &frame->value_storage);
return frame;
}
// Handler to end a map entry: inserts the value defined during the message into
// the map. This is the 'endmsg' handler on the map entry msgdef.
static bool endmap_handler(void *closure, const void *hd, upb_status* s) {
map_parse_frame_t* frame = closure;
const map_handlerdata_t* mapdata = hd;
VALUE key = native_slot_get(
mapdata->key_field_type, Qnil,
&frame->key_storage);
VALUE value_field_typeclass = Qnil;
if (mapdata->value_field_type == UPB_TYPE_MESSAGE ||
mapdata->value_field_type == UPB_TYPE_ENUM) {
value_field_typeclass = get_def_obj(mapdata->value_field_subdef);
}
upb_msg_iter i;
VALUE value = native_slot_get(
mapdata->value_field_type, value_field_typeclass,
&frame->value_storage);
for (upb_msg_begin(&i, desc->msgdef);
!upb_msg_done(&i);
upb_msg_next(&i)) {
const upb_fielddef *f = upb_msg_iter_field(&i);
size_t offset = desc->layout->offsets[upb_fielddef_index(f)];
Map_index_set(frame->map, key, value);
free(frame);
return true;
}
// Allocates a new map_handlerdata_t given the map entry message definition. If
// the offset of the field within the parent message is also given, that is
// added to the handler data as well. Note that this is called *twice* per map
// field: once in the parent message handler setup when setting the startsubmsg
// handler and once in the map entry message handler setup when setting the
// key/value and endmsg handlers. The reason is that there is no easy way to
// pass the handlerdata down to the sub-message handler setup.
static map_handlerdata_t* new_map_handlerdata(
size_t ofs,
const upb_msgdef* mapentry_def,
Descriptor* desc) {
map_handlerdata_t* hd = ALLOC(map_handlerdata_t);
hd->ofs = ofs;
const upb_fielddef* key_field = upb_msgdef_itof(mapentry_def,
MAP_KEY_FIELD);
assert(key_field != NULL);
hd->key_field_type = upb_fielddef_type(key_field);
const upb_fielddef* value_field = upb_msgdef_itof(mapentry_def,
MAP_VALUE_FIELD);
assert(value_field != NULL);
hd->value_field_type = upb_fielddef_type(value_field);
hd->value_field_subdef = upb_fielddef_subdef(value_field);
return hd;
}
// Handlers that set primitive values in oneofs.
#define DEFINE_ONEOF_HANDLER(type, ctype) \
static bool oneof##type##_handler(void *closure, const void *hd, \
ctype val) { \
const oneof_handlerdata_t *oneofdata = hd; \
DEREF(closure, oneofdata->case_ofs, uint32_t) = \
oneofdata->oneof_case_num; \
DEREF(closure, oneofdata->ofs, ctype) = val; \
return true; \
}
DEFINE_ONEOF_HANDLER(bool, bool)
DEFINE_ONEOF_HANDLER(int32, int32_t)
DEFINE_ONEOF_HANDLER(uint32, uint32_t)
DEFINE_ONEOF_HANDLER(float, float)
DEFINE_ONEOF_HANDLER(int64, int64_t)
DEFINE_ONEOF_HANDLER(uint64, uint64_t)
DEFINE_ONEOF_HANDLER(double, double)
if (upb_fielddef_isseq(f)) {
#undef DEFINE_ONEOF_HANDLER
// Handlers for strings in a oneof.
static void *oneofstr_handler(void *closure,
const void *hd,
size_t size_hint) {
MessageHeader* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
VALUE str = rb_str_new2("");
rb_enc_associate(str, kRubyStringUtf8Encoding);
DEREF(msg, oneofdata->case_ofs, uint32_t) =
oneofdata->oneof_case_num;
DEREF(msg, oneofdata->ofs, VALUE) = str;
return (void*)str;
}
static void *oneofbytes_handler(void *closure,
const void *hd,
size_t size_hint) {
MessageHeader* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
VALUE str = rb_str_new2("");
rb_enc_associate(str, kRubyString8bitEncoding);
DEREF(msg, oneofdata->case_ofs, uint32_t) =
oneofdata->oneof_case_num;
DEREF(msg, oneofdata->ofs, VALUE) = str;
return (void*)str;
}
// Handler for a submessage field in a oneof.
static void *oneofsubmsg_handler(void *closure,
const void *hd) {
MessageHeader* msg = closure;
const oneof_handlerdata_t *oneofdata = hd;
uint32_t oldcase = DEREF(msg, oneofdata->case_ofs, uint32_t);
VALUE subdesc =
get_def_obj((void*)oneofdata->md);
VALUE subklass = Descriptor_msgclass(subdesc);
if (oldcase != oneofdata->oneof_case_num ||
DEREF(msg, oneofdata->ofs, VALUE) == Qnil) {
DEREF(msg, oneofdata->ofs, VALUE) =
rb_class_new_instance(0, NULL, subklass);
}
// Set the oneof case *after* allocating the new class instance -- otherwise,
// if the Ruby GC is invoked as part of a call into the VM, it might invoke
// our mark routines, and our mark routines might see the case value
// 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;
VALUE submsg_rb = DEREF(msg, oneofdata->ofs, VALUE);
MessageHeader* submsg;
TypedData_Get_Struct(submsg_rb, MessageHeader, &Message_type, submsg);
return submsg;
}
// Set up handlers for a repeated field.
static void add_handlers_for_repeated_field(upb_handlers *h,
const upb_fielddef *f,
size_t offset) {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
upb_handlers_setstartseq(h, f, startseq_handler, &attr);
upb_handlerattr_uninit(&attr);
switch (upb_fielddef_type(f)) {
#define SET_HANDLER(utype, ltype) \
case utype: \
upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \
break;
SET_HANDLER(UPB_TYPE_BOOL, bool);
SET_HANDLER(UPB_TYPE_INT32, int32);
SET_HANDLER(UPB_TYPE_UINT32, uint32);
SET_HANDLER(UPB_TYPE_ENUM, int32);
SET_HANDLER(UPB_TYPE_FLOAT, float);
SET_HANDLER(UPB_TYPE_INT64, int64);
SET_HANDLER(UPB_TYPE_UINT64, uint64);
SET_HANDLER(UPB_TYPE_DOUBLE, double);
#undef SET_HANDLER
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
upb_handlers_setstartstr(h, f, is_bytes ?
appendbytes_handler : appendstr_handler,
NULL);
upb_handlers_setstring(h, f, stringdata_handler, NULL);
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f));
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
}
}
// Set up handlers for a singular field.
static void add_handlers_for_singular_field(upb_handlers *h,
const upb_fielddef *f,
size_t offset) {
switch (upb_fielddef_type(f)) {
case UPB_TYPE_BOOL:
case UPB_TYPE_INT32:
case UPB_TYPE_UINT32:
case UPB_TYPE_ENUM:
case UPB_TYPE_FLOAT:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT64:
case UPB_TYPE_DOUBLE:
upb_shim_set(h, f, offset, -1);
break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
upb_handlers_setstartseq(h, f, startseq_handler, &attr);
upb_handlers_setstartstr(h, f,
is_bytes ? bytes_handler : str_handler,
&attr);
upb_handlers_setstring(h, f, stringdata_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f));
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
}
}
switch (upb_fielddef_type(f)) {
// Adds handlers to a map field.
static void add_handlers_for_mapfield(upb_handlers* h,
const upb_fielddef* fielddef,
size_t offset,
Descriptor* desc) {
const upb_msgdef* map_msgdef = upb_fielddef_msgsubdef(fielddef);
map_handlerdata_t* hd = new_map_handlerdata(offset, map_msgdef, desc);
upb_handlers_addcleanup(h, hd, free);
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, hd);
upb_handlers_setstartsubmsg(h, fielddef, startmapentry_handler, &attr);
upb_handlerattr_uninit(&attr);
}
// Adds handlers to a map-entry msgdef.
static void add_handlers_for_mapentry(const upb_msgdef* msgdef,
upb_handlers* h,
Descriptor* desc) {
const upb_fielddef* key_field = map_entry_key(msgdef);
const upb_fielddef* value_field = map_entry_value(msgdef);
map_handlerdata_t* hd = new_map_handlerdata(0, msgdef, desc);
upb_handlers_addcleanup(h, hd, free);
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, hd);
upb_handlers_setendmsg(h, endmap_handler, &attr);
add_handlers_for_singular_field(
h, key_field,
offsetof(map_parse_frame_t, key_storage));
add_handlers_for_singular_field(
h, value_field,
offsetof(map_parse_frame_t, value_storage));
}
// Set up handlers for a oneof field.
static void add_handlers_for_oneof_field(upb_handlers *h,
const upb_fielddef *f,
size_t offset,
size_t oneof_case_offset) {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(
&attr, newoneofhandlerdata(h, offset, oneof_case_offset, f));
switch (upb_fielddef_type(f)) {
#define SET_HANDLER(utype, ltype) \
case utype: \
upb_handlers_set##ltype(h, f, append##ltype##_handler, NULL); \
upb_handlers_set##ltype(h, f, oneof##ltype##_handler, &attr); \
break;
SET_HANDLER(UPB_TYPE_BOOL, bool);
SET_HANDLER(UPB_TYPE_INT32, int32);
SET_HANDLER(UPB_TYPE_UINT32, uint32);
SET_HANDLER(UPB_TYPE_ENUM, int32);
SET_HANDLER(UPB_TYPE_FLOAT, float);
SET_HANDLER(UPB_TYPE_INT64, int64);
SET_HANDLER(UPB_TYPE_UINT64, uint64);
SET_HANDLER(UPB_TYPE_DOUBLE, double);
SET_HANDLER(UPB_TYPE_BOOL, bool);
SET_HANDLER(UPB_TYPE_INT32, int32);
SET_HANDLER(UPB_TYPE_UINT32, uint32);
SET_HANDLER(UPB_TYPE_ENUM, int32);
SET_HANDLER(UPB_TYPE_FLOAT, float);
SET_HANDLER(UPB_TYPE_INT64, int64);
SET_HANDLER(UPB_TYPE_UINT64, uint64);
SET_HANDLER(UPB_TYPE_DOUBLE, double);
#undef SET_HANDLER
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
upb_handlers_setstartstr(h, f, is_bytes ?
appendbytes_handler : appendstr_handler,
NULL);
upb_handlers_setstring(h, f, stringdata_handler, NULL);
}
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, 0, f));
upb_handlers_setstartsubmsg(h, f, appendsubmsg_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
}
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
upb_handlers_setstartstr(h, f, is_bytes ?
oneofbytes_handler : oneofstr_handler,
&attr);
upb_handlers_setstring(h, f, stringdata_handler, NULL);
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlers_setstartsubmsg(h, f, oneofsubmsg_handler, &attr);
break;
}
}
switch (upb_fielddef_type(f)) {
case UPB_TYPE_BOOL:
case UPB_TYPE_INT32:
case UPB_TYPE_UINT32:
case UPB_TYPE_ENUM:
case UPB_TYPE_FLOAT:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT64:
case UPB_TYPE_DOUBLE:
// The shim writes directly at the given offset (instead of using
// DEREF()) so we need to add the msg overhead.
upb_shim_set(h, f, offset + sizeof(MessageHeader), -1);
break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES: {
bool is_bytes = upb_fielddef_type(f) == UPB_TYPE_BYTES;
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, offset));
upb_handlers_setstartstr(h, f,
is_bytes ? bytes_handler : str_handler,
&attr);
upb_handlers_setstring(h, f, stringdata_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
case UPB_TYPE_MESSAGE: {
upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
upb_handlerattr_sethandlerdata(&attr, newsubmsghandlerdata(h, offset, f));
upb_handlers_setstartsubmsg(h, f, submsg_handler, &attr);
upb_handlerattr_uninit(&attr);
break;
}
upb_handlerattr_uninit(&attr);
}
static void add_handlers_for_message(const void *closure, upb_handlers *h) {
const upb_msgdef* msgdef = upb_handlers_msgdef(h);
Descriptor* desc = ruby_to_Descriptor(get_def_obj((void*)msgdef));
// If this is a mapentry message type, set up a special set of handlers and
// bail out of the normal (user-defined) message type handling.
if (upb_msgdef_mapentry(msgdef)) {
add_handlers_for_mapentry(msgdef, h, desc);
return;
}
// Ensure layout exists. We may be invoked to create handlers for a given
// message if we are included as a submsg of another message type before our
// class is actually built, so to work around this, we just create the layout
// (and handlers, in the class-building function) on-demand.
if (desc->layout == NULL) {
desc->layout = create_layout(desc->msgdef);
}
upb_msg_field_iter i;
for (upb_msg_field_begin(&i, desc->msgdef);
!upb_msg_field_done(&i);
upb_msg_field_next(&i)) {
const upb_fielddef *f = upb_msg_iter_field(&i);
size_t offset = desc->layout->fields[upb_fielddef_index(f)].offset +
sizeof(MessageHeader);
if (upb_fielddef_containingoneof(f)) {
size_t oneof_case_offset =
desc->layout->fields[upb_fielddef_index(f)].case_offset +
sizeof(MessageHeader);
add_handlers_for_oneof_field(h, f, offset, oneof_case_offset);
} else if (is_map_field(f)) {
add_handlers_for_mapfield(h, f, offset, desc);
} else if (upb_fielddef_isseq(f)) {
add_handlers_for_repeated_field(h, f, offset);
} else {
add_handlers_for_singular_field(h, f, offset);
}
}
}
@ -558,6 +868,88 @@ static void putary(VALUE ary, const upb_fielddef *f, upb_sink *sink,
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
}
static void put_ruby_value(VALUE value,
const upb_fielddef *f,
VALUE type_class,
int depth,
upb_sink *sink) {
upb_selector_t sel = 0;
if (upb_fielddef_isprimitive(f)) {
sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
}
switch (upb_fielddef_type(f)) {
case UPB_TYPE_INT32:
upb_sink_putint32(sink, sel, NUM2INT(value));
break;
case UPB_TYPE_INT64:
upb_sink_putint64(sink, sel, NUM2LL(value));
break;
case UPB_TYPE_UINT32:
upb_sink_putuint32(sink, sel, NUM2UINT(value));
break;
case UPB_TYPE_UINT64:
upb_sink_putuint64(sink, sel, NUM2ULL(value));
break;
case UPB_TYPE_FLOAT:
upb_sink_putfloat(sink, sel, NUM2DBL(value));
break;
case UPB_TYPE_DOUBLE:
upb_sink_putdouble(sink, sel, NUM2DBL(value));
break;
case UPB_TYPE_ENUM: {
if (TYPE(value) == T_SYMBOL) {
value = rb_funcall(type_class, rb_intern("resolve"), 1, value);
}
upb_sink_putint32(sink, sel, NUM2INT(value));
break;
}
case UPB_TYPE_BOOL:
upb_sink_putbool(sink, sel, value == Qtrue);
break;
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
putstr(value, f, sink);
break;
case UPB_TYPE_MESSAGE:
putsubmsg(value, f, sink, depth);
}
}
static void putmap(VALUE map, const upb_fielddef *f, upb_sink *sink,
int depth) {
if (map == Qnil) return;
Map* self = ruby_to_Map(map);
upb_sink subsink;
upb_sink_startseq(sink, getsel(f, UPB_HANDLER_STARTSEQ), &subsink);
assert(upb_fielddef_type(f) == UPB_TYPE_MESSAGE);
const upb_fielddef* key_field = map_field_key(f);
const upb_fielddef* value_field = map_field_value(f);
Map_iter it;
for (Map_begin(map, &it); !Map_done(&it); Map_next(&it)) {
VALUE key = Map_iter_key(&it);
VALUE value = Map_iter_value(&it);
upb_sink entry_sink;
upb_sink_startsubmsg(&subsink, getsel(f, UPB_HANDLER_STARTSUBMSG), &entry_sink);
upb_sink_startmsg(&entry_sink);
put_ruby_value(key, key_field, Qnil, depth + 1, &entry_sink);
put_ruby_value(value, value_field, self->value_type_class, depth + 1,
&entry_sink);
upb_status status;
upb_sink_endmsg(&entry_sink, &status);
upb_sink_endsubmsg(&subsink, getsel(f, UPB_HANDLER_ENDSUBMSG));
}
upb_sink_endseq(sink, getsel(f, UPB_HANDLER_ENDSEQ));
}
static void putmsg(VALUE msg_rb, const Descriptor* desc,
upb_sink *sink, int depth) {
upb_sink_startmsg(sink);
@ -571,33 +963,53 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc,
MessageHeader* msg;
TypedData_Get_Struct(msg_rb, MessageHeader, &Message_type, msg);
void* msg_data = Message_data(msg);
upb_msg_iter i;
for (upb_msg_begin(&i, desc->msgdef);
!upb_msg_done(&i);
upb_msg_next(&i)) {
upb_msg_field_iter i;
for (upb_msg_field_begin(&i, desc->msgdef);
!upb_msg_field_done(&i);
upb_msg_field_next(&i)) {
upb_fielddef *f = upb_msg_iter_field(&i);
uint32_t offset = desc->layout->offsets[upb_fielddef_index(f)];
uint32_t offset =
desc->layout->fields[upb_fielddef_index(f)].offset +
sizeof(MessageHeader);
if (upb_fielddef_containingoneof(f)) {
uint32_t oneof_case_offset =
desc->layout->fields[upb_fielddef_index(f)].case_offset +
sizeof(MessageHeader);
// For a oneof, check that this field is actually present -- skip all the
// below if not.
if (DEREF(msg, oneof_case_offset, uint32_t) !=
upb_fielddef_number(f)) {
continue;
}
// Otherwise, fall through to the appropriate singular-field handler
// below.
}
if (upb_fielddef_isseq(f)) {
VALUE ary = DEREF(msg_data, offset, VALUE);
if (is_map_field(f)) {
VALUE map = DEREF(msg, offset, VALUE);
if (map != Qnil) {
putmap(map, f, sink, depth);
}
} else if (upb_fielddef_isseq(f)) {
VALUE ary = DEREF(msg, offset, VALUE);
if (ary != Qnil) {
putary(ary, f, sink, depth);
}
} else if (upb_fielddef_isstring(f)) {
VALUE str = DEREF(msg_data, offset, VALUE);
VALUE str = DEREF(msg, offset, VALUE);
if (RSTRING_LEN(str) > 0) {
putstr(str, f, sink);
}
} else if (upb_fielddef_issubmsg(f)) {
putsubmsg(DEREF(msg_data, offset, VALUE), f, sink, depth);
putsubmsg(DEREF(msg, offset, VALUE), f, sink, depth);
} else {
upb_selector_t sel = getsel(f, upb_handlers_getprimitivehandlertype(f));
#define T(upbtypeconst, upbtype, ctype, default_value) \
case upbtypeconst: { \
ctype value = DEREF(msg_data, offset, ctype); \
ctype value = DEREF(msg, offset, ctype); \
if (value != default_value) { \
upb_sink_put##upbtype(sink, sel, value); \
} \

@ -0,0 +1,10 @@
#!/usr/bin/ruby
require 'mkmf'
$CFLAGS += " -O3 -std=c99 -Wno-unused-function -DNDEBUG "
$objs = ["protobuf.o", "defs.o", "storage.o", "message.o",
"repeated_field.o", "map.o", "encode_decode.o", "upb.o"]
create_makefile("google/protobuf_c")

@ -0,0 +1,805 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2014 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "protobuf.h"
// -----------------------------------------------------------------------------
// Basic map operations on top of upb's strtable.
//
// Note that we roll our own `Map` container here because, as for
// `RepeatedField`, we want a strongly-typed container. This is so that any user
// errors due to incorrect map key or value types are raised as close as
// possible to the error site, rather than at some deferred point (e.g.,
// serialization).
//
// We build our `Map` on top of upb_strtable so that we're able to take
// advantage of the native_slot storage abstraction, as RepeatedField does.
// (This is not quite a perfect mapping -- see the key conversions below -- but
// gives us full support and error-checking for all value types for free.)
// -----------------------------------------------------------------------------
// Map values are stored using the native_slot abstraction (as with repeated
// field values), but keys are a bit special. Since we use a strtable, we need
// to store keys as sequences of bytes such that equality of those bytes maps
// one-to-one to equality of keys. We store strings directly (i.e., they map to
// their own bytes) and integers as native integers (using the native_slot
// abstraction).
// Note that there is another tradeoff here in keeping string keys as native
// strings rather than Ruby strings: traversing the Map requires conversion to
// Ruby string values on every traversal, potentially creating more garbage. We
// should consider ways to cache a Ruby version of the key if this becomes an
// issue later.
// Forms a key to use with the underlying strtable from a Ruby key value. |buf|
// must point to TABLE_KEY_BUF_LENGTH bytes of temporary space, used to
// construct a key byte sequence if needed. |out_key| and |out_length| provide
// the resulting key data/length.
#define TABLE_KEY_BUF_LENGTH 8 // sizeof(uint64_t)
static void table_key(Map* self, VALUE key,
char* buf,
const char** out_key,
size_t* out_length) {
switch (self->key_type) {
case UPB_TYPE_BYTES:
case UPB_TYPE_STRING:
// Strings: use string content directly.
Check_Type(key, T_STRING);
native_slot_validate_string_encoding(self->key_type, key);
*out_key = RSTRING_PTR(key);
*out_length = RSTRING_LEN(key);
break;
case UPB_TYPE_BOOL:
case UPB_TYPE_INT32:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT32:
case UPB_TYPE_UINT64:
native_slot_set(self->key_type, Qnil, buf, key);
*out_key = buf;
*out_length = native_slot_size(self->key_type);
break;
default:
// Map constructor should not allow a Map with another key type to be
// constructed.
assert(false);
break;
}
}
static VALUE table_key_to_ruby(Map* self, const char* buf, size_t length) {
switch (self->key_type) {
case UPB_TYPE_BYTES:
case UPB_TYPE_STRING: {
VALUE ret = rb_str_new(buf, length);
rb_enc_associate(ret,
(self->key_type == UPB_TYPE_BYTES) ?
kRubyString8bitEncoding : kRubyStringUtf8Encoding);
return ret;
}
case UPB_TYPE_BOOL:
case UPB_TYPE_INT32:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT32:
case UPB_TYPE_UINT64:
return native_slot_get(self->key_type, Qnil, buf);
default:
assert(false);
return Qnil;
}
}
static void* value_memory(upb_value* v) {
return (void*)(&v->val.uint64);
}
// -----------------------------------------------------------------------------
// Map container type.
// -----------------------------------------------------------------------------
const rb_data_type_t Map_type = {
"Google::Protobuf::Map",
{ Map_mark, Map_free, NULL },
};
VALUE cMap;
Map* ruby_to_Map(VALUE _self) {
Map* self;
TypedData_Get_Struct(_self, Map, &Map_type, self);
return self;
}
void Map_mark(void* _self) {
Map* self = _self;
rb_gc_mark(self->value_type_class);
if (self->value_type == UPB_TYPE_STRING ||
self->value_type == UPB_TYPE_BYTES ||
self->value_type == UPB_TYPE_MESSAGE) {
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
native_slot_mark(self->value_type, mem);
}
}
}
void Map_free(void* _self) {
Map* self = _self;
upb_strtable_uninit(&self->table);
xfree(self);
}
VALUE Map_alloc(VALUE klass) {
Map* self = ALLOC(Map);
memset(self, 0, sizeof(Map));
self->value_type_class = Qnil;
VALUE ret = TypedData_Wrap_Struct(klass, &Map_type, self);
return ret;
}
static bool needs_typeclass(upb_fieldtype_t type) {
switch (type) {
case UPB_TYPE_MESSAGE:
case UPB_TYPE_ENUM:
return true;
default:
return false;
}
}
/*
* call-seq:
* Map.new(key_type, value_type, value_typeclass = nil, init_hashmap = {})
* => new map
*
* Allocates a new Map container. This constructor may be called with 2, 3, or 4
* arguments. The first two arguments are always present and are symbols (taking
* on the same values as field-type symbols in message descriptors) that
* indicate the type of the map key and value fields.
*
* The supported key types are: :int32, :int64, :uint32, :uint64, :bool,
* :string, :bytes.
*
* The supported value types are: :int32, :int64, :uint32, :uint64, :bool,
* :string, :bytes, :enum, :message.
*
* The third argument, value_typeclass, must be present if value_type is :enum
* or :message. As in RepeatedField#new, this argument must be a message class
* (for :message) or enum module (for :enum).
*
* The last argument, if present, provides initial content for map. Note that
* this may be an ordinary Ruby hashmap or another Map instance with identical
* key and value types. Also note that this argument may be present whether or
* not value_typeclass is present (and it is unambiguously separate from
* value_typeclass because value_typeclass's presence is strictly determined by
* value_type). The contents of this initial hashmap or Map instance are
* shallow-copied into the new Map: the original map is unmodified, but
* references to underlying objects will be shared if the value type is a
* message type.
*/
VALUE Map_init(int argc, VALUE* argv, VALUE _self) {
Map* self = ruby_to_Map(_self);
// We take either two args (:key_type, :value_type), three args (:key_type,
// :value_type, "ValueMessageType"), or four args (the above plus an initial
// hashmap).
if (argc < 2 || argc > 4) {
rb_raise(rb_eArgError, "Map constructor expects 2, 3 or 4 arguments.");
}
self->key_type = ruby_to_fieldtype(argv[0]);
self->value_type = ruby_to_fieldtype(argv[1]);
// Check that the key type is an allowed type.
switch (self->key_type) {
case UPB_TYPE_INT32:
case UPB_TYPE_INT64:
case UPB_TYPE_UINT32:
case UPB_TYPE_UINT64:
case UPB_TYPE_BOOL:
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
// These are OK.
break;
default:
rb_raise(rb_eArgError, "Invalid key type for map.");
}
int init_value_arg = 2;
if (needs_typeclass(self->value_type) && argc > 2) {
self->value_type_class = argv[2];
validate_type_class(self->value_type, self->value_type_class);
init_value_arg = 3;
}
// Table value type is always UINT64: this ensures enough space to store the
// native_slot value.
if (!upb_strtable_init(&self->table, UPB_CTYPE_UINT64)) {
rb_raise(rb_eRuntimeError, "Could not allocate table.");
}
if (argc > init_value_arg) {
Map_merge_into_self(_self, argv[init_value_arg]);
}
return Qnil;
}
/*
* call-seq:
* Map.each(&block)
*
* Invokes &block on each |key, value| pair in the map, in unspecified order.
* Note that Map also includes Enumerable; map thus acts like a normal Ruby
* sequence.
*/
VALUE Map_each(VALUE _self) {
Map* self = ruby_to_Map(_self);
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
VALUE key = table_key_to_ruby(
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
VALUE value = native_slot_get(self->value_type,
self->value_type_class,
mem);
rb_yield_values(2, key, value);
}
return Qnil;
}
/*
* call-seq:
* Map.keys => [list_of_keys]
*
* Returns the list of keys contained in the map, in unspecified order.
*/
VALUE Map_keys(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE ret = rb_ary_new();
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
VALUE key = table_key_to_ruby(
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
rb_ary_push(ret, key);
}
return ret;
}
/*
* call-seq:
* Map.values => [list_of_values]
*
* Returns the list of values contained in the map, in unspecified order.
*/
VALUE Map_values(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE ret = rb_ary_new();
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
VALUE value = native_slot_get(self->value_type,
self->value_type_class,
mem);
rb_ary_push(ret, value);
}
return ret;
}
/*
* call-seq:
* Map.[](key) => value
*
* Accesses the element at the given key. Throws an exception if the key type is
* incorrect. Returns nil when the key is not present in the map.
*/
VALUE Map_index(VALUE _self, VALUE key) {
Map* self = ruby_to_Map(_self);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
table_key(self, key, keybuf, &keyval, &length);
upb_value v;
if (upb_strtable_lookup2(&self->table, keyval, length, &v)) {
void* mem = value_memory(&v);
return native_slot_get(self->value_type, self->value_type_class, mem);
} else {
return Qnil;
}
}
/*
* call-seq:
* Map.[]=(key, value) => value
*
* Inserts or overwrites the value at the given key with the given new value.
* Throws an exception if the key type is incorrect. Returns the new value that
* was just inserted.
*/
VALUE Map_index_set(VALUE _self, VALUE key, VALUE value) {
Map* self = ruby_to_Map(_self);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
table_key(self, key, keybuf, &keyval, &length);
upb_value v;
void* mem = value_memory(&v);
native_slot_set(self->value_type, self->value_type_class, mem, value);
// Replace any existing value by issuing a 'remove' operation first.
upb_strtable_remove2(&self->table, keyval, length, NULL);
if (!upb_strtable_insert2(&self->table, keyval, length, v)) {
rb_raise(rb_eRuntimeError, "Could not insert into table");
}
// Ruby hashmap's :[]= method also returns the inserted value.
return value;
}
/*
* call-seq:
* Map.has_key?(key) => bool
*
* Returns true if the given key is present in the map. Throws an exception if
* the key has the wrong type.
*/
VALUE Map_has_key(VALUE _self, VALUE key) {
Map* self = ruby_to_Map(_self);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
table_key(self, key, keybuf, &keyval, &length);
if (upb_strtable_lookup2(&self->table, keyval, length, NULL)) {
return Qtrue;
} else {
return Qfalse;
}
}
/*
* call-seq:
* Map.delete(key) => old_value
*
* Deletes the value at the given key, if any, returning either the old value or
* nil if none was present. Throws an exception if the key is of the wrong type.
*/
VALUE Map_delete(VALUE _self, VALUE key) {
Map* self = ruby_to_Map(_self);
char keybuf[TABLE_KEY_BUF_LENGTH];
const char* keyval = NULL;
size_t length = 0;
table_key(self, key, keybuf, &keyval, &length);
upb_value v;
if (upb_strtable_remove2(&self->table, keyval, length, &v)) {
void* mem = value_memory(&v);
return native_slot_get(self->value_type, self->value_type_class, mem);
} else {
return Qnil;
}
}
/*
* call-seq:
* Map.clear
*
* Removes all entries from the map.
*/
VALUE Map_clear(VALUE _self) {
Map* self = ruby_to_Map(_self);
// Uninit and reinit the table -- this is faster than iterating and doing a
// delete-lookup on each key.
upb_strtable_uninit(&self->table);
if (!upb_strtable_init(&self->table, UPB_CTYPE_INT64)) {
rb_raise(rb_eRuntimeError, "Unable to re-initialize table");
}
return Qnil;
}
/*
* call-seq:
* Map.length
*
* Returns the number of entries (key-value pairs) in the map.
*/
VALUE Map_length(VALUE _self) {
Map* self = ruby_to_Map(_self);
return ULL2NUM(upb_strtable_count(&self->table));
}
static VALUE Map_new_this_type(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE new_map = Qnil;
VALUE key_type = fieldtype_to_ruby(self->key_type);
VALUE value_type = fieldtype_to_ruby(self->value_type);
if (self->value_type_class != Qnil) {
new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 3,
key_type, value_type, self->value_type_class);
} else {
new_map = rb_funcall(CLASS_OF(_self), rb_intern("new"), 2,
key_type, value_type);
}
return new_map;
}
/*
* call-seq:
* Map.dup => new_map
*
* Duplicates this map with a shallow copy. References to all non-primitive
* element objects (e.g., submessages) are shared.
*/
VALUE Map_dup(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE new_map = Map_new_this_type(_self);
Map* new_self = ruby_to_Map(new_map);
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
upb_value dup;
void* dup_mem = value_memory(&dup);
native_slot_dup(self->value_type, dup_mem, mem);
if (!upb_strtable_insert2(&new_self->table,
upb_strtable_iter_key(&it),
upb_strtable_iter_keylength(&it),
dup)) {
rb_raise(rb_eRuntimeError, "Error inserting value into new table");
}
}
return new_map;
}
// Used by Google::Protobuf.deep_copy but not exposed directly.
VALUE Map_deep_copy(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE new_map = Map_new_this_type(_self);
Map* new_self = ruby_to_Map(new_map);
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
upb_value dup;
void* dup_mem = value_memory(&dup);
native_slot_deep_copy(self->value_type, dup_mem, mem);
if (!upb_strtable_insert2(&new_self->table,
upb_strtable_iter_key(&it),
upb_strtable_iter_keylength(&it),
dup)) {
rb_raise(rb_eRuntimeError, "Error inserting value into new table");
}
}
return new_map;
}
/*
* call-seq:
* Map.==(other) => boolean
*
* Compares this map to another. Maps are equal if they have identical key sets,
* and for each key, the values in both maps compare equal. Elements are
* compared as per normal Ruby semantics, by calling their :== methods (or
* performing a more efficient comparison for primitive types).
*
* Maps with dissimilar key types or value types/typeclasses are never equal,
* even if value comparison (for example, between integers and floats) would
* have otherwise indicated that every element has equal value.
*/
VALUE Map_eq(VALUE _self, VALUE _other) {
Map* self = ruby_to_Map(_self);
// Allow comparisons to Ruby hashmaps by converting to a temporary Map
// instance. Slow, but workable.
if (TYPE(_other) == T_HASH) {
VALUE other_map = Map_new_this_type(_self);
Map_merge_into_self(other_map, _other);
_other = other_map;
}
Map* other = ruby_to_Map(_other);
if (self == other) {
return Qtrue;
}
if (self->key_type != other->key_type ||
self->value_type != other->value_type ||
self->value_type_class != other->value_type_class) {
return Qfalse;
}
if (upb_strtable_count(&self->table) != upb_strtable_count(&other->table)) {
return Qfalse;
}
// For each member of self, check that an equal member exists at the same key
// in other.
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
upb_value other_v;
void* other_mem = value_memory(&other_v);
if (!upb_strtable_lookup2(&other->table,
upb_strtable_iter_key(&it),
upb_strtable_iter_keylength(&it),
&other_v)) {
// Not present in other map.
return Qfalse;
}
if (!native_slot_eq(self->value_type, mem, other_mem)) {
// Present, but value not equal.
return Qfalse;
}
}
return Qtrue;
}
/*
* call-seq:
* Map.hash => hash_value
*
* Returns a hash value based on this map's contents.
*/
VALUE Map_hash(VALUE _self) {
Map* self = ruby_to_Map(_self);
st_index_t h = rb_hash_start(0);
VALUE hash_sym = rb_intern("hash");
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
VALUE key = table_key_to_ruby(
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
VALUE value = native_slot_get(self->value_type,
self->value_type_class,
mem);
h = rb_hash_uint(h, NUM2LONG(rb_funcall(key, hash_sym, 0)));
h = rb_hash_uint(h, NUM2LONG(rb_funcall(value, hash_sym, 0)));
}
return INT2FIX(h);
}
/*
* call-seq:
* Map.inspect => string
*
* Returns a string representing this map's elements. It will be formatted as
* "{key => value, key => value, ...}", with each key and value string
* representation computed by its own #inspect method.
*/
VALUE Map_inspect(VALUE _self) {
Map* self = ruby_to_Map(_self);
VALUE str = rb_str_new2("{");
bool first = true;
VALUE inspect_sym = rb_intern("inspect");
upb_strtable_iter it;
for (upb_strtable_begin(&it, &self->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
VALUE key = table_key_to_ruby(
self, upb_strtable_iter_key(&it), upb_strtable_iter_keylength(&it));
upb_value v = upb_strtable_iter_value(&it);
void* mem = value_memory(&v);
VALUE value = native_slot_get(self->value_type,
self->value_type_class,
mem);
if (!first) {
str = rb_str_cat2(str, ", ");
} else {
first = false;
}
str = rb_str_append(str, rb_funcall(key, inspect_sym, 0));
str = rb_str_cat2(str, "=>");
str = rb_str_append(str, rb_funcall(value, inspect_sym, 0));
}
str = rb_str_cat2(str, "}");
return str;
}
/*
* call-seq:
* Map.merge(other_map) => map
*
* Copies key/value pairs from other_map into a copy of this map. If a key is
* set in other_map and this map, the value from other_map overwrites the value
* in the new copy of this map. Returns the new copy of this map with merged
* contents.
*/
VALUE Map_merge(VALUE _self, VALUE hashmap) {
VALUE dupped = Map_dup(_self);
return Map_merge_into_self(dupped, hashmap);
}
static int merge_into_self_callback(VALUE key, VALUE value, VALUE self) {
Map_index_set(self, key, value);
return ST_CONTINUE;
}
// Used only internally -- shared by #merge and #initialize.
VALUE Map_merge_into_self(VALUE _self, VALUE hashmap) {
if (TYPE(hashmap) == T_HASH) {
rb_hash_foreach(hashmap, merge_into_self_callback, _self);
} else if (RB_TYPE_P(hashmap, T_DATA) && RTYPEDDATA_P(hashmap) &&
RTYPEDDATA_TYPE(hashmap) == &Map_type) {
Map* self = ruby_to_Map(_self);
Map* other = ruby_to_Map(hashmap);
if (self->key_type != other->key_type ||
self->value_type != other->value_type ||
self->value_type_class != other->value_type_class) {
rb_raise(rb_eArgError, "Attempt to merge Map with mismatching types");
}
upb_strtable_iter it;
for (upb_strtable_begin(&it, &other->table);
!upb_strtable_done(&it);
upb_strtable_next(&it)) {
// Replace any existing value by issuing a 'remove' operation first.
upb_value oldv;
upb_strtable_remove2(&self->table,
upb_strtable_iter_key(&it),
upb_strtable_iter_keylength(&it),
&oldv);
upb_value v = upb_strtable_iter_value(&it);
upb_strtable_insert2(&self->table,
upb_strtable_iter_key(&it),
upb_strtable_iter_keylength(&it),
v);
}
} else {
rb_raise(rb_eArgError, "Unknown type merging into Map");
}
return _self;
}
// Internal method: map iterator initialization (used for serialization).
void Map_begin(VALUE _self, Map_iter* iter) {
Map* self = ruby_to_Map(_self);
iter->self = self;
upb_strtable_begin(&iter->it, &self->table);
}
void Map_next(Map_iter* iter) {
upb_strtable_next(&iter->it);
}
bool Map_done(Map_iter* iter) {
return upb_strtable_done(&iter->it);
}
VALUE Map_iter_key(Map_iter* iter) {
return table_key_to_ruby(
iter->self,
upb_strtable_iter_key(&iter->it),
upb_strtable_iter_keylength(&iter->it));
}
VALUE Map_iter_value(Map_iter* iter) {
upb_value v = upb_strtable_iter_value(&iter->it);
void* mem = value_memory(&v);
return native_slot_get(iter->self->value_type,
iter->self->value_type_class,
mem);
}
void Map_register(VALUE module) {
VALUE klass = rb_define_class_under(module, "Map", rb_cObject);
rb_define_alloc_func(klass, Map_alloc);
cMap = klass;
rb_gc_register_address(&cMap);
rb_define_method(klass, "initialize", Map_init, -1);
rb_define_method(klass, "each", Map_each, 0);
rb_define_method(klass, "keys", Map_keys, 0);
rb_define_method(klass, "values", Map_values, 0);
rb_define_method(klass, "[]", Map_index, 1);
rb_define_method(klass, "[]=", Map_index_set, 2);
rb_define_method(klass, "has_key?", Map_has_key, 1);
rb_define_method(klass, "delete", Map_delete, 1);
rb_define_method(klass, "clear", Map_clear, 0);
rb_define_method(klass, "length", Map_length, 0);
rb_define_method(klass, "dup", Map_dup, 0);
rb_define_method(klass, "==", Map_eq, 1);
rb_define_method(klass, "hash", Map_hash, 0);
rb_define_method(klass, "inspect", Map_inspect, 0);
rb_define_method(klass, "merge", Map_merge, 1);
rb_include_module(klass, rb_mEnumerable);
}

@ -70,6 +70,35 @@ VALUE Message_alloc(VALUE klass) {
return ret;
}
static VALUE which_oneof_field(MessageHeader* self, const upb_oneofdef* o) {
// If no fields in the oneof, always nil.
if (upb_oneofdef_numfields(o) == 0) {
return Qnil;
}
// Grab the first field in the oneof so we can get its layout info to find the
// oneof_case field.
upb_oneof_iter it;
upb_oneof_begin(&it, o);
assert(!upb_oneof_done(&it));
const upb_fielddef* first_field = upb_oneof_iter_field(&it);
assert(upb_fielddef_containingoneof(first_field) != NULL);
size_t case_ofs =
self->descriptor->layout->
fields[upb_fielddef_index(first_field)].case_offset;
uint32_t oneof_case = *((uint32_t*)(Message_data(self) + case_ofs));
if (oneof_case == ONEOF_CASE_NONE) {
return Qnil;
}
// oneof_case is a field index, so find that field.
const upb_fielddef* f = upb_oneofdef_itof(o, oneof_case);
assert(f != NULL);
return ID2SYM(rb_intern(upb_fielddef_name(f)));
}
/*
* call-seq:
* Message.method_missing(*args)
@ -82,6 +111,10 @@ VALUE Message_alloc(VALUE klass) {
*
* msg.foo = 42
* puts msg.foo
*
* This method also provides read-only accessors for oneofs. If a oneof exists
* with name 'my_oneof', then msg.my_oneof will return a Ruby symbol equal to
* the name of the field in that oneof that is currently set, or nil if none.
*/
VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
MessageHeader* self;
@ -104,6 +137,17 @@ VALUE Message_method_missing(int argc, VALUE* argv, VALUE _self) {
name_len--;
}
// Check for a oneof name first.
const upb_oneofdef* o = upb_msgdef_ntoo(self->descriptor->msgdef,
name, name_len);
if (o != NULL) {
if (setter) {
rb_raise(rb_eRuntimeError, "Oneof accessors are read-only.");
}
return which_oneof_field(self, o);
}
// Otherwise, check for a field with that name.
const upb_fielddef* f = upb_msgdef_ntof(self->descriptor->msgdef,
name, name_len);
@ -139,7 +183,14 @@ int Message_initialize_kwarg(VALUE key, VALUE val, VALUE _self) {
"Unknown field name in initialization map entry.");
}
if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
if (is_map_field(f)) {
if (TYPE(val) != T_HASH) {
rb_raise(rb_eArgError,
"Expected Hash object as initializer value for map field.");
}
VALUE map = layout_get(self->descriptor->layout, Message_data(self), f);
Map_merge_into_self(map, val);
} else if (upb_fielddef_label(f) == UPB_LABEL_REPEATED) {
if (TYPE(val) != T_ARRAY) {
rb_raise(rb_eArgError,
"Expected array as initializer value for repeated field.");
@ -450,13 +501,15 @@ VALUE build_module_from_enumdesc(EnumDescriptor* enumdesc) {
* call-seq:
* Google::Protobuf.deep_copy(obj) => copy_of_obj
*
* Performs a deep copy of either a RepeatedField instance or a message object,
* recursively copying its members.
* Performs a deep copy of a RepeatedField instance, a Map instance, or a
* message object, recursively copying its members.
*/
VALUE Google_Protobuf_deep_copy(VALUE self, VALUE obj) {
VALUE klass = CLASS_OF(obj);
if (klass == cRepeatedField) {
return RepeatedField_deep_copy(obj);
} else if (klass == cMap) {
return Map_deep_copy(obj);
} else {
return Message_deep_copy(obj);
}

@ -77,11 +77,14 @@ void Init_protobuf_c() {
DescriptorPool_register(protobuf);
Descriptor_register(protobuf);
FieldDescriptor_register(protobuf);
OneofDescriptor_register(protobuf);
EnumDescriptor_register(protobuf);
MessageBuilderContext_register(internal);
OneofBuilderContext_register(internal);
EnumBuilderContext_register(internal);
Builder_register(internal);
RepeatedField_register(protobuf);
Map_register(protobuf);
rb_define_singleton_method(protobuf, "encode", Google_Protobuf_encode, 1);
rb_define_singleton_method(protobuf, "decode", Google_Protobuf_decode, 2);

@ -35,15 +35,7 @@
#include <ruby/vm.h>
#include <ruby/encoding.h>
#include "upb/def.h"
#include "upb/handlers.h"
#include "upb/pb/decoder.h"
#include "upb/pb/encoder.h"
#include "upb/pb/glue.h"
#include "upb/json/parser.h"
#include "upb/json/printer.h"
#include "upb/shim/shim.h"
#include "upb/symtab.h"
#include "upb.h"
// Forward decls.
struct DescriptorPool;
@ -51,6 +43,7 @@ struct Descriptor;
struct FieldDescriptor;
struct EnumDescriptor;
struct MessageLayout;
struct MessageField;
struct MessageHeader;
struct MessageBuilderContext;
struct EnumBuilderContext;
@ -59,10 +52,13 @@ struct Builder;
typedef struct DescriptorPool DescriptorPool;
typedef struct Descriptor Descriptor;
typedef struct FieldDescriptor FieldDescriptor;
typedef struct OneofDescriptor OneofDescriptor;
typedef struct EnumDescriptor EnumDescriptor;
typedef struct MessageLayout MessageLayout;
typedef struct MessageField MessageField;
typedef struct MessageHeader MessageHeader;
typedef struct MessageBuilderContext MessageBuilderContext;
typedef struct OneofBuilderContext OneofBuilderContext;
typedef struct EnumBuilderContext EnumBuilderContext;
typedef struct Builder Builder;
@ -118,12 +114,20 @@ struct Descriptor {
const upb_pbdecodermethod* fill_method;
const upb_handlers* pb_serialize_handlers;
const upb_handlers* json_serialize_handlers;
// Handlers hold type class references for sub-message fields directly in some
// cases. We need to keep these rooted because they might otherwise be
// collected.
VALUE typeclass_references;
};
struct FieldDescriptor {
const upb_fielddef* fielddef;
};
struct OneofDescriptor {
const upb_oneofdef* oneofdef;
};
struct EnumDescriptor {
const upb_enumdef* enumdef;
VALUE module; // begins as nil
@ -131,6 +135,12 @@ struct EnumDescriptor {
struct MessageBuilderContext {
VALUE descriptor;
VALUE builder;
};
struct OneofBuilderContext {
VALUE descriptor;
VALUE builder;
};
struct EnumBuilderContext {
@ -147,6 +157,7 @@ extern VALUE cDescriptor;
extern VALUE cFieldDescriptor;
extern VALUE cEnumDescriptor;
extern VALUE cMessageBuilderContext;
extern VALUE cOneofBuilderContext;
extern VALUE cEnumBuilderContext;
extern VALUE cBuilder;
@ -178,6 +189,9 @@ VALUE Descriptor_name_set(VALUE _self, VALUE str);
VALUE Descriptor_each(VALUE _self);
VALUE Descriptor_lookup(VALUE _self, VALUE name);
VALUE Descriptor_add_field(VALUE _self, VALUE obj);
VALUE Descriptor_add_oneof(VALUE _self, VALUE obj);
VALUE Descriptor_each_oneof(VALUE _self);
VALUE Descriptor_lookup_oneof(VALUE _self, VALUE name);
VALUE Descriptor_msgclass(VALUE _self);
extern const rb_data_type_t _Descriptor_type;
@ -202,6 +216,16 @@ VALUE FieldDescriptor_set(VALUE _self, VALUE msg_rb, VALUE value);
upb_fieldtype_t ruby_to_fieldtype(VALUE type);
VALUE fieldtype_to_ruby(upb_fieldtype_t type);
void OneofDescriptor_mark(void* _self);
void OneofDescriptor_free(void* _self);
VALUE OneofDescriptor_alloc(VALUE klass);
void OneofDescriptor_register(VALUE module);
OneofDescriptor* ruby_to_OneofDescriptor(VALUE value);
VALUE OneofDescriptor_name(VALUE _self);
VALUE OneofDescriptor_name_set(VALUE _self, VALUE value);
VALUE OneofDescriptor_add_field(VALUE _self, VALUE field);
VALUE OneofDescriptor_each(VALUE _self, VALUE field);
void EnumDescriptor_mark(void* _self);
void EnumDescriptor_free(void* _self);
VALUE EnumDescriptor_alloc(VALUE klass);
@ -221,10 +245,24 @@ void MessageBuilderContext_free(void* _self);
VALUE MessageBuilderContext_alloc(VALUE klass);
void MessageBuilderContext_register(VALUE module);
MessageBuilderContext* ruby_to_MessageBuilderContext(VALUE value);
VALUE MessageBuilderContext_initialize(VALUE _self, VALUE descriptor);
VALUE MessageBuilderContext_initialize(VALUE _self,
VALUE descriptor,
VALUE builder);
VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self);
VALUE MessageBuilderContext_required(int argc, VALUE* argv, VALUE _self);
VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self);
VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self);
VALUE MessageBuilderContext_oneof(VALUE _self, VALUE name);
void OneofBuilderContext_mark(void* _self);
void OneofBuilderContext_free(void* _self);
VALUE OneofBuilderContext_alloc(VALUE klass);
void OneofBuilderContext_register(VALUE module);
OneofBuilderContext* ruby_to_OneofBuilderContext(VALUE value);
VALUE OneofBuilderContext_initialize(VALUE _self,
VALUE descriptor,
VALUE builder);
VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self);
void EnumBuilderContext_mark(void* _self);
void EnumBuilderContext_free(void* _self);
@ -247,14 +285,25 @@ VALUE Builder_finalize_to_pool(VALUE _self, VALUE pool_rb);
// Native slot storage abstraction.
// -----------------------------------------------------------------------------
#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
size_t native_slot_size(upb_fieldtype_t type);
void native_slot_set(upb_fieldtype_t type,
VALUE type_class,
void* memory,
VALUE value);
// Atomically (with respect to Ruby VM calls) either update the value and set a
// oneof case, or do neither. If |case_memory| is null, then no case value is
// set.
void native_slot_set_value_and_case(upb_fieldtype_t type,
VALUE type_class,
void* memory,
VALUE value,
uint32_t* case_memory,
uint32_t case_number);
VALUE native_slot_get(upb_fieldtype_t type,
VALUE type_class,
void* memory);
const void* memory);
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);
@ -262,11 +311,32 @@ 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_validate_string_encoding(upb_fieldtype_t type, VALUE value);
void native_slot_check_int_range_precision(upb_fieldtype_t type, VALUE value);
extern rb_encoding* kRubyStringUtf8Encoding;
extern rb_encoding* kRubyStringASCIIEncoding;
extern rb_encoding* kRubyString8bitEncoding;
VALUE field_type_class(const upb_fielddef* field);
#define MAP_KEY_FIELD 1
#define MAP_VALUE_FIELD 2
// Oneof case slot value to indicate that no oneof case is set. The value `0` is
// safe because field numbers are used as case identifiers, and no field can
// have a number of 0.
#define ONEOF_CASE_NONE 0
// These operate on a map field (i.e., a repeated field of submessages whose
// submessage type is a map-entry msgdef).
bool is_map_field(const upb_fielddef* field);
const upb_fielddef* map_field_key(const upb_fielddef* field);
const upb_fielddef* map_field_value(const upb_fielddef* field);
// These operate on a map-entry msgdef.
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef);
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef);
// -----------------------------------------------------------------------------
// Repeated field container type.
// -----------------------------------------------------------------------------
@ -290,7 +360,6 @@ extern VALUE cRepeatedField;
RepeatedField* ruby_to_RepeatedField(VALUE value);
void RepeatedField_register(VALUE module);
VALUE RepeatedField_each(VALUE _self);
VALUE RepeatedField_index(VALUE _self, VALUE _index);
void* RepeatedField_index_native(VALUE _self, int index);
@ -310,20 +379,80 @@ VALUE RepeatedField_hash(VALUE _self);
VALUE RepeatedField_inspect(VALUE _self);
VALUE RepeatedField_plus(VALUE _self, VALUE list);
// Defined in repeated_field.c; also used by Map.
void validate_type_class(upb_fieldtype_t type, VALUE klass);
// -----------------------------------------------------------------------------
// Map container type.
// -----------------------------------------------------------------------------
typedef struct {
upb_fieldtype_t key_type;
upb_fieldtype_t value_type;
VALUE value_type_class;
upb_strtable table;
} Map;
void Map_mark(void* self);
void Map_free(void* self);
VALUE Map_alloc(VALUE klass);
VALUE Map_init(int argc, VALUE* argv, VALUE self);
void Map_register(VALUE module);
extern const rb_data_type_t Map_type;
extern VALUE cMap;
Map* ruby_to_Map(VALUE value);
VALUE Map_each(VALUE _self);
VALUE Map_keys(VALUE _self);
VALUE Map_values(VALUE _self);
VALUE Map_index(VALUE _self, VALUE key);
VALUE Map_index_set(VALUE _self, VALUE key, VALUE value);
VALUE Map_has_key(VALUE _self, VALUE key);
VALUE Map_delete(VALUE _self, VALUE key);
VALUE Map_clear(VALUE _self);
VALUE Map_length(VALUE _self);
VALUE Map_dup(VALUE _self);
VALUE Map_deep_copy(VALUE _self);
VALUE Map_eq(VALUE _self, VALUE _other);
VALUE Map_hash(VALUE _self);
VALUE Map_inspect(VALUE _self);
VALUE Map_merge(VALUE _self, VALUE hashmap);
VALUE Map_merge_into_self(VALUE _self, VALUE hashmap);
typedef struct {
Map* self;
upb_strtable_iter it;
} Map_iter;
void Map_begin(VALUE _self, Map_iter* iter);
void Map_next(Map_iter* iter);
bool Map_done(Map_iter* iter);
VALUE Map_iter_key(Map_iter* iter);
VALUE Map_iter_value(Map_iter* iter);
// -----------------------------------------------------------------------------
// Message layout / storage.
// -----------------------------------------------------------------------------
#define MESSAGE_FIELD_NO_CASE ((size_t)-1)
struct MessageField {
size_t offset;
size_t case_offset; // for oneofs, a uint32. Else, MESSAGE_FIELD_NO_CASE.
};
struct MessageLayout {
const upb_msgdef* msgdef;
size_t* offsets;
MessageField* fields;
size_t size;
};
MessageLayout* create_layout(const upb_msgdef* msgdef);
void free_layout(MessageLayout* layout);
VALUE layout_get(MessageLayout* layout,
void* storage,
const void* storage,
const upb_fielddef* field);
void layout_set(MessageLayout* layout,
void* storage,

@ -316,6 +316,29 @@ VALUE RepeatedField_deep_copy(VALUE _self) {
return new_rptfield;
}
/*
* call-seq:
* RepeatedField.to_ary => array
*
* Used when converted implicitly into array, e.g. compared to an Array.
* Also called as a fallback of Object#to_a
*/
VALUE RepeatedField_to_ary(VALUE _self) {
RepeatedField* self = ruby_to_RepeatedField(_self);
upb_fieldtype_t field_type = self->field_type;
size_t elem_size = native_slot_size(field_type);
size_t off = 0;
VALUE ary = rb_ary_new2(self->size);
for (int i = 0; i < self->size; i++, off += elem_size) {
void* mem = ((uint8_t *)self->elements) + off;
VALUE elem = native_slot_get(field_type, self->field_type_class, mem);
rb_ary_push(ary, elem);
}
return ary;
}
/*
* call-seq:
* RepeatedField.==(other) => boolean
@ -324,6 +347,10 @@ VALUE RepeatedField_deep_copy(VALUE _self) {
* element types are equal, their lengths are equal, and each element is equal.
* Elements are compared as per normal Ruby semantics, by calling their :==
* methods (or performing a more efficient comparison for primitive types).
*
* Repeated fields with dissimilar element types are never equal, even if value
* comparison (for example, between integers and floats) would have otherwise
* indicated that every element has equal value.
*/
VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
if (_self == _other) {
@ -331,15 +358,9 @@ VALUE RepeatedField_eq(VALUE _self, VALUE _other) {
}
RepeatedField* self = ruby_to_RepeatedField(_self);
// Inefficient but workable: to support comparison to a generic array, we
// build a temporary RepeatedField of our type.
if (TYPE(_other) == T_ARRAY) {
VALUE new_rptfield = RepeatedField_new_this_type(_self);
for (int i = 0; i < RARRAY_LEN(_other); i++) {
VALUE elem = rb_ary_entry(_other, i);
RepeatedField_push(new_rptfield, elem);
}
_other = new_rptfield;
VALUE self_ary = RepeatedField_to_ary(_self);
return rb_equal(self_ary, _other);
}
RepeatedField* other = ruby_to_RepeatedField(_other);
@ -397,29 +418,8 @@ VALUE RepeatedField_hash(VALUE _self) {
* representation computed by its own #inspect method.
*/
VALUE RepeatedField_inspect(VALUE _self) {
RepeatedField* self = ruby_to_RepeatedField(_self);
VALUE str = rb_str_new2("[");
bool first = true;
upb_fieldtype_t field_type = self->field_type;
VALUE field_type_class = self->field_type_class;
size_t elem_size = native_slot_size(field_type);
size_t off = 0;
for (int i = 0; i < self->size; i++, off += elem_size) {
void* mem = ((uint8_t *)self->elements) + off;
VALUE elem = native_slot_get(field_type, field_type_class, mem);
if (!first) {
str = rb_str_cat2(str, ", ");
} else {
first = false;
}
str = rb_str_append(str, rb_funcall(elem, rb_intern("inspect"), 0));
}
str = rb_str_cat2(str, "]");
return str;
VALUE self_ary = RepeatedField_to_ary(_self);
return rb_funcall(self_ary, rb_intern("inspect"), 0);
}
/*
@ -458,7 +458,7 @@ VALUE RepeatedField_plus(VALUE _self, VALUE list) {
return dupped;
}
static void validate_type_class(upb_fieldtype_t type, VALUE klass) {
void validate_type_class(upb_fieldtype_t type, VALUE klass) {
if (rb_iv_get(klass, kDescriptorInstanceVar) == Qnil) {
rb_raise(rb_eArgError,
"Type class has no descriptor. Please pass a "
@ -590,6 +590,7 @@ void RepeatedField_register(VALUE module) {
// Also define #clone so that we don't inherit Object#clone.
rb_define_method(klass, "clone", RepeatedField_dup, 0);
rb_define_method(klass, "==", RepeatedField_eq, 1);
rb_define_method(klass, "to_ary", RepeatedField_to_ary, 0);
rb_define_method(klass, "hash", RepeatedField_hash, 0);
rb_define_method(klass, "inspect", RepeatedField_inspect, 0);
rb_define_method(klass, "+", RepeatedField_plus, 1);

@ -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));
@ -105,6 +109,17 @@ void native_slot_validate_string_encoding(upb_fieldtype_t type, VALUE value) {
void native_slot_set(upb_fieldtype_t type, VALUE type_class,
void* memory, VALUE value) {
native_slot_set_value_and_case(type, type_class, memory, value, NULL, 0);
}
void native_slot_set_value_and_case(upb_fieldtype_t type, VALUE type_class,
void* memory, VALUE value,
uint32_t* case_memory,
uint32_t case_number) {
// Note that in order to atomically change the value in memory and the case
// value (w.r.t. Ruby VM calls), we must set the value at |memory| only after
// all Ruby VM calls are complete. The case is then set at the bottom of this
// function.
switch (type) {
case UPB_TYPE_FLOAT:
if (!is_ruby_num(value)) {
@ -156,14 +171,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 +188,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);
@ -197,9 +209,15 @@ void native_slot_set(upb_fieldtype_t type, VALUE type_class,
default:
break;
}
if (case_memory != NULL) {
*case_memory = case_number;
}
}
VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, void* memory) {
VALUE native_slot_get(upb_fieldtype_t type,
VALUE type_class,
const void* memory) {
switch (type) {
case UPB_TYPE_FLOAT:
return DBL2NUM(DEREF(memory, float));
@ -210,7 +228,7 @@ VALUE native_slot_get(upb_fieldtype_t type, VALUE type_class, void* memory) {
case UPB_TYPE_STRING:
case UPB_TYPE_BYTES:
case UPB_TYPE_MESSAGE:
return *((VALUE *)memory);
return DEREF(memory, VALUE);
case UPB_TYPE_ENUM: {
int32_t val = DEREF(memory, int32_t);
VALUE symbol = enum_lookup(type_class, INT2NUM(val));
@ -246,8 +264,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;
@ -321,25 +340,135 @@ 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 = upb_fielddef_msgsubdef(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 = upb_fielddef_msgsubdef(field);
return map_entry_key(subdef);
}
const upb_fielddef* map_field_value(const upb_fielddef* field) {
assert(is_map_field(field));
const upb_msgdef* subdef = upb_fielddef_msgsubdef(field);
return map_entry_value(subdef);
}
const upb_fielddef* map_entry_key(const upb_msgdef* msgdef) {
const upb_fielddef* key_field = upb_msgdef_itof(msgdef, MAP_KEY_FIELD);
assert(key_field != NULL);
return key_field;
}
const upb_fielddef* map_entry_value(const upb_msgdef* msgdef) {
const upb_fielddef* value_field = upb_msgdef_itof(msgdef, MAP_VALUE_FIELD);
assert(value_field != NULL);
return value_field;
}
// -----------------------------------------------------------------------------
// Memory layout management.
// -----------------------------------------------------------------------------
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);
}
MessageLayout* create_layout(const upb_msgdef* msgdef) {
MessageLayout* layout = ALLOC(MessageLayout);
int nfields = upb_msgdef_numfields(msgdef);
layout->offsets = ALLOC_N(size_t, nfields);
layout->fields = ALLOC_N(MessageField, nfields);
upb_msg_iter it;
upb_msg_field_iter it;
size_t off = 0;
for (upb_msg_begin(&it, msgdef); !upb_msg_done(&it); upb_msg_next(&it)) {
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);
size_t field_size =
(upb_fielddef_label(field) == UPB_LABEL_REPEATED) ?
sizeof(VALUE) : native_slot_size(upb_fielddef_type(field));
// align current offset
if (upb_fielddef_containingoneof(field)) {
// Oneofs are handled separately below.
continue;
}
// Allocate |field_size| bytes for this field in the layout.
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 up to |size| granularity.
off = align_up_to(off, field_size);
layout->fields[upb_fielddef_index(field)].offset = off;
layout->fields[upb_fielddef_index(field)].case_offset = MESSAGE_FIELD_NO_CASE;
off += field_size;
}
// Handle oneofs now -- we iterate over oneofs specifically and allocate only
// one slot per oneof.
//
// We assign all value slots first, then pack the 'case' fields at the end,
// since in the common case (modern 64-bit platform) these are 8 bytes and 4
// bytes respectively and we want to avoid alignment overhead.
//
// Note that we reserve 4 bytes (a uint32) per 'case' slot because the value
// space for oneof cases is conceptually as wide as field tag numbers. In
// practice, it's unlikely that a oneof would have more than e.g. 256 or 64K
// members (8 or 16 bits respectively), so conceivably we could assign
// consecutive case numbers and then pick a smaller oneof case slot size, but
// the complexity to implement this indirection is probably not worthwhile.
upb_msg_oneof_iter oit;
for (upb_msg_oneof_begin(&oit, msgdef);
!upb_msg_oneof_done(&oit);
upb_msg_oneof_next(&oit)) {
const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit);
// Always allocate NATIVE_SLOT_MAX_SIZE bytes, but share the slot between
// all fields.
size_t field_size = NATIVE_SLOT_MAX_SIZE;
// Align the offset.
off = align_up_to(off, field_size);
// Assign all fields in the oneof this same offset.
upb_oneof_iter fit;
for (upb_oneof_begin(&fit, oneof);
!upb_oneof_done(&fit);
upb_oneof_next(&fit)) {
const upb_fielddef* field = upb_oneof_iter_field(&fit);
layout->fields[upb_fielddef_index(field)].offset = off;
}
off += field_size;
}
// Now the case fields.
for (upb_msg_oneof_begin(&oit, msgdef);
!upb_msg_oneof_done(&oit);
upb_msg_oneof_next(&oit)) {
const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit);
size_t field_size = sizeof(uint32_t);
// Align the offset.
off = (off + field_size - 1) & ~(field_size - 1);
layout->offsets[upb_fielddef_index(field)] = off;
// Assign all fields in the oneof this same offset.
upb_oneof_iter fit;
for (upb_oneof_begin(&fit, oneof);
!upb_oneof_done(&fit);
upb_oneof_next(&fit)) {
const upb_fielddef* field = upb_oneof_iter_field(&fit);
layout->fields[upb_fielddef_index(field)].case_offset = off;
}
off += field_size;
}
@ -352,12 +481,12 @@ MessageLayout* create_layout(const upb_msgdef* msgdef) {
}
void free_layout(MessageLayout* layout) {
xfree(layout->offsets);
xfree(layout->fields);
upb_msgdef_unref(layout->msgdef, &layout->msgdef);
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 =
@ -371,16 +500,39 @@ static VALUE get_type_class(const upb_fielddef* field) {
return type_class;
}
static void* slot_memory(MessageLayout* layout,
const void* storage,
const upb_fielddef* field) {
return ((uint8_t *)storage) +
layout->fields[upb_fielddef_index(field)].offset;
}
static uint32_t* slot_oneof_case(MessageLayout* layout,
const void* storage,
const upb_fielddef* field) {
return (uint32_t *)(((uint8_t *)storage) +
layout->fields[upb_fielddef_index(field)].case_offset);
}
VALUE layout_get(MessageLayout* layout,
void* storage,
const void* storage,
const upb_fielddef* field) {
void* memory = ((uint8_t *)storage) +
layout->offsets[upb_fielddef_index(field)];
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case != upb_fielddef_number(field)) {
return Qnil;
}
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 {
return native_slot_get(upb_fielddef_type(field),
get_type_class(field),
field_type_class(field),
memory);
}
}
@ -398,9 +550,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,34 +560,117 @@ 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) {
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_containingoneof(field)) {
if (val == Qnil) {
// Assigning nil to a oneof field clears the oneof completely.
*oneof_case = ONEOF_CASE_NONE;
memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
} else {
// The transition between field types for a single oneof (union) slot is
// somewhat complex because we need to ensure that a GC triggered at any
// point by a call into the Ruby VM sees a valid state for this field and
// does not either go off into the weeds (following what it thinks is a
// VALUE but is actually a different field type) or miss an object (seeing
// what it thinks is a primitive field but is actually a VALUE for the new
// field type).
//
// In order for the transition to be safe, the oneof case slot must be in
// sync with the value slot whenever the Ruby VM has been called. Thus, we
// use native_slot_set_value_and_case(), which ensures that both the value
// and case number are altered atomically (w.r.t. the Ruby VM).
native_slot_set_value_and_case(
upb_fielddef_type(field), field_type_class(field),
memory, val,
oneof_case, upb_fielddef_number(field));
}
} else 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);
}
}
void layout_init(MessageLayout* layout,
void* storage) {
upb_msg_iter it;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
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 = ((uint8_t *)storage) +
layout->offsets[upb_fielddef_index(field)];
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
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 = 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 +681,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);
}
@ -455,16 +690,20 @@ void layout_init(MessageLayout* layout,
}
void layout_mark(MessageLayout* layout, void* storage) {
upb_msg_iter it;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
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 = ((uint8_t *)storage) +
layout->offsets[upb_fielddef_index(field)];
void* memory = slot_memory(layout, storage, field);
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
rb_gc_mark(*((VALUE *)memory));
if (upb_fielddef_containingoneof(field)) {
if (*oneof_case == upb_fielddef_number(field)) {
native_slot_mark(upb_fielddef_type(field), memory);
}
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
rb_gc_mark(DEREF(memory, VALUE));
} else {
native_slot_mark(upb_fielddef_type(field), memory);
}
@ -472,18 +711,26 @@ void layout_mark(MessageLayout* layout, void* storage) {
}
void layout_dup(MessageLayout* layout, void* to, void* from) {
upb_msg_iter it;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
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* to_memory = ((uint8_t *)to) +
layout->offsets[upb_fielddef_index(field)];
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));
void* to_memory = slot_memory(layout, to, field);
uint32_t* to_oneof_case = slot_oneof_case(layout, to, field);
void* from_memory = slot_memory(layout, from, field);
uint32_t* from_oneof_case = slot_oneof_case(layout, from, field);
if (upb_fielddef_containingoneof(field)) {
if (*from_oneof_case == upb_fielddef_number(field)) {
*to_oneof_case = *from_oneof_case;
native_slot_dup(upb_fielddef_type(field), to_memory, from_memory);
}
} else 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);
}
@ -491,18 +738,28 @@ void layout_dup(MessageLayout* layout, void* to, void* from) {
}
void layout_deep_copy(MessageLayout* layout, void* to, void* from) {
upb_msg_iter it;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
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* to_memory = ((uint8_t *)to) +
layout->offsets[upb_fielddef_index(field)];
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));
void* to_memory = slot_memory(layout, to, field);
uint32_t* to_oneof_case = slot_oneof_case(layout, to, field);
void* from_memory = slot_memory(layout, from, field);
uint32_t* from_oneof_case = slot_oneof_case(layout, from, field);
if (upb_fielddef_containingoneof(field)) {
if (*from_oneof_case == upb_fielddef_number(field)) {
*to_oneof_case = *from_oneof_case;
native_slot_deep_copy(upb_fielddef_type(field), to_memory, from_memory);
}
} else 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);
}
@ -510,19 +767,33 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) {
}
VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) {
upb_msg_iter it;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&it)) {
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* msg1_memory = ((uint8_t *)msg1) +
layout->offsets[upb_fielddef_index(field)];
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) {
void* msg1_memory = slot_memory(layout, msg1, field);
uint32_t* msg1_oneof_case = slot_oneof_case(layout, msg1, field);
void* msg2_memory = slot_memory(layout, msg2, field);
uint32_t* msg2_oneof_case = slot_oneof_case(layout, msg2, field);
if (upb_fielddef_containingoneof(field)) {
if (*msg1_oneof_case != *msg2_oneof_case ||
(*msg1_oneof_case == upb_fielddef_number(field) &&
!native_slot_eq(upb_fielddef_type(field),
msg1_memory,
msg2_memory))) {
return Qfalse;
}
} else if (is_map_field(field)) {
if (!Map_eq(DEREF(msg1_memory, VALUE),
DEREF(msg2_memory, VALUE))) {
return Qfalse;
}
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
if (!RepeatedField_eq(DEREF(msg1_memory, VALUE),
DEREF(msg2_memory, VALUE))) {
return Qfalse;
}
} else {
@ -536,12 +807,12 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) {
}
VALUE layout_hash(MessageLayout* layout, void* storage) {
upb_msg_iter it;
upb_msg_field_iter it;
st_index_t h = rb_hash_start(0);
VALUE hash_sym = rb_intern("hash");
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&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);
VALUE field_val = layout_get(layout, storage, field);
h = rb_hash_uint(h, NUM2LONG(rb_funcall(field_val, hash_sym, 0)));
@ -554,11 +825,11 @@ VALUE layout_hash(MessageLayout* layout, void* storage) {
VALUE layout_inspect(MessageLayout* layout, void* storage) {
VALUE str = rb_str_new2("");
upb_msg_iter it;
upb_msg_field_iter it;
bool first = true;
for (upb_msg_begin(&it, layout->msgdef);
!upb_msg_done(&it);
upb_msg_next(&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);
VALUE field_val = layout_get(layout, storage, field);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,23 +0,0 @@
#!/usr/bin/ruby
require 'mkmf'
upb_path = File.absolute_path(File.dirname($0)) + "/../../../upb"
libs = ["upb_pic", "upb.pb_pic", "upb.json_pic"]
system("cd #{upb_path}; make " + libs.map{|l| "lib/lib#{l}.a"}.join(" "))
$CFLAGS += " -O3 -std=c99 -Wno-unused-function -DNDEBUG"
find_header("upb/upb.h", upb_path) or
raise "Can't find upb headers"
find_library("upb_pic", "upb_msgdef_new", upb_path + "/lib") or
raise "Can't find upb lib"
find_library("upb.pb_pic", "upb_pbdecoder_init", upb_path + "/lib") or
raise "Can't find upb.pb lib"
find_library("upb.json_pic", "upb_json_printer_init", upb_path + "/lib") or
raise "Can't find upb.pb lib"
$objs = ["protobuf.o", "defs.o", "storage.o", "message.o",
"repeated_field.o", "encode_decode.o"]
create_makefile("protobuf_c")

@ -0,0 +1,24 @@
class << Gem::Specification
def find_c_source(dir)
`cd #{dir}; git ls-files "*.c" "*.h" extconf.rb Makefile`.split
.map{|f| "#{dir}/#{f}"}
end
end
Gem::Specification.new do |s|
s.name = "google-protobuf"
s.version = "3.0.0.alpha.2"
s.licenses = ["BSD"]
s.summary = "Protocol Buffers"
s.description = "Protocol Buffers are Google's data interchange format."
s.authors = ["Protobuf Authors"]
s.email = "protobuf@googlegroups.com"
s.require_paths = ["lib"]
s.extensions = ["ext/google/protobuf_c/extconf.rb"]
s.files = ["lib/google/protobuf.rb"] +
# extension C source
find_c_source("ext/google/protobuf_c")
s.test_files = ["tests/basic.rb",
"tests/stress.rb",
"tests/generated_code_test.rb"]
end

@ -28,4 +28,4 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'protobuf_c'
require 'google/protobuf_c'

@ -1,6 +1,6 @@
#!/usr/bin/ruby
require 'protobuf'
require 'google/protobuf'
require 'test/unit'
# ------------- generated code --------------
@ -36,23 +36,52 @@ module BasicTest
add_message "TestMessage2" do
optional :foo, :int32, 1
end
add_message "Recursive1" do
optional :foo, :message, 1, "Recursive2"
end
add_message "Recursive2" do
optional :foo, :message, 1, "Recursive1"
end
add_enum "TestEnum" do
value :Default, 0
value :A, 1
value :B, 2
value :C, 3
end
add_message "BadFieldNames" do
optional :dup, :int32, 1
optional :class, :int32, 2
optional :"a.b", :int32, 3
end
add_message "MapMessage" do
map :map_string_int32, :string, :int32, 1
map :map_string_msg, :string, :message, 2, "TestMessage2"
end
add_message "MapMessageWireEquiv" do
repeated :map_string_int32, :message, 1, "MapMessageWireEquiv_entry1"
repeated :map_string_msg, :message, 2, "MapMessageWireEquiv_entry2"
end
add_message "MapMessageWireEquiv_entry1" do
optional :key, :string, 1
optional :value, :int32, 2
end
add_message "MapMessageWireEquiv_entry2" do
optional :key, :string, 1
optional :value, :message, 2, "TestMessage2"
end
add_message "OneofMessage" do
oneof :my_oneof do
optional :a, :string, 1
optional :b, :int32, 2
optional :c, :message, 3, "TestMessage2"
optional :d, :enum, 4, "TestEnum"
end
end
end
TestMessage = pool.lookup("TestMessage").msgclass
@ -61,6 +90,13 @@ module BasicTest
Recursive2 = pool.lookup("Recursive2").msgclass
TestEnum = pool.lookup("TestEnum").enummodule
BadFieldNames = pool.lookup("BadFieldNames").msgclass
MapMessage = pool.lookup("MapMessage").msgclass
MapMessageWireEquiv = pool.lookup("MapMessageWireEquiv").msgclass
MapMessageWireEquiv_entry1 =
pool.lookup("MapMessageWireEquiv_entry1").msgclass
MapMessageWireEquiv_entry2 =
pool.lookup("MapMessageWireEquiv_entry2").msgclass
OneofMessage = pool.lookup("OneofMessage").msgclass
# ------------ test cases ---------------
@ -200,7 +236,8 @@ module BasicTest
assert l.count == 0
l = Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])
assert l.count == 3
assert l == [1, 2, 3]
assert_equal [1, 2, 3], l
assert_equal l, [1, 2, 3]
l.push 4
assert l == [1, 2, 3, 4]
dst_list = []
@ -300,7 +337,7 @@ module BasicTest
l.push :B
l.push :C
assert l.count == 3
assert_raise NameError do
assert_raise RangeError do
l.push :D
end
assert l[0] == :A
@ -324,12 +361,329 @@ module BasicTest
end
end
def test_map_basic
# allowed key types:
# :int32, :int64, :uint32, :uint64, :bool, :string, :bytes.
m = Google::Protobuf::Map.new(:string, :int32)
m["asdf"] = 1
assert m["asdf"] == 1
m["jkl;"] = 42
assert m == { "jkl;" => 42, "asdf" => 1 }
assert m.has_key?("asdf")
assert !m.has_key?("qwerty")
assert m.length == 2
m2 = m.dup
assert m == m2
assert m.hash != 0
assert m.hash == m2.hash
collected = {}
m.each { |k,v| collected[v] = k }
assert collected == { 42 => "jkl;", 1 => "asdf" }
assert m.delete("asdf") == 1
assert !m.has_key?("asdf")
assert m["asdf"] == nil
assert !m.has_key?("asdf")
# We only assert on inspect value when there is one map entry because the
# order in which elements appear is unspecified (depends on the internal
# hash function). We don't want a brittle test.
assert m.inspect == "{\"jkl;\"=>42}"
assert m.keys == ["jkl;"]
assert m.values == [42]
m.clear
assert m.length == 0
assert m == {}
assert_raise TypeError do
m[1] = 1
end
assert_raise RangeError do
m["asdf"] = 0x1_0000_0000
end
end
def test_map_ctor
m = Google::Protobuf::Map.new(:string, :int32,
{"a" => 1, "b" => 2, "c" => 3})
assert m == {"a" => 1, "c" => 3, "b" => 2}
end
def test_map_keytypes
m = Google::Protobuf::Map.new(:int32, :int32)
m[1] = 42
m[-1] = 42
assert_raise RangeError do
m[0x8000_0000] = 1
end
assert_raise TypeError do
m["asdf"] = 1
end
m = Google::Protobuf::Map.new(:int64, :int32)
m[0x1000_0000_0000_0000] = 1
assert_raise RangeError do
m[0x1_0000_0000_0000_0000] = 1
end
assert_raise TypeError do
m["asdf"] = 1
end
m = Google::Protobuf::Map.new(:uint32, :int32)
m[0x8000_0000] = 1
assert_raise RangeError do
m[0x1_0000_0000] = 1
end
assert_raise RangeError do
m[-1] = 1
end
m = Google::Protobuf::Map.new(:uint64, :int32)
m[0x8000_0000_0000_0000] = 1
assert_raise RangeError do
m[0x1_0000_0000_0000_0000] = 1
end
assert_raise RangeError do
m[-1] = 1
end
m = Google::Protobuf::Map.new(:bool, :int32)
m[true] = 1
m[false] = 2
assert_raise TypeError do
m[1] = 1
end
assert_raise TypeError do
m["asdf"] = 1
end
m = Google::Protobuf::Map.new(:string, :int32)
m["asdf"] = 1
assert_raise TypeError do
m[1] = 1
end
assert_raise TypeError do
bytestring = ["FFFF"].pack("H*")
m[bytestring] = 1
end
m = Google::Protobuf::Map.new(:bytes, :int32)
bytestring = ["FFFF"].pack("H*")
m[bytestring] = 1
assert_raise TypeError do
m["asdf"] = 1
end
assert_raise TypeError do
m[1] = 1
end
end
def test_map_msg_enum_valuetypes
m = Google::Protobuf::Map.new(:string, :message, TestMessage)
m["asdf"] = TestMessage.new
assert_raise TypeError do
m["jkl;"] = TestMessage2.new
end
m = Google::Protobuf::Map.new(
:string, :message, TestMessage,
{ "a" => TestMessage.new(:optional_int32 => 42),
"b" => TestMessage.new(:optional_int32 => 84) })
assert m.length == 2
assert m.values.map{|msg| msg.optional_int32}.sort == [42, 84]
m = Google::Protobuf::Map.new(:string, :enum, TestEnum,
{ "x" => :A, "y" => :B, "z" => :C })
assert m.length == 3
assert m["z"] == :C
m["z"] = 2
assert m["z"] == :B
m["z"] = 4
assert m["z"] == 4
assert_raise RangeError do
m["z"] = :Z
end
assert_raise TypeError do
m["z"] = "z"
end
end
def test_map_dup_deep_copy
m = Google::Protobuf::Map.new(
:string, :message, TestMessage,
{ "a" => TestMessage.new(:optional_int32 => 42),
"b" => TestMessage.new(:optional_int32 => 84) })
m2 = m.dup
assert m == m2
assert m.object_id != m2.object_id
assert m["a"].object_id == m2["a"].object_id
assert m["b"].object_id == m2["b"].object_id
m2 = Google::Protobuf.deep_copy(m)
assert m == m2
assert m.object_id != m2.object_id
assert m["a"].object_id != m2["a"].object_id
assert m["b"].object_id != m2["b"].object_id
end
def test_map_field
m = MapMessage.new
assert m.map_string_int32 == {}
assert m.map_string_msg == {}
m = MapMessage.new(
:map_string_int32 => {"a" => 1, "b" => 2},
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
"b" => TestMessage2.new(:foo => 2)})
assert m.map_string_int32.keys.sort == ["a", "b"]
assert m.map_string_int32["a"] == 1
assert m.map_string_msg["b"].foo == 2
m.map_string_int32["c"] = 3
assert m.map_string_int32["c"] == 3
m.map_string_msg["c"] = TestMessage2.new(:foo => 3)
assert m.map_string_msg["c"] == TestMessage2.new(:foo => 3)
m.map_string_msg.delete("b")
m.map_string_msg.delete("c")
assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) }
assert_raise TypeError do
m.map_string_msg["e"] = TestMessage.new # wrong value type
end
# ensure nothing was added by the above
assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) }
m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
assert_raise TypeError do
m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64)
end
assert_raise TypeError do
m.map_string_int32 = {}
end
assert_raise TypeError do
m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" })
end
end
def test_map_encode_decode
m = MapMessage.new(
:map_string_int32 => {"a" => 1, "b" => 2},
:map_string_msg => {"a" => TestMessage2.new(:foo => 1),
"b" => TestMessage2.new(:foo => 2)})
m2 = MapMessage.decode(MapMessage.encode(m))
assert m == m2
m3 = MapMessageWireEquiv.decode(MapMessage.encode(m))
assert m3.map_string_int32.length == 2
kv = {}
m3.map_string_int32.map { |msg| kv[msg.key] = msg.value }
assert kv == {"a" => 1, "b" => 2}
kv = {}
m3.map_string_msg.map { |msg| kv[msg.key] = msg.value }
assert kv == {"a" => TestMessage2.new(:foo => 1),
"b" => TestMessage2.new(:foo => 2)}
end
def test_oneof_descriptors
d = OneofMessage.descriptor
o = d.lookup_oneof("my_oneof")
assert o != nil
assert o.class == Google::Protobuf::OneofDescriptor
assert o.name == "my_oneof"
oneof_count = 0
d.each_oneof{ |oneof|
oneof_count += 1
assert oneof == o
}
assert oneof_count == 1
assert o.count == 4
field_names = o.map{|f| f.name}.sort
assert field_names == ["a", "b", "c", "d"]
end
def test_oneof
d = OneofMessage.new
assert d.a == nil
assert d.b == nil
assert d.c == nil
assert d.d == nil
assert d.my_oneof == nil
d.a = "hi"
assert d.a == "hi"
assert d.b == nil
assert d.c == nil
assert d.d == nil
assert d.my_oneof == :a
d.b = 42
assert d.a == nil
assert d.b == 42
assert d.c == nil
assert d.d == nil
assert d.my_oneof == :b
d.c = TestMessage2.new(:foo => 100)
assert d.a == nil
assert d.b == nil
assert d.c.foo == 100
assert d.d == nil
assert d.my_oneof == :c
d.d = :C
assert d.a == nil
assert d.b == nil
assert d.c == nil
assert d.d == :C
assert d.my_oneof == :d
d2 = OneofMessage.decode(OneofMessage.encode(d))
assert d2 == d
encoded_field_a = OneofMessage.encode(OneofMessage.new(:a => "string"))
encoded_field_b = OneofMessage.encode(OneofMessage.new(:b => 1000))
encoded_field_c = OneofMessage.encode(
OneofMessage.new(:c => TestMessage2.new(:foo => 1)))
encoded_field_d = OneofMessage.encode(OneofMessage.new(:d => :B))
d3 = OneofMessage.decode(
encoded_field_c + encoded_field_a + encoded_field_d)
assert d3.a == nil
assert d3.b == nil
assert d3.c == nil
assert d3.d == :B
d4 = OneofMessage.decode(
encoded_field_c + encoded_field_a + encoded_field_d +
encoded_field_c)
assert d4.a == nil
assert d4.b == nil
assert d4.c.foo == 1
assert d4.d == nil
d5 = OneofMessage.new(:a => "hello")
assert d5.a != nil
d5.a = nil
assert d5.a == nil
assert OneofMessage.encode(d5) == ''
assert d5.my_oneof == nil
end
def test_enum_field
m = TestMessage.new
assert m.optional_enum == :Default
m.optional_enum = :A
assert m.optional_enum == :A
assert_raise NameError do
assert_raise RangeError do
m.optional_enum = :ASDF
end
m.optional_enum = 1
@ -363,6 +717,14 @@ module BasicTest
assert m.repeated_msg[0].object_id != m2.repeated_msg[0].object_id
end
def test_eq
m = TestMessage.new(:optional_int32 => 42,
:repeated_int32 => [1, 2, 3])
m2 = TestMessage.new(:optional_int32 => 43,
:repeated_int32 => [1, 2, 3])
assert m != m2
end
def test_enum_lookup
assert TestEnum::A == 1
assert TestEnum::B == 2
@ -384,7 +746,8 @@ module BasicTest
:repeated_string => ["a", "b", "c"],
:repeated_int32 => [42, 43, 44],
:repeated_enum => [:A, :B, :C, 100],
:repeated_msg => [TestMessage2.new(:foo => 1), TestMessage2.new(:foo => 2)])
:repeated_msg => [TestMessage2.new(:foo => 1),
TestMessage2.new(:foo => 2)])
data = TestMessage.encode m
m2 = TestMessage.decode data
assert m == m2
@ -629,5 +992,13 @@ module BasicTest
m2 = TestMessage.decode_json(json_text)
assert m == m2
end
def test_json_maps
m = MapMessage.new(:map_string_int32 => {"a" => 1})
expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}'
assert MapMessage.encode_json(m) == expected
m2 = MapMessage.decode_json(MapMessage.encode_json(m))
assert m == m2
end
end
end

@ -0,0 +1,67 @@
syntax = "proto3";
package A.B.C;
message TestMessage {
optional int32 optional_int32 = 1;
optional int64 optional_int64 = 2;
optional uint32 optional_uint32 = 3;
optional uint64 optional_uint64 = 4;
optional bool optional_bool = 5;
optional double optional_double = 6;
optional float optional_float = 7;
optional string optional_string = 8;
optional bytes optional_bytes = 9;
optional TestEnum optional_enum = 10;
optional TestMessage optional_msg = 11;
repeated int32 repeated_int32 = 21;
repeated int64 repeated_int64 = 22;
repeated uint32 repeated_uint32 = 23;
repeated uint64 repeated_uint64 = 24;
repeated bool repeated_bool = 25;
repeated double repeated_double = 26;
repeated float repeated_float = 27;
repeated string repeated_string = 28;
repeated bytes repeated_bytes = 29;
repeated TestEnum repeated_enum = 30;
repeated TestMessage repeated_msg = 31;
oneof my_oneof {
int32 oneof_int32 = 41;
int64 oneof_int64 = 42;
uint32 oneof_uint32 = 43;
uint64 oneof_uint64 = 44;
bool oneof_bool = 45;
double oneof_double = 46;
float oneof_float = 47;
string oneof_string = 48;
bytes oneof_bytes = 49;
TestEnum oneof_enum = 50;
TestMessage oneof_msg = 51;
}
map<int32, string> map_int32_string = 61;
map<int64, string> map_int64_string = 62;
map<uint32, string> map_uint32_string = 63;
map<uint64, string> map_uint64_string = 64;
map<bool, string> map_bool_string = 65;
map<string, string> map_string_string = 66;
map<string, TestMessage> map_string_msg = 67;
map<string, TestEnum> map_string_enum = 68;
map<string, int32> map_string_int32 = 69;
map<string, bool> map_string_bool = 70;
message NestedMessage {
optional int32 foo = 1;
}
optional NestedMessage nested_message = 80;
}
enum TestEnum {
Default = 0;
A = 1;
B = 2;
C = 3;
}

@ -0,0 +1,74 @@
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: generated_code.proto
require 'google/protobuf'
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message "A.B.C.TestMessage" do
optional :optional_int32, :int32, 1
optional :optional_int64, :int64, 2
optional :optional_uint32, :uint32, 3
optional :optional_uint64, :uint64, 4
optional :optional_bool, :bool, 5
optional :optional_double, :double, 6
optional :optional_float, :float, 7
optional :optional_string, :string, 8
optional :optional_bytes, :string, 9
optional :optional_enum, :enum, 10, "A.B.C.TestEnum"
optional :optional_msg, :message, 11, "A.B.C.TestMessage"
repeated :repeated_int32, :int32, 21
repeated :repeated_int64, :int64, 22
repeated :repeated_uint32, :uint32, 23
repeated :repeated_uint64, :uint64, 24
repeated :repeated_bool, :bool, 25
repeated :repeated_double, :double, 26
repeated :repeated_float, :float, 27
repeated :repeated_string, :string, 28
repeated :repeated_bytes, :string, 29
repeated :repeated_enum, :enum, 30, "A.B.C.TestEnum"
repeated :repeated_msg, :message, 31, "A.B.C.TestMessage"
map :map_int32_string, :int32, :string, 61
map :map_int64_string, :int64, :string, 62
map :map_uint32_string, :uint32, :string, 63
map :map_uint64_string, :uint64, :string, 64
map :map_bool_string, :bool, :string, 65
map :map_string_string, :string, :string, 66
map :map_string_msg, :string, :message, 67, "A.B.C.TestMessage"
map :map_string_enum, :string, :enum, 68, "A.B.C.TestEnum"
map :map_string_int32, :string, :int32, 69
map :map_string_bool, :string, :bool, 70
optional :nested_message, :message, 80, "A.B.C.TestMessage.NestedMessage"
oneof :my_oneof do
optional :oneof_int32, :int32, 41
optional :oneof_int64, :int64, 42
optional :oneof_uint32, :uint32, 43
optional :oneof_uint64, :uint64, 44
optional :oneof_bool, :bool, 45
optional :oneof_double, :double, 46
optional :oneof_float, :float, 47
optional :oneof_string, :string, 48
optional :oneof_bytes, :string, 49
optional :oneof_enum, :enum, 50, "A.B.C.TestEnum"
optional :oneof_msg, :message, 51, "A.B.C.TestMessage"
end
end
add_message "A.B.C.TestMessage.NestedMessage" do
optional :foo, :int32, 1
end
add_enum "A.B.C.TestEnum" do
value :Default, 0
value :A, 1
value :B, 2
value :C, 3
end
end
module A
module B
module C
TestMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage").msgclass
TestMessage::NestedMessage = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestMessage.NestedMessage").msgclass
TestEnum = Google::Protobuf::DescriptorPool.generated_pool.lookup("A.B.C.TestEnum").enummodule
end
end
end

@ -0,0 +1,17 @@
#!/usr/bin/ruby
# generated_code.rb is in the same directory as this test.
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
require 'generated_code'
require 'test/unit'
class GeneratedCodeTest < Test::Unit::TestCase
def test_generated_msg
# just test that we can instantiate the message. The purpose of this test
# is to ensure that the output of the code generator is valid Ruby and
# successfully creates message definitions and classes, not to test every
# aspect of the extension (basic.rb is for that).
m = A::B::C::TestMessage.new()
end
end

@ -1,6 +1,6 @@
#!/usr/bin/ruby
require 'protobuf'
require 'google/protobuf'
require 'test/unit'
module StressTest

@ -107,7 +107,9 @@ nobase_include_HEADERS = \
google/protobuf/compiler/cpp/cpp_generator.h \
google/protobuf/compiler/java/java_generator.h \
google/protobuf/compiler/java/java_names.h \
google/protobuf/compiler/python/python_generator.h
google/protobuf/compiler/javanano/javanano_generator.h \
google/protobuf/compiler/python/python_generator.h \
google/protobuf/compiler/ruby/ruby_generator.h
lib_LTLIBRARIES = libprotobuf-lite.la libprotobuf.la libprotoc.la
@ -241,26 +243,28 @@ libprotoc_la_SOURCES = \
google/protobuf/compiler/java/java_doc_comment.cc \
google/protobuf/compiler/java/java_doc_comment.h \
google/protobuf/compiler/javanano/javanano_enum.cc \
google/protobuf/compiler/javanano/javanano_enum.h \
google/protobuf/compiler/javanano/javanano_enum_field.cc \
google/protobuf/compiler/javanano/javanano_enum_field.h \
google/protobuf/compiler/javanano/javanano_extension.cc \
google/protobuf/compiler/javanano/javanano_extension.h \
google/protobuf/compiler/javanano/javanano_field.cc \
google/protobuf/compiler/javanano/javanano_field.h \
google/protobuf/compiler/javanano/javanano_file.cc \
google/protobuf/compiler/javanano/javanano_file.h \
google/protobuf/compiler/javanano/javanano_generator.cc \
google/protobuf/compiler/javanano/javanano_generator.h \
google/protobuf/compiler/javanano/javanano_helpers.cc \
google/protobuf/compiler/javanano/javanano_helpers.h \
google/protobuf/compiler/javanano/javanano_map_field.cc \
google/protobuf/compiler/javanano/javanano_map_field.h \
google/protobuf/compiler/javanano/javanano_message.cc \
google/protobuf/compiler/javanano/javanano_message.h \
google/protobuf/compiler/javanano/javanano_message_field.cc \
google/protobuf/compiler/javanano/javanano_message_field.h \
google/protobuf/compiler/javanano/javanano_params.h \
google/protobuf/compiler/javanano/javanano_primitive_field.h \
google/protobuf/compiler/javanano/javanano_enum_field.cc \
google/protobuf/compiler/javanano/javanano_enum.h \
google/protobuf/compiler/javanano/javanano_extension.h \
google/protobuf/compiler/javanano/javanano_field.h \
google/protobuf/compiler/javanano/javanano_file.h \
google/protobuf/compiler/javanano/javanano_generator.h \
google/protobuf/compiler/javanano/javanano_helpers.h \
google/protobuf/compiler/javanano/javanano_message_field.cc \
google/protobuf/compiler/javanano/javanano_message.h \
google/protobuf/compiler/javanano/javanano_primitive_field.cc \
google/protobuf/compiler/javanano/javanano_primitive_field.h \
google/protobuf/compiler/python/python_generator.cc \
google/protobuf/compiler/ruby/ruby_generator.cc
@ -456,6 +460,7 @@ protobuf_test_SOURCES = \
google/protobuf/compiler/java/java_plugin_unittest.cc \
google/protobuf/compiler/java/java_doc_comment_unittest.cc \
google/protobuf/compiler/python/python_plugin_unittest.cc \
google/protobuf/compiler/ruby/ruby_generator_unittest.cc \
$(COMMON_TEST_SOURCES)
nodist_protobuf_test_SOURCES = $(protoc_outputs)

@ -301,7 +301,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which,
printer->Print(vars_,
" default:\n"
" GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n"
" return *reinterpret_cast< ::google::protobuf::Message*>(NULL);\n"
" return *static_cast< ::google::protobuf::Message*>(NULL);\n"
" }\n"
"}\n"
"\n");

@ -0,0 +1,87 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
//
// Provides a mechanism for mapping a descriptor to the
// fully-qualified name of the corresponding Java class.
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_NAMES_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVA_NAMES_H__
#include <string>
namespace google {
namespace protobuf {
class Descriptor;
class EnumDescriptor;
class FileDescriptor;
class ServiceDescriptor;
namespace compiler {
namespace java {
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified Java class name.
string ClassName(const Descriptor* descriptor);
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified Java class name.
string ClassName(const EnumDescriptor* descriptor);
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified Java class name.
string ClassName(const FileDescriptor* descriptor);
// Requires:
// descriptor != NULL
//
// Returns:
// The fully-qualified Java class name.
string ClassName(const ServiceDescriptor* descriptor);
} // namespace java
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_NAMES_H__

@ -36,6 +36,7 @@
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/compiler/javanano/javanano_primitive_field.h>
#include <google/protobuf/compiler/javanano/javanano_enum_field.h>
#include <google/protobuf/compiler/javanano/javanano_map_field.h>
#include <google/protobuf/compiler/javanano/javanano_message_field.h>
#include <google/protobuf/stubs/common.h>
@ -97,7 +98,11 @@ FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field,
if (field->is_repeated()) {
switch (java_type) {
case JAVATYPE_MESSAGE:
return new RepeatedMessageFieldGenerator(field, params);
if (IsMapEntry(field->message_type())) {
return new MapFieldGenerator(field, params);
} else {
return new RepeatedMessageFieldGenerator(field, params);
}
case JAVATYPE_ENUM:
return new RepeatedEnumFieldGenerator(field, params);
default:

@ -560,6 +560,17 @@ void SetBitOperationVariables(const string name,
(*variables)["different_" + name] = GenerateDifferentBit(bitIndex);
}
bool HasMapField(const Descriptor* descriptor) {
for (int i = 0; i < descriptor->field_count(); ++i) {
const FieldDescriptor* field = descriptor->field(i);
if (field->type() == FieldDescriptor::TYPE_MESSAGE &&
IsMapEntry(field->message_type())) {
return true;
}
}
return false;
}
} // namespace javanano
} // namespace compiler
} // namespace protobuf

@ -181,6 +181,14 @@ string GenerateDifferentBit(int bit_index);
void SetBitOperationVariables(const string name,
int bitIndex, map<string, string>* variables);
inline bool IsMapEntry(const Descriptor* descriptor) {
// TODO(liujisi): Add an option to turn on maps for proto2 syntax as well.
return descriptor->options().map_entry() &&
descriptor->file()->syntax() == FileDescriptor::SYNTAX_PROTO3;
}
bool HasMapField(const Descriptor* descriptor);
} // namespace javanano
} // namespace compiler
} // namespace protobuf

@ -0,0 +1,186 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/javanano/javanano_map_field.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace javanano {
namespace {
string TypeName(const Params& params, const FieldDescriptor* field,
bool boxed) {
JavaType java_type = GetJavaType(field);
switch (java_type) {
case JAVATYPE_MESSAGE:
return ClassName(params, field->message_type());
case JAVATYPE_INT:
case JAVATYPE_LONG:
case JAVATYPE_FLOAT:
case JAVATYPE_DOUBLE:
case JAVATYPE_BOOLEAN:
case JAVATYPE_STRING:
case JAVATYPE_BYTES:
case JAVATYPE_ENUM:
if (boxed) {
return BoxedPrimitiveTypeName(java_type);
} else {
return PrimitiveTypeName(java_type);
}
// No default because we want the compiler to complain if any new JavaTypes
// are added..
}
GOOGLE_LOG(FATAL) << "should not reach here.";
return "";
}
const FieldDescriptor* KeyField(const FieldDescriptor* descriptor) {
GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
const Descriptor* message = descriptor->message_type();
GOOGLE_CHECK(message->options().map_entry());
return message->FindFieldByName("key");
}
const FieldDescriptor* ValueField(const FieldDescriptor* descriptor) {
GOOGLE_CHECK_EQ(FieldDescriptor::TYPE_MESSAGE, descriptor->type());
const Descriptor* message = descriptor->message_type();
GOOGLE_CHECK(message->options().map_entry());
return message->FindFieldByName("value");
}
void SetMapVariables(const Params& params,
const FieldDescriptor* descriptor, map<string, string>* variables) {
const FieldDescriptor* key = KeyField(descriptor);
const FieldDescriptor* value = ValueField(descriptor);
(*variables)["name"] =
RenameJavaKeywords(UnderscoresToCamelCase(descriptor));
(*variables)["number"] = SimpleItoa(descriptor->number());
(*variables)["key_type"] = TypeName(params, key, false);
(*variables)["boxed_key_type"] = TypeName(params,key, true);
(*variables)["key_desc_type"] =
"TYPE_" + ToUpper(FieldDescriptor::TypeName(key->type()));
(*variables)["key_tag"] = SimpleItoa(internal::WireFormat::MakeTag(key));
(*variables)["value_type"] = TypeName(params, value, false);
(*variables)["boxed_value_type"] = TypeName(params, value, true);
(*variables)["value_desc_type"] =
"TYPE_" + ToUpper(FieldDescriptor::TypeName(value->type()));
(*variables)["value_tag"] = SimpleItoa(internal::WireFormat::MakeTag(value));
(*variables)["type_parameters"] =
(*variables)["boxed_key_type"] + ", " + (*variables)["boxed_value_type"];
(*variables)["value_default"] =
value->type() == FieldDescriptor::TYPE_MESSAGE
? "new " + (*variables)["value_type"] + "()"
: "null";
}
} // namespace
// ===================================================================
MapFieldGenerator::MapFieldGenerator(const FieldDescriptor* descriptor,
const Params& params)
: FieldGenerator(params), descriptor_(descriptor) {
SetMapVariables(params, descriptor, &variables_);
}
MapFieldGenerator::~MapFieldGenerator() {}
void MapFieldGenerator::
GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const {
printer->Print(variables_,
"public java.util.Map<$type_parameters$> $name$;\n");
}
void MapFieldGenerator::
GenerateClearCode(io::Printer* printer) const {
printer->Print(variables_,
"$name$ = null;\n");
}
void MapFieldGenerator::
GenerateMergingCode(io::Printer* printer) const {
printer->Print(variables_,
"this.$name$ = com.google.protobuf.nano.InternalNano.mergeMapEntry(\n"
" input, this.$name$, mapFactory,\n"
" com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
" com.google.protobuf.nano.InternalNano.$value_desc_type$,\n"
" $value_default$,\n"
" $key_tag$, $value_tag$);\n"
"\n");
}
void MapFieldGenerator::
GenerateSerializationCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null) {\n"
" com.google.protobuf.nano.InternalNano.serializeMapField(\n"
" output, this.$name$, $number$,\n"
" com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
" com.google.protobuf.nano.InternalNano.$value_desc_type$);\n"
"}\n");
}
void MapFieldGenerator::
GenerateSerializedSizeCode(io::Printer* printer) const {
printer->Print(variables_,
"if (this.$name$ != null) {\n"
" size += com.google.protobuf.nano.InternalNano.computeMapFieldSize(\n"
" this.$name$, $number$,\n"
" com.google.protobuf.nano.InternalNano.$key_desc_type$,\n"
" com.google.protobuf.nano.InternalNano.$value_desc_type$);\n"
"}\n");
}
void MapFieldGenerator::
GenerateEqualsCode(io::Printer* printer) const {
printer->Print(variables_,
"if (!com.google.protobuf.nano.InternalNano.equals(\n"
" this.$name$, other.$name$)) {\n"
" return false;\n"
"}\n");
}
void MapFieldGenerator::
GenerateHashCodeCode(io::Printer* printer) const {
printer->Print(variables_,
"result = 31 * result +\n"
" com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n");
}
} // namespace javanano
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,70 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__
#include <map>
#include <string>
#include <vector>
#include <google/protobuf/compiler/javanano/javanano_field.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace javanano {
class MapFieldGenerator : public FieldGenerator {
public:
explicit MapFieldGenerator(
const FieldDescriptor* descriptor, const Params& params);
~MapFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateMembers(io::Printer* printer, bool lazy_init) const;
void GenerateClearCode(io::Printer* printer) const;
void GenerateMergingCode(io::Printer* printer) const;
void GenerateSerializationCode(io::Printer* printer) const;
void GenerateSerializedSizeCode(io::Printer* printer) const;
void GenerateEqualsCode(io::Printer* printer) const;
void GenerateHashCodeCode(io::Printer* printer) const;
private:
const FieldDescriptor* descriptor_;
map<string, string> variables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);
};
} // namespace javanano
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_MAP_FIELD_H__

@ -90,6 +90,7 @@ void MessageGenerator::GenerateStaticVariables(io::Printer* printer) {
// Generate static members for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariables(printer);
}
@ -100,6 +101,7 @@ void MessageGenerator::GenerateStaticVariableInitializers(
// Generate static member initializers for all nested types.
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// TODO(kenton): Reuse MessageGenerator objects?
if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariableInitializers(printer);
}
@ -159,6 +161,7 @@ void MessageGenerator::Generate(io::Printer* printer) {
}
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
if (IsMapEntry(descriptor_->nested_type(i))) continue;
MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer);
}
@ -342,6 +345,11 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
"classname", descriptor_->name());
printer->Indent();
if (HasMapField(descriptor_)) {
printer->Print(
"com.google.protobuf.nano.MapFactories.MapFactory mapFactory =\n"
" com.google.protobuf.nano.MapFactories.getMapFactory();\n");
}
printer->Print(
"while (true) {\n");

@ -100,15 +100,35 @@ std::string TypeName(const google::protobuf::FieldDescriptor* field) {
}
}
void GenerateMessage(const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
printer->Print(
"add_message \"$name$\" do\n",
"name", message->full_name());
printer->Indent();
void GenerateField(const google::protobuf::FieldDescriptor* field,
google::protobuf::io::Printer* printer) {
if (field->is_map()) {
const FieldDescriptor* key_field =
field->message_type()->FindFieldByNumber(1);
const FieldDescriptor* value_field =
field->message_type()->FindFieldByNumber(2);
printer->Print(
"map :$name$, :$key_type$, :$value_type$, $number$",
"name", field->name(),
"key_type", TypeName(key_field),
"value_type", TypeName(value_field),
"number", IntToString(field->number()));
if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
", \"$subtype$\"\n",
"subtype", value_field->message_type()->full_name());
} else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer->Print(
", \"$subtype$\"\n",
"subtype", value_field->enum_type()->full_name());
} else {
printer->Print("\n");
}
} else {
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
printer->Print(
"$label$ :$name$, ",
"label", LabelForField(field),
@ -117,6 +137,7 @@ void GenerateMessage(const google::protobuf::Descriptor* message,
":$type$, $number$",
"type", TypeName(field),
"number", IntToString(field->number()));
if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer->Print(
", \"$subtype$\"\n",
@ -129,6 +150,49 @@ void GenerateMessage(const google::protobuf::Descriptor* message,
printer->Print("\n");
}
}
}
void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
google::protobuf::io::Printer* printer) {
printer->Print(
"oneof :$name$ do\n",
"name", oneof->name());
printer->Indent();
for (int i = 0; i < oneof->field_count(); i++) {
const FieldDescriptor* field = oneof->field(i);
GenerateField(field, printer);
}
printer->Outdent();
printer->Print("end\n");
}
void GenerateMessage(const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
// Don't generate MapEntry messages -- we use the Ruby extension's native
// support for map fields instead.
if (message->options().map_entry()) {
return;
}
printer->Print(
"add_message \"$name$\" do\n",
"name", message->full_name());
printer->Indent();
for (int i = 0; i < message->field_count(); i++) {
const FieldDescriptor* field = message->field(i);
if (!field->containing_oneof()) {
GenerateField(field, printer);
}
}
for (int i = 0; i < message->oneof_decl_count(); i++) {
const OneofDescriptor* oneof = message->oneof_decl(i);
GenerateOneof(oneof, printer);
}
printer->Outdent();
printer->Print("end\n");
@ -185,6 +249,13 @@ void GenerateMessageAssignment(
const std::string& prefix,
const google::protobuf::Descriptor* message,
google::protobuf::io::Printer* printer) {
// Don't generate MapEntry messages -- we use the Ruby extension's native
// support for map fields instead.
if (message->options().map_entry()) {
return;
}
printer->Print(
"$prefix$$name$ = ",
"prefix", prefix,
@ -261,7 +332,7 @@ void GenerateFile(const google::protobuf::FileDescriptor* file,
"filename", file->name());
printer->Print(
"require 'protobuf'\n\n");
"require 'google/protobuf'\n\n");
for (int i = 0; i < file->dependency_count(); i++) {
const std::string& name = file->dependency(i)->name();
@ -297,6 +368,14 @@ bool Generator::Generate(
const string& parameter,
GeneratorContext* generator_context,
string* error) const {
if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
*error =
"Can only generate Ruby code for proto3 .proto files.\n"
"Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
return false;
}
std::string filename =
StripDotProto(file->name()) + ".rb";
scoped_ptr<io::ZeroCopyOutputStream> output(

@ -40,7 +40,8 @@ namespace protobuf {
namespace compiler {
namespace ruby {
class Generator : public google::protobuf::compiler::CodeGenerator {
class LIBPROTOC_EXPORT Generator
: public google::protobuf::compiler::CodeGenerator {
virtual bool Generate(
const FileDescriptor* file,
const string& parameter,

@ -0,0 +1,119 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2014 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <memory>
#include <google/protobuf/compiler/ruby/ruby_generator.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/testing/file.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace ruby {
namespace {
string FindRubyTestDir() {
// Inspired by TestSourceDir() in src/google/protobuf/testing/googletest.cc.
string prefix = ".";
while (!File::Exists(prefix + "/ruby/tests")) {
if (!File::Exists(prefix)) {
GOOGLE_LOG(FATAL)
<< "Could not find Ruby test directory. Please run tests from "
"somewhere within the protobuf source package.";
}
prefix += "/..";
}
return prefix + "/ruby/tests";
}
// This test is a simple golden-file test over the output of the Ruby code
// generator. When we make changes to the Ruby extension and alter the Ruby code
// generator to use those changes, we should (i) manually test the output of the
// code generator with the extension, and (ii) update the golden output above.
// Some day, we may integrate build systems between protoc and the language
// extensions to the point where we can do this test in a more automated way.
TEST(RubyGeneratorTest, GeneratorTest) {
string ruby_tests = FindRubyTestDir();
google::protobuf::compiler::CommandLineInterface cli;
cli.SetInputsAreProtoPathRelative(true);
ruby::Generator ruby_generator;
cli.RegisterGenerator("--ruby_out", &ruby_generator, "");
// Copy generated_code.proto to the temporary test directory.
string test_input;
GOOGLE_CHECK_OK(File::GetContents(
ruby_tests + "/generated_code.proto",
&test_input,
true));
GOOGLE_CHECK_OK(File::SetContents(
TestTempDir() + "/generated_code.proto",
test_input,
true));
// Invoke the proto compiler (we will be inside TestTempDir() at this point).
string ruby_out = "--ruby_out=" + TestTempDir();
string proto_path = "--proto_path=" + TestTempDir();
const char* argv[] = {
"protoc",
ruby_out.c_str(),
proto_path.c_str(),
"generated_code.proto",
};
EXPECT_EQ(0, cli.Run(4, argv));
// Load the generated output and compare to the expected result.
string output;
GOOGLE_CHECK_OK(File::GetContents(
TestTempDir() + "/generated_code.rb",
&output,
true));
string expected_output;
GOOGLE_CHECK_OK(File::GetContents(
ruby_tests + "/generated_code.rb",
&expected_output,
true));
EXPECT_EQ(expected_output, output);
}
} // namespace
} // namespace ruby
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -123,7 +123,8 @@
#endif
#else
#include <sys/param.h> // __BYTE_ORDER
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN && \
#if ((defined(__LITTLE_ENDIAN__) && !defined(__BIG_ENDIAN__)) || \
(defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN)) && \
!defined(PROTOBUF_DISABLE_LITTLE_ENDIAN_OPT_FOR_TEST)
#define PROTOBUF_LITTLE_ENDIAN 1
#endif

@ -65,7 +65,7 @@ inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
"2:\n"
".set pop\n"
: "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
: "Ir" (old_value), "r" (new_value), "m" (*ptr)
: "r" (old_value), "r" (new_value), "m" (*ptr)
: "memory");
return prev;
}
@ -197,7 +197,7 @@ inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
"2:\n"
".set pop\n"
: "=&r" (prev), "=m" (*ptr), "=&r" (tmp)
: "Ir" (old_value), "r" (new_value), "m" (*ptr)
: "r" (old_value), "r" (new_value), "m" (*ptr)
: "memory");
return prev;
}

1
upb

@ -1 +0,0 @@
Subproject commit 56913be6bb57f81dbbf7baf9cc9a0a2cd1a36493

@ -223,6 +223,10 @@
RelativePath="..\src\google\protobuf\compiler\cpp\cpp_string_field.h"
>
</File>
<File
RelativePath="..\src\google\protobuf\compiler\ruby\ruby_generator.h"
>
</File>
<File
RelativePath="..\src\google\protobuf\compiler\java\java_context.h"
>
@ -375,6 +379,10 @@
RelativePath="..\src\google\protobuf\compiler\cpp\cpp_string_field.cc"
>
</File>
<File
RelativePath="..\src\google\protobuf\compiler\ruby\ruby_generator.cc"
>
</File>
<File
RelativePath="..\src\google\protobuf\compiler\java\java_context.cc"
>

Loading…
Cancel
Save