am 0a1429cc: Merge "Nano support for extensions and unknown fields."

* commit '0a1429cc5ee4865550bcdddfae681a30cef58a5a':
  Nano support for extensions and unknown fields.
This commit is contained in:
Wink Saville 2013-06-25 09:56:06 -07:00 committed by Android Git Automerger
commit 50acfdac08
14 changed files with 806 additions and 25 deletions

View File

@ -108,6 +108,7 @@ COMPILER_SRC_FILES := \
src/google/protobuf/compiler/javamicro/javamicro_primitive_field.cc \ src/google/protobuf/compiler/javamicro/javamicro_primitive_field.cc \
src/google/protobuf/compiler/javanano/javanano_enum.cc \ src/google/protobuf/compiler/javanano/javanano_enum.cc \
src/google/protobuf/compiler/javanano/javanano_enum_field.cc \ src/google/protobuf/compiler/javanano/javanano_enum_field.cc \
src/google/protobuf/compiler/javanano/javanano_extension.cc \
src/google/protobuf/compiler/javanano/javanano_field.cc \ src/google/protobuf/compiler/javanano/javanano_field.cc \
src/google/protobuf/compiler/javanano/javanano_file.cc \ src/google/protobuf/compiler/javanano/javanano_file.cc \
src/google/protobuf/compiler/javanano/javanano_generator.cc \ src/google/protobuf/compiler/javanano/javanano_generator.cc \

View File

@ -540,6 +540,23 @@ public final class CodedInputByteBufferNano {
return bufferPos - bufferStart; return bufferPos - bufferStart;
} }
/**
* Retrieves a subset of data in the buffer. The returned array is not backed by the original
* buffer array.
*
* @param offset the position (relative to the buffer start position) to start at.
* @param length the number of bytes to retrieve.
*/
public byte[] getData(int offset, int length) {
if (length == 0) {
return WireFormatNano.EMPTY_BYTES;
}
byte[] copy = new byte[length];
int start = bufferStart + offset;
System.arraycopy(buffer, start, copy, 0, length);
return copy;
}
/** /**
* Rewind to previous position. Cannot go forward. * Rewind to previous position. Cannot go forward.
*/ */

View File

@ -0,0 +1,114 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2013 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.
package com.google.protobuf.nano;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
/**
* Represents an extension.
*
* @author bduff@google.com (Brian Duff)
* @param <T> the type of the extension.
*/
public class Extension<T> {
public final int fieldNumber;
public boolean isRepeatedField;
public Class<T> fieldType;
public Class<T> listType;
private Extension(int fieldNumber, TypeLiteral<T> type) {
this.fieldNumber = fieldNumber;
isRepeatedField = type.isList();
fieldType = type.getTargetClass();
listType = isRepeatedField ? type.getListType() : null;
}
/**
* Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
* {@code type}.
*/
public static <T> Extension<T> create(int fieldNumber, TypeLiteral<T> type) {
return new Extension<T>(fieldNumber, type);
}
/**
* Creates a new instance of {@code Extension} for the specified {@code fieldNumber} and
* {@code type}. This version is used for repeated fields.
*/
public static <T> Extension<List<T>> createRepeated(int fieldNumber, TypeLiteral<List<T>> type) {
return new Extension<List<T>>(fieldNumber, type);
}
/**
* Represents a generic type literal. We can't typesafely reference a
* Class&lt;List&lt;Foo>>.class in Java, so we use this instead.
* See: http://gafter.blogspot.com/2006/12/super-type-tokens.html
*
* <p>Somewhat specialized because we only ever have a Foo or a List&lt;Foo>.
*/
public static abstract class TypeLiteral<T> {
private final Type type;
protected TypeLiteral() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter");
}
this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
}
/**
* If the generic type is a list, returns {@code true}.
*/
private boolean isList() {
return type instanceof ParameterizedType;
}
@SuppressWarnings("unchecked")
private Class<T> getListType() {
return (Class<T>) ((ParameterizedType) type).getRawType();
}
/**
* If the generic type is a list, returns the type of element in the list. Otherwise,
* returns the actual type.
*/
@SuppressWarnings("unchecked")
private Class<T> getTargetClass() {
if (isList()) {
return (Class<T>) ((ParameterizedType) type).getActualTypeArguments()[0];
}
return (Class<T>) type;
}
}
}

View File

@ -93,7 +93,7 @@ public abstract class MessageNano {
output.checkNoSpaceLeft(); output.checkNoSpaceLeft();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException("Serializing to a byte array threw an IOException " throw new RuntimeException("Serializing to a byte array threw an IOException "
+ "(should never happen)."); + "(should never happen).", e);
} }
} }

View File

@ -0,0 +1,47 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2013 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.
package com.google.protobuf.nano;
/**
* Stores unknown fields. These might be extensions or fields that the generated API doesn't
* know about yet.
*
* @author bduff@google.com (Brian Duff)
*/
public final class UnknownFieldData {
final int tag;
final byte[] bytes;
UnknownFieldData(int tag, byte[] bytes) {
this.tag = tag;
this.bytes = bytes;
}
}

View File

@ -31,6 +31,9 @@
package com.google.protobuf.nano; package com.google.protobuf.nano;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/** /**
* This class is used internally by the Protocol Buffer library and generated * This class is used internally by the Protocol Buffer library and generated
@ -97,8 +100,12 @@ public final class WireFormatNano {
public static final byte[] EMPTY_BYTES = {}; public static final byte[] EMPTY_BYTES = {};
/** /**
* Called by subclasses to parse an unknown field. * Parses an unknown field. This implementation skips the field.
* @return {@code true} unless the tag is an end-group tag. *
* <p>Generated messages will call this for unknown fields if the store_unknown_fields
* option is off.
*
* @return {@literal true} unless the tag is an end-group tag.
*/ */
public static boolean parseUnknownField( public static boolean parseUnknownField(
final CodedInputByteBufferNano input, final CodedInputByteBufferNano input,
@ -106,6 +113,30 @@ public final class WireFormatNano {
return input.skipField(tag); return input.skipField(tag);
} }
/**
* Stores the binary data of an unknown field.
*
* <p>Generated messages will call this for unknown fields if the store_unknown_fields
* option is on.
*
* @param data a Collection in which to store the data.
* @param input the input buffer.
* @param tag the tag of the field.
* @return {@literal true} unless the tag is an end-group tag.
*/
public static boolean storeUnknownField(
final List<UnknownFieldData> data,
final CodedInputByteBufferNano input,
final int tag) throws IOException {
int startPos = input.getPosition();
boolean skip = input.skipField(tag);
int endPos = input.getPosition();
byte[] bytes = input.getData(startPos, endPos - startPos);
data.add(new UnknownFieldData(tag, bytes));
return skip;
}
/** /**
* Computes the array length of a repeated field. We assume that in the common case repeated * Computes the array length of a repeated field. We assume that in the common case repeated
* fields are contiguously serialized but we still correctly handle interspersed values of a * fields are contiguously serialized but we still correctly handle interspersed values of a
@ -135,4 +166,194 @@ public final class WireFormatNano {
input.rewindToPosition(startPos); input.rewindToPosition(startPos);
return arrayLength; return arrayLength;
} }
/**
* Decodes the value of an extension.
*/
public static <T> T getExtension(Extension<T> extension, List<UnknownFieldData> unknownFields) {
if (unknownFields == null) {
return null;
}
List<UnknownFieldData> dataForField = new ArrayList<UnknownFieldData>();
for (UnknownFieldData data : unknownFields) {
if (getTagFieldNumber(data.tag) == extension.fieldNumber) {
dataForField.add(data);
}
}
if (dataForField.isEmpty()) {
return null;
}
if (extension.isRepeatedField) {
List<Object> result = new ArrayList<Object>(dataForField.size());
for (UnknownFieldData data : dataForField) {
result.add(readData(extension.fieldType, data.bytes));
}
return extension.listType.cast(result);
}
// Normal fields. Note that the protobuf docs require us to handle multiple instances
// of the same field even for fields that are not repeated.
UnknownFieldData lastData = dataForField.get(dataForField.size() - 1);
return readData(extension.fieldType, lastData.bytes);
}
/**
* Reads (extension) data of the specified type from the specified byte array.
*
* @throws IllegalArgumentException if an error occurs while reading the data.
*/
private static <T> T readData(Class<T> clazz, byte[] data) {
if (data.length == 0) {
return null;
}
CodedInputByteBufferNano buffer = CodedInputByteBufferNano.newInstance(data);
try {
if (clazz == String.class) {
return clazz.cast(buffer.readString());
} else if (clazz == Integer.class) {
return clazz.cast(buffer.readInt32());
} else if (clazz == Long.class) {
return clazz.cast(buffer.readInt64());
} else if (clazz == Boolean.class) {
return clazz.cast(buffer.readBool());
} else if (clazz == Float.class) {
return clazz.cast(buffer.readFloat());
} else if (clazz == Double.class) {
return clazz.cast(buffer.readDouble());
} else if (clazz == byte[].class) {
return clazz.cast(buffer.readBytes());
} else if (MessageNano.class.isAssignableFrom(clazz)) {
try {
MessageNano message = (MessageNano) clazz.newInstance();
buffer.readMessage(message);
return clazz.cast(message);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
} catch (InstantiationException e) {
throw new IllegalArgumentException("Error creating instance of class " + clazz, e);
}
} else {
throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
}
} catch (IOException e) {
throw new IllegalArgumentException("Error reading extension field", e);
}
}
public static <T> void setExtension(Extension<T> extension, T value,
List<UnknownFieldData> unknownFields) {
// First, remove all unknown fields with this tag.
for (Iterator<UnknownFieldData> i = unknownFields.iterator(); i.hasNext();) {
UnknownFieldData data = i.next();
if (extension.fieldNumber == getTagFieldNumber(data.tag)) {
i.remove();
}
}
if (value == null) {
return;
}
// Repeated field.
if (value instanceof List) {
for (Object item : (List<?>) value) {
unknownFields.add(write(extension.fieldNumber, item));
}
} else {
unknownFields.add(write(extension.fieldNumber, value));
}
}
/**
* Writes extension data and returns an {@link UnknownFieldData} containing
* bytes and a tag.
*
* @throws IllegalArgumentException if an error occurs while writing.
*/
private static UnknownFieldData write(int fieldNumber, Object object) {
byte[] data;
int tag;
Class<?> clazz = object.getClass();
try {
if (clazz == String.class) {
String str = (String) object;
data = new byte[CodedOutputByteBufferNano.computeStringSizeNoTag(str)];
CodedOutputByteBufferNano.newInstance(data).writeStringNoTag(str);
tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
} else if (clazz == Integer.class) {
Integer integer = (Integer) object;
data = new byte[CodedOutputByteBufferNano.computeInt32SizeNoTag(integer)];
CodedOutputByteBufferNano.newInstance(data).writeInt32NoTag(integer);
tag = makeTag(fieldNumber, WIRETYPE_VARINT);
} else if (clazz == Long.class) {
Long longValue = (Long) object;
data = new byte[CodedOutputByteBufferNano.computeInt64SizeNoTag(longValue)];
CodedOutputByteBufferNano.newInstance(data).writeInt64NoTag(longValue);
tag = makeTag(fieldNumber, WIRETYPE_VARINT);
} else if (clazz == Boolean.class) {
Boolean boolValue = (Boolean) object;
data = new byte[CodedOutputByteBufferNano.computeBoolSizeNoTag(boolValue)];
CodedOutputByteBufferNano.newInstance(data).writeBoolNoTag(boolValue);
tag = makeTag(fieldNumber, WIRETYPE_VARINT);
} else if (clazz == Float.class) {
Float floatValue = (Float) object;
data = new byte[CodedOutputByteBufferNano.computeFloatSizeNoTag(floatValue)];
CodedOutputByteBufferNano.newInstance(data).writeFloatNoTag(floatValue);
tag = makeTag(fieldNumber, WIRETYPE_FIXED32);
} else if (clazz == Double.class) {
Double doubleValue = (Double) object;
data = new byte[CodedOutputByteBufferNano.computeDoubleSizeNoTag(doubleValue)];
CodedOutputByteBufferNano.newInstance(data).writeDoubleNoTag(doubleValue);
tag = makeTag(fieldNumber, WIRETYPE_FIXED64);
} else if (clazz == byte[].class) {
byte[] byteArrayValue = (byte[]) object;
data = new byte[CodedOutputByteBufferNano.computeByteArraySizeNoTag(byteArrayValue)];
CodedOutputByteBufferNano.newInstance(data).writeByteArrayNoTag(byteArrayValue);
tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
} else if (MessageNano.class.isAssignableFrom(clazz)) {
MessageNano messageValue = (MessageNano) object;
int messageSize = messageValue.getSerializedSize();
int delimiterSize = CodedOutputByteBufferNano.computeRawVarint32Size(messageSize);
data = new byte[messageSize + delimiterSize];
CodedOutputByteBufferNano buffer = CodedOutputByteBufferNano.newInstance(data);
buffer.writeRawVarint32(messageSize);
buffer.writeRawBytes(MessageNano.toByteArray(messageValue));
tag = makeTag(fieldNumber, WIRETYPE_LENGTH_DELIMITED);
} else {
throw new IllegalArgumentException("Unhandled extension field type: " + clazz);
}
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
return new UnknownFieldData(tag, data);
}
/**
* Given a set of unknown field data, compute the wire size.
*/
public static int computeWireSize(List<UnknownFieldData> unknownFields) {
if (unknownFields == null) {
return 0;
}
int size = 0;
for (UnknownFieldData unknownField : unknownFields) {
size += CodedOutputByteBufferNano.computeRawVarint32Size(unknownField.tag);
size += unknownField.bytes.length;
}
return size;
}
/**
* Write unknown fields.
*/
public static void writeUnknownFields(List<UnknownFieldData> unknownFields,
CodedOutputByteBufferNano outBuffer) throws IOException {
if (unknownFields == null) {
return;
}
for (UnknownFieldData data : unknownFields) {
outBuffer.writeTag(getTagFieldNumber(data.tag), getTagWireType(data.tag));
outBuffer.writeRawBytes(data.bytes);
}
}
} }

View File

@ -30,6 +30,9 @@
package com.google.protobuf; package com.google.protobuf;
import com.google.protobuf.nano.CodedInputByteBufferNano;
import com.google.protobuf.nano.Extensions;
import com.google.protobuf.nano.Extensions.AnotherMessage;
import com.google.protobuf.nano.InternalNano; import com.google.protobuf.nano.InternalNano;
import com.google.protobuf.nano.MessageNano; import com.google.protobuf.nano.MessageNano;
import com.google.protobuf.nano.NanoOuterClass; import com.google.protobuf.nano.NanoOuterClass;
@ -37,10 +40,12 @@ import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano;
import com.google.protobuf.nano.RecursiveMessageNano; import com.google.protobuf.nano.RecursiveMessageNano;
import com.google.protobuf.nano.SimpleMessageNano; import com.google.protobuf.nano.SimpleMessageNano;
import com.google.protobuf.nano.UnittestImportNano; import com.google.protobuf.nano.UnittestImportNano;
import com.google.protobuf.nano.CodedInputByteBufferNano;
import junit.framework.TestCase; import junit.framework.TestCase;
import java.util.ArrayList;
import java.util.List;
/** /**
* Test nano runtime. * Test nano runtime.
* *
@ -2155,4 +2160,93 @@ public class NanoTest extends TestCase {
assertTrue(protoPrint.contains(" default_int32: 41")); assertTrue(protoPrint.contains(" default_int32: 41"));
assertTrue(protoPrint.contains(" default_string: \"hello\"")); assertTrue(protoPrint.contains(" default_string: \"hello\""));
} }
public void testExtensions() throws Exception {
Extensions.ExtendableMessage message = new Extensions.ExtendableMessage();
message.field = 5;
message.setExtension(Extensions.someString, "Hello World!");
message.setExtension(Extensions.someBool, true);
message.setExtension(Extensions.someInt, 42);
message.setExtension(Extensions.someLong, 124234234234L);
message.setExtension(Extensions.someFloat, 42.0f);
message.setExtension(Extensions.someDouble, 422222.0);
message.setExtension(Extensions.someEnum, Extensions.FIRST_VALUE);
AnotherMessage another = new AnotherMessage();
another.string = "Foo";
another.value = true;
message.setExtension(Extensions.someMessage, another);
message.setExtension(Extensions.someRepeatedString, list("a", "bee", "seeya"));
message.setExtension(Extensions.someRepeatedBool, list(true, false, true));
message.setExtension(Extensions.someRepeatedInt, list(4, 8, 15, 16, 23, 42));
message.setExtension(Extensions.someRepeatedLong, list(4L, 8L, 15L, 16L, 23L, 42L));
message.setExtension(Extensions.someRepeatedFloat, list(1.0f, 3.0f));
message.setExtension(Extensions.someRepeatedDouble, list(55.133, 3.14159));
message.setExtension(Extensions.someRepeatedEnum, list(Extensions.FIRST_VALUE,
Extensions.SECOND_VALUE));
AnotherMessage second = new AnotherMessage();
second.string = "Whee";
second.value = false;
message.setExtension(Extensions.someRepeatedMessage, list(another, second));
byte[] data = MessageNano.toByteArray(message);
Extensions.ExtendableMessage deserialized = Extensions.ExtendableMessage.parseFrom(data);
assertEquals(5, deserialized.field);
assertEquals("Hello World!", deserialized.getExtension(Extensions.someString));
assertEquals(Boolean.TRUE, deserialized.getExtension(Extensions.someBool));
assertEquals(Integer.valueOf(42), deserialized.getExtension(Extensions.someInt));
assertEquals(Long.valueOf(124234234234L), deserialized.getExtension(Extensions.someLong));
assertEquals(Float.valueOf(42.0f), deserialized.getExtension(Extensions.someFloat));
assertEquals(Double.valueOf(422222.0), deserialized.getExtension(Extensions.someDouble));
assertEquals(Integer.valueOf(Extensions.FIRST_VALUE),
deserialized.getExtension(Extensions.someEnum));
assertEquals(another.string, deserialized.getExtension(Extensions.someMessage).string);
assertEquals(another.value, deserialized.getExtension(Extensions.someMessage).value);
assertEquals(list("a", "bee", "seeya"), deserialized.getExtension(Extensions.someRepeatedString));
assertEquals(list(true, false, true), deserialized.getExtension(Extensions.someRepeatedBool));
assertEquals(list(4, 8, 15, 16, 23, 42), deserialized.getExtension(Extensions.someRepeatedInt));
assertEquals(list(4L, 8L, 15L, 16L, 23L, 42L), deserialized.getExtension(Extensions.someRepeatedLong));
assertEquals(list(1.0f, 3.0f), deserialized.getExtension(Extensions.someRepeatedFloat));
assertEquals(list(55.133, 3.14159), deserialized.getExtension(Extensions.someRepeatedDouble));
assertEquals(list(Extensions.FIRST_VALUE,
Extensions.SECOND_VALUE), deserialized.getExtension(Extensions.someRepeatedEnum));
assertEquals("Foo", deserialized.getExtension(Extensions.someRepeatedMessage).get(0).string);
assertEquals(true, deserialized.getExtension(Extensions.someRepeatedMessage).get(0).value);
assertEquals("Whee", deserialized.getExtension(Extensions.someRepeatedMessage).get(1).string);
assertEquals(false, deserialized.getExtension(Extensions.someRepeatedMessage).get(1).value);
}
public void testUnknownFields() throws Exception {
// Check that we roundtrip (serialize and deserialize) unrecognized fields.
AnotherMessage message = new AnotherMessage();
message.string = "Hello World";
message.value = false;
byte[] bytes = MessageNano.toByteArray(message);
int extraFieldSize = CodedOutputStream.computeStringSize(1001, "This is an unknown field");
byte[] newBytes = new byte[bytes.length + extraFieldSize];
System.arraycopy(bytes, 0, newBytes, 0, bytes.length);
CodedOutputStream.newInstance(newBytes, bytes.length, extraFieldSize).writeString(1001,
"This is an unknown field");
// Deserialize with an unknown field.
AnotherMessage deserialized = AnotherMessage.parseFrom(newBytes);
byte[] serialized = MessageNano.toByteArray(deserialized);
assertEquals(newBytes.length, serialized.length);
// Clear, and make sure it clears everything.
deserialized.clear();
assertEquals(0, MessageNano.toByteArray(deserialized).length);
}
private <T> List<T> list(T first, T... remaining) {
List<T> list = new ArrayList<T>();
list.add(first);
for (T item : remaining) {
list.add(item);
}
return list;
}
} }

View File

@ -0,0 +1,96 @@
// 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.
// Author: bduff@google.com (Brian Duff)
#include <google/protobuf/compiler/javanano/javanano_extension.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/wire_format.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace javanano {
using internal::WireFormat;
void SetVariables(const FieldDescriptor* descriptor, const Params params,
map<string, string>* variables) {
(*variables)["name"] = UnderscoresToCamelCase(descriptor);
(*variables)["number"] = SimpleItoa(descriptor->number());
(*variables)["extends"] = ClassName(params, descriptor->containing_type());
string type;
JavaType java_type = GetJavaType(descriptor->type());
switch (java_type) {
case JAVATYPE_ENUM:
type = "java.lang.Integer";
break;
case JAVATYPE_MESSAGE:
type = ClassName(params, descriptor->message_type());
break;
default:
type = BoxedPrimitiveTypeName(java_type);
break;
}
(*variables)["type"] = type;
}
ExtensionGenerator::
ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params)
: params_(params), descriptor_(descriptor) {
SetVariables(descriptor, params, &variables_);
}
ExtensionGenerator::~ExtensionGenerator() {}
void ExtensionGenerator::Generate(io::Printer* printer) const {
if (descriptor_->is_repeated()) {
printer->Print(variables_,
"// Extends $extends$\n"
"public static final com.google.protobuf.nano.Extension<java.util.List<$type$>> $name$ = \n"
" com.google.protobuf.nano.Extension.createRepeated($number$,\n"
" new com.google.protobuf.nano.Extension.TypeLiteral<java.util.List<$type$>>(){});\n");
} else {
printer->Print(variables_,
"// Extends $extends$\n"
"public static final com.google.protobuf.nano.Extension<$type$> $name$ =\n"
" com.google.protobuf.nano.Extension.create($number$,\n"
" new com.google.protobuf.nano.Extension.TypeLiteral<$type$>(){});\n");
}
}
} // namespace javanano
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@ -0,0 +1,74 @@
// 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.
// Author: bduff@google.com (Brian Duff)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_
#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/javanano/javanano_params.h>
#include <google/protobuf/descriptor.pb.h>
namespace google {
namespace protobuf {
namespace io {
class Printer; // printer.h
}
}
namespace protobuf {
namespace compiler {
namespace javanano {
class ExtensionGenerator {
public:
explicit ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params);
~ExtensionGenerator();
void Generate(io::Printer* printer) const;
private:
const Params& params_;
const FieldDescriptor* descriptor_;
map<string, string> variables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator);
};
} // namespace javanano
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H_

View File

@ -34,6 +34,7 @@
#include <google/protobuf/compiler/javanano/javanano_file.h> #include <google/protobuf/compiler/javanano/javanano_file.h>
#include <google/protobuf/compiler/javanano/javanano_enum.h> #include <google/protobuf/compiler/javanano/javanano_enum.h>
#include <google/protobuf/compiler/javanano/javanano_extension.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h> #include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/compiler/javanano/javanano_message.h> #include <google/protobuf/compiler/javanano/javanano_message.h>
#include <google/protobuf/compiler/code_generator.h> #include <google/protobuf/compiler/code_generator.h>
@ -94,10 +95,11 @@ bool FileGenerator::Validate(string* error) {
// Check for extensions // Check for extensions
FileDescriptorProto file_proto; FileDescriptorProto file_proto;
file_->CopyTo(&file_proto); file_->CopyTo(&file_proto);
if (UsesExtensions(file_proto)) { if (UsesExtensions(file_proto) && !params_.store_unknown_fields()) {
error->assign(file_->name()); error->assign(file_->name());
error->append( error->append(
": Java NANO_RUNTIME does not support extensions\""); ": Java NANO_RUNTIME only supports extensions when the "
"'store_unknown_fields' generator option is 'true'.");
return false; return false;
} }
@ -179,6 +181,11 @@ void FileGenerator::Generate(io::Printer* printer) {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// Extensions.
for (int i = 0; i < file_->extension_count(); i++) {
ExtensionGenerator(file_->extension(i), params_).Generate(printer);
}
if (!params_.java_multiple_files()) { if (!params_.java_multiple_files()) {
for (int i = 0; i < file_->enum_type_count(); i++) { for (int i = 0; i < file_->enum_type_count(); i++) {
EnumGenerator(file_->enum_type(i), params_).Generate(printer); EnumGenerator(file_->enum_type(i), params_).Generate(printer);

View File

@ -115,6 +115,8 @@ bool JavaNanoGenerator::Generate(const FileDescriptor* file,
return false; return false;
} }
params.set_java_outer_classname(parts[0], parts[1]); params.set_java_outer_classname(parts[0], parts[1]);
} else if (options[i].first == "store_unknown_fields") {
params.set_store_unknown_fields(options[i].second == "true");
} else if (options[i].first == "java_multiple_files") { } else if (options[i].first == "java_multiple_files") {
params.set_java_multiple_files(options[i].second == "true"); params.set_java_multiple_files(options[i].second == "true");
} else { } else {

View File

@ -36,6 +36,7 @@
#include <google/protobuf/stubs/hash.h> #include <google/protobuf/stubs/hash.h>
#include <google/protobuf/compiler/javanano/javanano_message.h> #include <google/protobuf/compiler/javanano/javanano_message.h>
#include <google/protobuf/compiler/javanano/javanano_enum.h> #include <google/protobuf/compiler/javanano/javanano_enum.h>
#include <google/protobuf/compiler/javanano/javanano_extension.h>
#include <google/protobuf/compiler/javanano/javanano_helpers.h> #include <google/protobuf/compiler/javanano/javanano_helpers.h>
#include <google/protobuf/stubs/strutil.h> #include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/io/printer.h> #include <google/protobuf/io/printer.h>
@ -117,10 +118,6 @@ void MessageGenerator::GenerateStaticVariableInitializers(
MessageGenerator(descriptor_->nested_type(i), params_) MessageGenerator(descriptor_->nested_type(i), params_)
.GenerateStaticVariableInitializers(printer); .GenerateStaticVariableInitializers(printer);
} }
if (descriptor_->extension_count() != 0) {
GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n";
}
} }
void MessageGenerator::Generate(io::Printer* printer) { void MessageGenerator::Generate(io::Printer* printer) {
@ -135,9 +132,10 @@ void MessageGenerator::Generate(io::Printer* printer) {
GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name()); GOOGLE_LOG(INFO) << "has_java_outer_classname()=" << params_.has_java_outer_classname(file_->name());
#endif #endif
if ((descriptor_->extension_count() != 0) if (!params_.store_unknown_fields() &&
|| (descriptor_->extension_range_count() != 0)) { (descriptor_->extension_count() != 0 || descriptor_->extension_range_count() != 0)) {
GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n"; GOOGLE_LOG(FATAL) << "Extensions are only supported in NANO_RUNTIME if the "
"'store_unknown_fields' generator option is 'true'\n";
} }
// Note: Fields (which will be emitted in the loop, below) may have the same names as fields in // Note: Fields (which will be emitted in the loop, below) may have the same names as fields in
@ -156,7 +154,17 @@ void MessageGenerator::Generate(io::Printer* printer) {
"\n", "\n",
"classname", descriptor_->name()); "classname", descriptor_->name());
if (params_.store_unknown_fields()) {
printer->Print(
"private java.util.List<com.google.protobuf.nano.UnknownFieldData>\n"
" unknownFieldData;\n");
}
// Nested types and extensions // Nested types and extensions
for (int i = 0; i < descriptor_->extension_count(); i++) {
ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer);
}
for (int i = 0; i < descriptor_->enum_type_count(); i++) { for (int i = 0; i < descriptor_->enum_type_count(); i++) {
EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer); EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer);
} }
@ -173,6 +181,24 @@ void MessageGenerator::Generate(io::Printer* printer) {
} }
GenerateClear(printer); GenerateClear(printer);
// If we have an extension range, generate accessors for extensions.
if (params_.store_unknown_fields()
&& descriptor_->extension_range_count() > 0) {
printer->Print(
"public <T> T getExtension(com.google.protobuf.nano.Extension<T> extension) {\n"
" return com.google.protobuf.nano.WireFormatNano.getExtension(\n"
" extension, unknownFieldData);\n"
"}\n\n"
"public <T> void setExtension(com.google.protobuf.nano.Extension<T> extension, T value) {\n"
" if (unknownFieldData == null) {\n"
" unknownFieldData = \n"
" new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
" }\n"
" com.google.protobuf.nano.WireFormatNano.setExtension(\n"
" extension, value, unknownFieldData);\n"
"}\n\n");
}
GenerateMessageSerializationMethods(printer); GenerateMessageSerializationMethods(printer);
GenerateMergeFromMethods(printer); GenerateMergeFromMethods(printer);
GenerateParseFromMethods(printer); GenerateParseFromMethods(printer);
@ -188,12 +214,8 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
scoped_array<const FieldDescriptor*> sorted_fields( scoped_array<const FieldDescriptor*> sorted_fields(
SortFieldsByNumber(descriptor_)); SortFieldsByNumber(descriptor_));
if (descriptor_->extension_range_count() != 0) {
GOOGLE_LOG(FATAL) << "Extensions not supported in NANO_RUNTIME\n";
}
// writeTo only throws an exception if it contains one or more fields to write // writeTo only throws an exception if it contains one or more fields to write
if (descriptor_->field_count() > 0) { if (descriptor_->field_count() > 0 || params_.store_unknown_fields()) {
printer->Print( printer->Print(
"@Override\n" "@Override\n"
"public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n" "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n"
@ -207,7 +229,14 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
// Output the fields in sorted order // Output the fields in sorted order
for (int i = 0; i < descriptor_->field_count(); i++) { for (int i = 0; i < descriptor_->field_count(); i++) {
GenerateSerializeOneField(printer, sorted_fields[i]); GenerateSerializeOneField(printer, sorted_fields[i]);
}
// Write unknown fields.
if (params_.store_unknown_fields()) {
printer->Print(
"com.google.protobuf.nano.WireFormatNano.writeUnknownFields(\n"
" unknownFieldData, output);\n");
} }
printer->Outdent(); printer->Outdent();
@ -233,6 +262,11 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer);
} }
if (params_.store_unknown_fields()) {
printer->Print(
"size += com.google.protobuf.nano.WireFormatNano.computeWireSize(unknownFieldData);\n");
}
printer->Outdent(); printer->Outdent();
printer->Print( printer->Print(
" cachedSize = size;\n" " cachedSize = size;\n"
@ -266,12 +300,28 @@ void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) {
printer->Print( printer->Print(
"case 0:\n" // zero signals EOF / limit reached "case 0:\n" // zero signals EOF / limit reached
" return this;\n" " return this;\n"
"default: {\n" "default: {\n");
" if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {\n"
" return this;\n" // it's an endgroup tag printer->Indent();
" }\n" if (params_.store_unknown_fields()) {
" break;\n" printer->Print(
"}\n"); "if (unknownFieldData == null) {\n"
" unknownFieldData = \n"
" new java.util.ArrayList<com.google.protobuf.nano.UnknownFieldData>();\n"
"}\n"
"if (!com.google.protobuf.nano.WireFormatNano.storeUnknownField(unknownFieldData, \n"
" input, tag)) {\n"
" return this;\n"
"}\n");
} else {
printer->Print(
"if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {\n"
" return this;\n" // it's an endgroup tag
"}\n");
}
printer->Print("break;\n");
printer->Outdent();
printer->Print("}\n");
for (int i = 0; i < descriptor_->field_count(); i++) { for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = sorted_fields[i]; const FieldDescriptor* field = sorted_fields[i];
@ -356,6 +406,11 @@ void MessageGenerator::GenerateClear(io::Printer* printer) {
} }
} }
// Clear unknown fields.
if (params_.store_unknown_fields()) {
printer->Print("unknownFieldData = null;\n");
}
printer->Outdent(); printer->Outdent();
printer->Print( printer->Print(
" cachedSize = -1;\n" " cachedSize = -1;\n"

View File

@ -49,6 +49,7 @@ class Params {
string empty_; string empty_;
string base_name_; string base_name_;
bool java_multiple_files_; bool java_multiple_files_;
bool store_unknown_fields_;
NameMap java_packages_; NameMap java_packages_;
NameMap java_outer_classnames_; NameMap java_outer_classnames_;
@ -56,6 +57,7 @@ class Params {
Params(const string & base_name) : Params(const string & base_name) :
empty_(""), empty_(""),
base_name_(base_name), base_name_(base_name),
store_unknown_fields_(false),
java_multiple_files_(false) { java_multiple_files_(false) {
} }
@ -107,6 +109,13 @@ class Params {
return java_outer_classnames_; return java_outer_classnames_;
} }
void set_store_unknown_fields(bool value) {
store_unknown_fields_ = value;
}
bool store_unknown_fields() const {
return store_unknown_fields_;
}
void set_java_multiple_files(bool value) { void set_java_multiple_files(bool value) {
java_multiple_files_ = value; java_multiple_files_ = value;
} }

View File

@ -0,0 +1,44 @@
syntax = "proto2";
option java_outer_classname = "Extensions";
option java_package = "com.google.protobuf.nano";
message ExtendableMessage {
optional int32 field = 1;
extensions 10 to max;
}
enum AnEnum {
FIRST_VALUE = 1;
SECOND_VALUE = 2;
}
message AnotherMessage {
optional string string = 1;
optional bool value = 2;
}
extend ExtendableMessage {
optional string some_string = 10;
optional int32 some_int = 11;
optional int64 some_long = 12;
optional float some_float = 13;
optional double some_double = 14;
optional bool some_bool = 15;
optional AnEnum some_enum = 16;
optional AnotherMessage some_message = 17;
repeated string some_repeated_string = 18;
repeated int32 some_repeated_int = 19;
repeated int64 some_repeated_long = 20;
repeated float some_repeated_float = 21;
repeated double some_repeated_double = 22;
repeated bool some_repeated_bool = 23;
repeated AnEnum some_repeated_enum = 24;
repeated AnotherMessage some_repeated_message = 25;
}
message ContainerMessage {
extend ExtendableMessage {
optional bool another_thing = 100;
}
}