Integrate google internal changes.
This commit is contained in:
parent
78105897a8
commit
3b3c8abb96
@ -193,6 +193,8 @@ java_EXTRA_DIST=
|
||||
java/core/src/main/java/com/google/protobuf/BlockingRpcChannel.java \
|
||||
java/core/src/main/java/com/google/protobuf/BlockingService.java \
|
||||
java/core/src/main/java/com/google/protobuf/BooleanArrayList.java \
|
||||
java/core/src/main/java/com/google/protobuf/ByteBufferWriter.java \
|
||||
java/core/src/main/java/com/google/protobuf/ByteOutput.java \
|
||||
java/core/src/main/java/com/google/protobuf/ByteString.java \
|
||||
java/core/src/main/java/com/google/protobuf/CodedInputStream.java \
|
||||
java/core/src/main/java/com/google/protobuf/CodedOutputStream.java \
|
||||
@ -243,6 +245,8 @@ java_EXTRA_DIST=
|
||||
java/core/src/main/java/com/google/protobuf/SmallSortedMap.java \
|
||||
java/core/src/main/java/com/google/protobuf/TextFormat.java \
|
||||
java/core/src/main/java/com/google/protobuf/TextFormatEscaper.java \
|
||||
java/core/src/main/java/com/google/protobuf/TextFormatParseInfoTree.java \
|
||||
java/core/src/main/java/com/google/protobuf/TextFormatParseLocation.java \
|
||||
java/core/src/main/java/com/google/protobuf/UninitializedMessageException.java \
|
||||
java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java \
|
||||
java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java \
|
||||
@ -254,6 +258,7 @@ java_EXTRA_DIST=
|
||||
java/core/src/test/java/com/google/protobuf/AnyTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/BooleanArrayListTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/ByteBufferWriterTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/ByteStringTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/CheckUtf8Test.java \
|
||||
java/core/src/test/java/com/google/protobuf/CodedInputStreamTest.java \
|
||||
@ -262,6 +267,7 @@ java_EXTRA_DIST=
|
||||
java/core/src/test/java/com/google/protobuf/DescriptorsTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/DoubleArrayListTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/DynamicMessageTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/EnumTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/FieldPresenceTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/FloatArrayListTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/ForceFieldBuildersPreRun.java \
|
||||
@ -294,6 +300,8 @@ java_EXTRA_DIST=
|
||||
java/core/src/test/java/com/google/protobuf/SmallSortedMapTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/TestBadIdentifiers.java \
|
||||
java/core/src/test/java/com/google/protobuf/TestUtil.java \
|
||||
java/core/src/test/java/com/google/protobuf/TextFormatParseInfoTreeTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/TextFormatParseLocationTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/TextFormatTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \
|
||||
java/core/src/test/java/com/google/protobuf/UnknownFieldSetLiteTest.java \
|
||||
|
@ -129,7 +129,7 @@ class ConformanceJava {
|
||||
typeRegistry = TypeRegistry.newBuilder().add(
|
||||
Conformance.TestAllTypes.getDescriptor()).build();
|
||||
while (doTestIo()) {
|
||||
// Empty.
|
||||
this.testCount++;
|
||||
}
|
||||
|
||||
System.err.println("ConformanceJava: received EOF from test runner after " +
|
||||
|
125
conformance/ConformanceJavaLite.java
Normal file
125
conformance/ConformanceJavaLite.java
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
import com.google.protobuf.conformance.Conformance;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
class ConformanceJavaLite {
|
||||
private int testCount = 0;
|
||||
|
||||
private boolean readFromStdin(byte[] buf, int len) throws Exception {
|
||||
int ofs = 0;
|
||||
while (len > 0) {
|
||||
int read = System.in.read(buf, ofs, len);
|
||||
if (read == -1) {
|
||||
return false; // EOF
|
||||
}
|
||||
ofs += read;
|
||||
len -= read;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeToStdout(byte[] buf) throws Exception {
|
||||
System.out.write(buf);
|
||||
}
|
||||
|
||||
// Returns -1 on EOF (the actual values will always be positive).
|
||||
private int readLittleEndianIntFromStdin() throws Exception {
|
||||
byte[] buf = new byte[4];
|
||||
if (!readFromStdin(buf, 4)) {
|
||||
return -1;
|
||||
}
|
||||
return (buf[0] & 0xff)
|
||||
| ((buf[1] & 0xff) << 8)
|
||||
| ((buf[2] & 0xff) << 16)
|
||||
| ((buf[3] & 0xff) << 24);
|
||||
}
|
||||
|
||||
private void writeLittleEndianIntToStdout(int val) throws Exception {
|
||||
byte[] buf = new byte[4];
|
||||
buf[0] = (byte)val;
|
||||
buf[1] = (byte)(val >> 8);
|
||||
buf[2] = (byte)(val >> 16);
|
||||
buf[3] = (byte)(val >> 24);
|
||||
writeToStdout(buf);
|
||||
}
|
||||
|
||||
private Conformance.ConformanceResponse doTest(Conformance.ConformanceRequest request) {
|
||||
Conformance.TestAllTypes testMessage;
|
||||
|
||||
switch (request.getPayloadCase()) {
|
||||
case PROTOBUF_PAYLOAD: {
|
||||
try {
|
||||
testMessage = Conformance.TestAllTypes.parseFrom(request.getProtobufPayload());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
return Conformance.ConformanceResponse.newBuilder().setParseError(e.getMessage()).build();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSON_PAYLOAD: {
|
||||
return Conformance.ConformanceResponse.newBuilder().setSkipped(
|
||||
"Lite runtime does not suport Json Formant.").build();
|
||||
}
|
||||
case PAYLOAD_NOT_SET: {
|
||||
throw new RuntimeException("Request didn't have payload.");
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new RuntimeException("Unexpected payload case.");
|
||||
}
|
||||
}
|
||||
|
||||
switch (request.getRequestedOutputFormat()) {
|
||||
case UNSPECIFIED:
|
||||
throw new RuntimeException("Unspecified output format.");
|
||||
|
||||
case PROTOBUF:
|
||||
return Conformance.ConformanceResponse.newBuilder().setProtobufPayload(testMessage.toByteString()).build();
|
||||
|
||||
case JSON:
|
||||
return Conformance.ConformanceResponse.newBuilder().setSkipped(
|
||||
"Lite runtime does not suport Json Formant.").build();
|
||||
|
||||
default: {
|
||||
throw new RuntimeException("Unexpected request output.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doTestIo() throws Exception {
|
||||
int bytes = readLittleEndianIntFromStdin();
|
||||
|
||||
if (bytes == -1) {
|
||||
return false; // EOF
|
||||
}
|
||||
|
||||
byte[] serializedInput = new byte[bytes];
|
||||
|
||||
if (!readFromStdin(serializedInput, bytes)) {
|
||||
throw new RuntimeException("Unexpected EOF from test program.");
|
||||
}
|
||||
|
||||
Conformance.ConformanceRequest request =
|
||||
Conformance.ConformanceRequest.parseFrom(serializedInput);
|
||||
Conformance.ConformanceResponse response = doTest(request);
|
||||
byte[] serializedOutput = response.toByteArray();
|
||||
|
||||
writeLittleEndianIntToStdout(serializedOutput.length);
|
||||
writeToStdout(serializedOutput);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void run() throws Exception {
|
||||
while (doTestIo()) {
|
||||
this.testCount++;
|
||||
}
|
||||
|
||||
System.err.println("ConformanceJavaLite: received EOF from test runner after " +
|
||||
this.testCount + " tests");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new ConformanceJavaLite().run();
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ protoc_outputs = \
|
||||
other_language_protoc_outputs = \
|
||||
conformance.rb \
|
||||
com/google/protobuf/conformance/Conformance.java \
|
||||
lite/com/google/protobuf/conformance/Conformance.java \
|
||||
conformance_pb2.py \
|
||||
Conformance.pbobjc.h \
|
||||
Conformance.pbobjc.m
|
||||
@ -30,6 +31,7 @@ bin_PROGRAMS = conformance-test-runner conformance-cpp
|
||||
# automatically.
|
||||
EXTRA_DIST = \
|
||||
ConformanceJava.java \
|
||||
ConformanceJavaLite.java \
|
||||
README.md \
|
||||
conformance.proto \
|
||||
conformance_python.py \
|
||||
@ -88,6 +90,7 @@ if USE_EXTERNAL_PROTOC
|
||||
protoc_middleman: $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
|
||||
$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --objc_out=. --python_out=. $(conformance_protoc_inputs)
|
||||
$(PROTOC) -I$(srcdir) -I$(top_srcdir) --cpp_out=. --java_out=. --ruby_out=. --python_out=. $(well_known_type_protoc_inputs)
|
||||
$(PROTOC) -I$(srcdir) -I$(top_srcdir) --java_out=lite:lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
|
||||
touch protoc_middleman
|
||||
|
||||
else
|
||||
@ -98,6 +101,8 @@ else
|
||||
protoc_middleman: $(top_srcdir)/src/protoc$(EXEEXT) $(conformance_protoc_inputs) $(well_known_type_protoc_inputs)
|
||||
oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --objc_out=$$oldpwd --python_out=$$oldpwd $(conformance_protoc_inputs) )
|
||||
oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --cpp_out=$$oldpwd --java_out=$$oldpwd --ruby_out=$$oldpwd --python_out=$$oldpwd $(well_known_type_protoc_inputs) )
|
||||
@mkdir -p lite
|
||||
oldpwd=`pwd` && ( cd $(srcdir) && $$oldpwd/../src/protoc$(EXEEXT) -I. -I$(top_srcdir)/src --java_out=lite:$$oldpwd/lite $(conformance_protoc_inputs) $(well_known_type_protoc_inputs) )
|
||||
touch protoc_middleman
|
||||
|
||||
endif
|
||||
@ -108,7 +113,7 @@ $(other_language_protoc_outputs): protoc_middleman
|
||||
|
||||
BUILT_SOURCES = $(protoc_outputs) $(other_language_protoc_outputs)
|
||||
|
||||
CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java conformance-csharp $(other_language_protoc_outputs)
|
||||
CLEANFILES = $(protoc_outputs) protoc_middleman javac_middleman conformance-java javac_middleman_lite conformance-java-lite conformance-csharp $(other_language_protoc_outputs)
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
Makefile.in
|
||||
@ -123,6 +128,16 @@ conformance-java: javac_middleman
|
||||
@jar=`ls ../java/util/target/*jar-with-dependencies.jar` && echo java -classpath .:../java/target/classes:$$jar ConformanceJava '$$@' >> conformance-java
|
||||
@chmod +x conformance-java
|
||||
|
||||
javac_middleman_lite: ConformanceJavaLite.java protoc_middleman $(other_language_protoc_outputs)
|
||||
javac -classpath ../java/lite/target/classes:lite ConformanceJavaLite.java lite/com/google/protobuf/conformance/Conformance.java
|
||||
@touch javac_middleman_lite
|
||||
|
||||
conformance-java-lite: javac_middleman_lite
|
||||
@echo "Writing shortcut script conformance-java-lite..."
|
||||
@echo '#! /bin/sh' > conformance-java-lite
|
||||
@echo java -classpath .:../java/lite/target/classes:lite ConformanceJavaLite '$$@' >> conformance-java-lite
|
||||
@chmod +x conformance-java-lite
|
||||
|
||||
# Currently the conformance code is alongside the rest of the C#
|
||||
# source, as it's easier to maintain there. We assume we've already
|
||||
# built that, so we just need a script to run it.
|
||||
@ -139,6 +154,9 @@ test_cpp: protoc_middleman conformance-test-runner conformance-cpp
|
||||
test_java: protoc_middleman conformance-test-runner conformance-java
|
||||
./conformance-test-runner --failure_list failure_list_java.txt ./conformance-java
|
||||
|
||||
test_java_lite: protoc_middleman conformance-test-runner conformance-java-lite
|
||||
./conformance-test-runner ./conformance-java-lite
|
||||
|
||||
test_csharp: protoc_middleman conformance-test-runner conformance-csharp
|
||||
./conformance-test-runner --failure_list failure_list_csharp.txt ./conformance-csharp
|
||||
|
||||
|
@ -10,8 +10,6 @@
|
||||
# to make when building protoc. This is particularly useful for passing
|
||||
# -j4 to run 4 jobs simultaneously.
|
||||
|
||||
set -e
|
||||
|
||||
if test ! -e src/google/protobuf/stubs/common.h; then
|
||||
cat >&2 << __EOF__
|
||||
Could not find source code. Make sure you are running this script from the
|
||||
@ -52,9 +50,14 @@ do
|
||||
echo "Round $PROCESS_ROUND"
|
||||
CORE_PROTO_IS_CORRECT=1
|
||||
|
||||
make $@ protoc &&
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP google/protobuf/compiler/plugin.proto
|
||||
make $@ protoc
|
||||
if test $? -ne 0; then
|
||||
echo "Failed to build protoc."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:$TMP ${RUNTIME_PROTO_FILES[@]} && \
|
||||
./protoc --cpp_out=dllexport_decl=LIBPROTOC_EXPORT:$TMP google/protobuf/compiler/plugin.proto
|
||||
|
||||
for PROTO_FILE in ${RUNTIME_PROTO_FILES[@]}; do
|
||||
BASE_NAME=${PROTO_FILE%.*}
|
||||
|
@ -0,0 +1,145 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Utility class to provide efficient writing of {@link ByteBuffer}s to {@link OutputStream}s.
|
||||
*/
|
||||
final class ByteBufferWriter {
|
||||
private ByteBufferWriter() {}
|
||||
|
||||
/**
|
||||
* Minimum size for a cached buffer. This prevents us from allocating buffers that are too
|
||||
* small to be easily reused.
|
||||
*/
|
||||
// TODO(nathanmittler): tune this property or allow configuration?
|
||||
private static final int MIN_CACHED_BUFFER_SIZE = 1024;
|
||||
|
||||
/**
|
||||
* Maximum size for a cached buffer. If a larger buffer is required, it will be allocated
|
||||
* but not cached.
|
||||
*/
|
||||
// TODO(nathanmittler): tune this property or allow configuration?
|
||||
private static final int MAX_CACHED_BUFFER_SIZE = 16 * 1024;
|
||||
|
||||
/**
|
||||
* The fraction of the requested buffer size under which the buffer will be reallocated.
|
||||
*/
|
||||
// TODO(nathanmittler): tune this property or allow configuration?
|
||||
private static final float BUFFER_REALLOCATION_THRESHOLD = 0.5f;
|
||||
|
||||
/**
|
||||
* Keeping a soft reference to a thread-local buffer. This buffer is used for writing a
|
||||
* {@link ByteBuffer} to an {@link OutputStream} when no zero-copy alternative was available.
|
||||
* Using a "soft" reference since VMs may keep this reference around longer than "weak"
|
||||
* (e.g. HotSpot will maintain soft references until memory pressure warrants collection).
|
||||
*/
|
||||
private static final ThreadLocal<SoftReference<byte[]>> BUFFER =
|
||||
new ThreadLocal<SoftReference<byte[]>>();
|
||||
|
||||
/**
|
||||
* For testing purposes only. Clears the cached buffer to force a new allocation on the next
|
||||
* invocation.
|
||||
*/
|
||||
static void clearCachedBuffer() {
|
||||
BUFFER.set(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the remaining content of the buffer to the given stream. The buffer {@code position}
|
||||
* will remain unchanged by this method.
|
||||
*/
|
||||
static void write(ByteBuffer buffer, OutputStream output) throws IOException {
|
||||
final int initialPos = buffer.position();
|
||||
try {
|
||||
if (buffer.hasArray()) {
|
||||
// Optimized write for array-backed buffers.
|
||||
// Note that we're taking the risk that a malicious OutputStream could modify the array.
|
||||
output.write(buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
|
||||
} else if (output instanceof FileOutputStream) {
|
||||
// Use a channel to write out the ByteBuffer. This will automatically empty the buffer.
|
||||
((FileOutputStream) output).getChannel().write(buffer);
|
||||
} else {
|
||||
// Read all of the data from the buffer to an array.
|
||||
// TODO(nathanmittler): Consider performance improvements for other "known" stream types.
|
||||
final byte[] array = getOrCreateBuffer(buffer.remaining());
|
||||
while (buffer.hasRemaining()) {
|
||||
int length = min(buffer.remaining(), array.length);
|
||||
buffer.get(array, 0, length);
|
||||
output.write(array, 0, length);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Restore the initial position.
|
||||
buffer.position(initialPos);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] getOrCreateBuffer(int requestedSize) {
|
||||
requestedSize = max(requestedSize, MIN_CACHED_BUFFER_SIZE);
|
||||
|
||||
byte[] buffer = getBuffer();
|
||||
// Only allocate if we need to.
|
||||
if (buffer == null || needToReallocate(requestedSize, buffer.length)) {
|
||||
buffer = new byte[requestedSize];
|
||||
|
||||
// Only cache the buffer if it's not too big.
|
||||
if (requestedSize <= MAX_CACHED_BUFFER_SIZE) {
|
||||
setBuffer(buffer);
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private static boolean needToReallocate(int requestedSize, int bufferLength) {
|
||||
// First check against just the requested length to avoid the multiply.
|
||||
return bufferLength < requestedSize
|
||||
&& bufferLength < requestedSize * BUFFER_REALLOCATION_THRESHOLD;
|
||||
}
|
||||
|
||||
private static byte[] getBuffer() {
|
||||
SoftReference<byte[]> sr = BUFFER.get();
|
||||
return sr == null ? null : sr.get();
|
||||
}
|
||||
|
||||
private static void setBuffer(byte[] value) {
|
||||
BUFFER.set(new SoftReference<byte[]>(value));
|
||||
}
|
||||
}
|
116
java/core/src/main/java/com/google/protobuf/ByteOutput.java
Normal file
116
java/core/src/main/java/com/google/protobuf/ByteOutput.java
Normal file
@ -0,0 +1,116 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* An output target for raw bytes. This interface provides semantics that support two types of
|
||||
* writing:
|
||||
*
|
||||
* <p/><b>Traditional write operations:</b>
|
||||
* (as defined by {@link java.io.OutputStream}) where the target method is responsible for either
|
||||
* copying the data or completing the write before returning from the method call.
|
||||
*
|
||||
* <p/><b>Lazy write operations:</b> where the caller guarantees that it will never modify the
|
||||
* provided buffer and it can therefore be considered immutable. The target method is free to
|
||||
* maintain a reference to the buffer beyond the scope of the method call (e.g. until the write
|
||||
* operation completes).
|
||||
*/
|
||||
@ExperimentalApi
|
||||
public abstract class ByteOutput {
|
||||
/**
|
||||
* Writes a single byte.
|
||||
*
|
||||
* @param value the byte to be written
|
||||
* @throws IOException thrown if an error occurred while writing
|
||||
*/
|
||||
public abstract void write(byte value) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will
|
||||
* not be processed prior to the return of this method call, since {@code value} may be
|
||||
* reused/altered by the caller.
|
||||
*
|
||||
* <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
|
||||
* programming error and will lead to data corruption which will be difficult to debug.
|
||||
*
|
||||
* @param value the bytes to be written
|
||||
* @param offset the offset of the start of the writable range
|
||||
* @param length the number of bytes to write starting from {@code offset}
|
||||
* @throws IOException thrown if an error occurred while writing
|
||||
*/
|
||||
public abstract void write(byte[] value, int offset, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value
|
||||
* beyond the scope of this method call (e.g. write later) since it is considered immutable and is
|
||||
* guaranteed not to change by the caller.
|
||||
*
|
||||
* <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
|
||||
* programming error and will lead to data corruption which will be difficult to debug.
|
||||
*
|
||||
* @param value the bytes to be written
|
||||
* @param offset the offset of the start of the writable range
|
||||
* @param length the number of bytes to write starting from {@code offset}
|
||||
* @throws IOException thrown if an error occurred while writing
|
||||
*/
|
||||
public abstract void writeLazy(byte[] value, int offset, int length) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes a sequence of bytes. The {@link ByteOutput} must copy {@code value} if it will
|
||||
* not be processed prior to the return of this method call, since {@code value} may be
|
||||
* reused/altered by the caller.
|
||||
*
|
||||
* <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
|
||||
* programming error and will lead to data corruption which will be difficult to debug.
|
||||
*
|
||||
* @param value the bytes to be written. Upon returning from this call, the {@code position} of
|
||||
* this buffer will be set to the {@code limit}
|
||||
* @throws IOException thrown if an error occurred while writing
|
||||
*/
|
||||
public abstract void write(ByteBuffer value) throws IOException;
|
||||
|
||||
/**
|
||||
* Writes a sequence of bytes. The {@link ByteOutput} is free to retain a reference to the value
|
||||
* beyond the scope of this method call (e.g. write later) since it is considered immutable and is
|
||||
* guaranteed not to change by the caller.
|
||||
*
|
||||
* <p>NOTE: This method <strong>MUST NOT</strong> modify the {@code value}. Doing so is a
|
||||
* programming error and will lead to data corruption which will be difficult to debug.
|
||||
*
|
||||
* @param value the bytes to be written. Upon returning from this call, the {@code position} of
|
||||
* this buffer will be set to the {@code limit}
|
||||
* @throws IOException thrown if an error occurred while writing
|
||||
*/
|
||||
public abstract void writeLazy(ByteBuffer value) throws IOException;
|
||||
}
|
@ -1,4 +1,32 @@
|
||||
// Copyright 2007 Google Inc. All rights reserved.
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
@ -15,6 +43,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.UnsupportedCharsetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
@ -58,6 +87,54 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
* Empty {@code ByteString}.
|
||||
*/
|
||||
public static final ByteString EMPTY = new LiteralByteString(Internal.EMPTY_BYTE_ARRAY);
|
||||
|
||||
/**
|
||||
* An interface to efficiently copy {@code byte[]}.
|
||||
*
|
||||
* <p>One of the noticable costs of copying a byte[] into a new array using
|
||||
* {@code System.arraycopy} is nullification of a new buffer before the copy. It has been shown
|
||||
* the Hotspot VM is capable to intrisicfy {@code Arrays.copyOfRange} operation to avoid this
|
||||
* expensive nullification and provide substantial performance gain. Unfortunately this does not
|
||||
* hold on Android runtimes and could make the copy slightly slower due to additional code in
|
||||
* the {@code Arrays.copyOfRange}. Thus we provide two different implementation for array copier
|
||||
* for Hotspot and Android runtimes.
|
||||
*/
|
||||
private interface ByteArrayCopier {
|
||||
/**
|
||||
* Copies the specified range of the specified array into a new array
|
||||
*/
|
||||
byte[] copyFrom(byte[] bytes, int offset, int size);
|
||||
}
|
||||
|
||||
/** Implementation of {@code ByteArrayCopier} which uses {@link System#arraycopy}. */
|
||||
private static final class SystemByteArrayCopier implements ByteArrayCopier {
|
||||
@Override
|
||||
public byte[] copyFrom(byte[] bytes, int offset, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(bytes, offset, copy, 0, size);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
/** Implementation of {@code ByteArrayCopier} which uses {@link Arrays#copyOfRange}. */
|
||||
private static final class ArraysByteArrayCopier implements ByteArrayCopier {
|
||||
@Override
|
||||
public byte[] copyFrom(byte[] bytes, int offset, int size) {
|
||||
return Arrays.copyOfRange(bytes, offset, offset + size);
|
||||
}
|
||||
}
|
||||
|
||||
private static final ByteArrayCopier byteArrayCopier;
|
||||
static {
|
||||
boolean isAndroid = true;
|
||||
try {
|
||||
Class.forName("android.content.Context");
|
||||
} catch (ClassNotFoundException e) {
|
||||
isAndroid = false;
|
||||
}
|
||||
|
||||
byteArrayCopier = isAndroid ? new SystemByteArrayCopier() : new ArraysByteArrayCopier();
|
||||
}
|
||||
|
||||
/**
|
||||
* Cached hash value. Intentionally accessed via a data race, which
|
||||
@ -77,7 +154,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
*
|
||||
* @param index index of byte
|
||||
* @return the value
|
||||
* @throws ArrayIndexOutOfBoundsException {@code index < 0 or index >= size}
|
||||
* @throws IndexOutOfBoundsException {@code index < 0 or index >= size}
|
||||
*/
|
||||
public abstract byte byteAt(int index);
|
||||
|
||||
@ -109,7 +186,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
public byte nextByte() {
|
||||
try {
|
||||
return byteAt(position++);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
throw new NoSuchElementException(e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -220,9 +297,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
* @return new {@code ByteString}
|
||||
*/
|
||||
public static ByteString copyFrom(byte[] bytes, int offset, int size) {
|
||||
byte[] copy = new byte[size];
|
||||
System.arraycopy(bytes, offset, copy, 0, size);
|
||||
return new LiteralByteString(copy);
|
||||
return new LiteralByteString(byteArrayCopier.copyFrom(bytes, offset, size));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -559,12 +634,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the complete contents of this byte string to
|
||||
* the specified output stream argument.
|
||||
*
|
||||
* <p>It is assumed that the {@link OutputStream} will not modify the contents passed it
|
||||
* it. It may be possible for a malicious {@link OutputStream} to corrupt
|
||||
* the data underlying the {@link ByteString}.
|
||||
* Writes a copy of the contents of this byte string to the specified output stream argument.
|
||||
*
|
||||
* @param out the output stream to which to write the data.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
@ -578,8 +648,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
* @param sourceOffset offset within these bytes
|
||||
* @param numberToWrite number of bytes to write
|
||||
* @throws IOException if an I/O error occurs.
|
||||
* @throws IndexOutOfBoundsException if an offset or size is negative or too
|
||||
* large
|
||||
* @throws IndexOutOfBoundsException if an offset or size is negative or too large
|
||||
*/
|
||||
final void writeTo(OutputStream out, int sourceOffset, int numberToWrite)
|
||||
throws IOException {
|
||||
@ -596,6 +665,20 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
abstract void writeToInternal(OutputStream out, int sourceOffset, int numberToWrite)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Writes this {@link ByteString} to the provided {@link ByteOutput}. Calling
|
||||
* this method may result in multiple operations on the target {@link ByteOutput}.
|
||||
*
|
||||
* <p>This method may expose internal backing buffers of the {@link ByteString} to the {@link
|
||||
* ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious
|
||||
* {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution!
|
||||
*
|
||||
* @param byteOutput the output target to receive the bytes
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @see UnsafeByteOperations#unsafeWriteTo(ByteString, ByteOutput)
|
||||
*/
|
||||
abstract void writeTo(ByteOutput byteOutput) throws IOException;
|
||||
|
||||
/**
|
||||
* Constructs a read-only {@code java.nio.ByteBuffer} whose content
|
||||
* is equal to the contents of this byte string.
|
||||
@ -1102,7 +1185,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
*
|
||||
* @param index the index position to be tested
|
||||
* @param size the length of the array
|
||||
* @throws ArrayIndexOutOfBoundsException if the index does not fall within the array.
|
||||
* @throws IndexOutOfBoundsException if the index does not fall within the array.
|
||||
*/
|
||||
static void checkIndex(int index, int size) {
|
||||
if ((index | (size - (index + 1))) < 0) {
|
||||
@ -1120,7 +1203,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
* @param endIndex the end index of the range (exclusive)
|
||||
* @param size the size of the array.
|
||||
* @return the length of the range.
|
||||
* @throws ArrayIndexOutOfBoundsException some or all of the range falls outside of the array.
|
||||
* @throws IndexOutOfBoundsException some or all of the range falls outside of the array.
|
||||
*/
|
||||
static int checkRange(int startIndex, int endIndex, int size) {
|
||||
final int length = endIndex - startIndex;
|
||||
@ -1235,6 +1318,11 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
|
||||
outputStream.write(bytes, getOffsetIntoBytes() + sourceOffset, numberToWrite);
|
||||
}
|
||||
|
||||
@Override
|
||||
final void writeTo(ByteOutput output) throws IOException {
|
||||
output.writeLazy(bytes, getOffsetIntoBytes(), size());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final String toStringInternal(Charset charset) {
|
||||
return new String(bytes, getOffsetIntoBytes(), size(), charset);
|
||||
|
@ -55,7 +55,14 @@ public final class CodedInputStream {
|
||||
* Create a new CodedInputStream wrapping the given InputStream.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final InputStream input) {
|
||||
return new CodedInputStream(input);
|
||||
return new CodedInputStream(input, BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given InputStream.
|
||||
*/
|
||||
static CodedInputStream newInstance(final InputStream input, int bufferSize) {
|
||||
return new CodedInputStream(input, bufferSize);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,14 +77,14 @@ public final class CodedInputStream {
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int len) {
|
||||
return newInstance(buf, off, len, false);
|
||||
return newInstance(buf, off, len, false /* bufferIsImmutable */);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new CodedInputStream wrapping the given byte array slice.
|
||||
*/
|
||||
public static CodedInputStream newInstance(final byte[] buf, final int off,
|
||||
final int len, boolean bufferIsImmutable) {
|
||||
static CodedInputStream newInstance(
|
||||
final byte[] buf, final int off, final int len, final boolean bufferIsImmutable) {
|
||||
CodedInputStream result = new CodedInputStream(buf, off, len, bufferIsImmutable);
|
||||
try {
|
||||
// Some uses of CodedInputStream can be more efficient if they know
|
||||
@ -361,6 +368,11 @@ public final class CodedInputStream {
|
||||
return result;
|
||||
} else if (size == 0) {
|
||||
return "";
|
||||
} else if (size <= bufferSize) {
|
||||
refillBuffer(size);
|
||||
String result = new String(buffer, bufferPos, size, Internal.UTF_8);
|
||||
bufferPos += size;
|
||||
return result;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
return new String(readRawBytesSlowPath(size), Internal.UTF_8);
|
||||
@ -375,14 +387,21 @@ public final class CodedInputStream {
|
||||
public String readStringRequireUtf8() throws IOException {
|
||||
final int size = readRawVarint32();
|
||||
final byte[] bytes;
|
||||
int pos = bufferPos;
|
||||
if (size <= (bufferSize - pos) && size > 0) {
|
||||
final int oldPos = bufferPos;
|
||||
final int pos;
|
||||
if (size <= (bufferSize - oldPos) && size > 0) {
|
||||
// Fast path: We already have the bytes in a contiguous buffer, so
|
||||
// just copy directly from it.
|
||||
bytes = buffer;
|
||||
bufferPos = pos + size;
|
||||
bufferPos = oldPos + size;
|
||||
pos = oldPos;
|
||||
} else if (size == 0) {
|
||||
return "";
|
||||
} else if (size <= bufferSize) {
|
||||
refillBuffer(size);
|
||||
bytes = buffer;
|
||||
pos = 0;
|
||||
bufferPos = pos + size;
|
||||
} else {
|
||||
// Slow path: Build a byte array first then copy it.
|
||||
bytes = readRawBytesSlowPath(size);
|
||||
@ -869,7 +888,8 @@ public final class CodedInputStream {
|
||||
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
|
||||
private static final int BUFFER_SIZE = 4096;
|
||||
|
||||
private CodedInputStream(final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) {
|
||||
private CodedInputStream(
|
||||
final byte[] buffer, final int off, final int len, boolean bufferIsImmutable) {
|
||||
this.buffer = buffer;
|
||||
bufferSize = off + len;
|
||||
bufferPos = off;
|
||||
@ -878,8 +898,8 @@ public final class CodedInputStream {
|
||||
this.bufferIsImmutable = bufferIsImmutable;
|
||||
}
|
||||
|
||||
private CodedInputStream(final InputStream input) {
|
||||
buffer = new byte[BUFFER_SIZE];
|
||||
private CodedInputStream(final InputStream input, int bufferSize) {
|
||||
buffer = new byte[bufferSize];
|
||||
bufferSize = 0;
|
||||
bufferPos = 0;
|
||||
totalBytesRetired = 0;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -272,7 +272,7 @@ public final class Descriptors {
|
||||
* because a field has an undefined type or because two messages
|
||||
* were defined with the same name.
|
||||
*/
|
||||
private static FileDescriptor buildFrom(
|
||||
public static FileDescriptor buildFrom(
|
||||
final FileDescriptorProto proto, final FileDescriptor[] dependencies,
|
||||
final boolean allowUnknownDependencies)
|
||||
throws DescriptorValidationException {
|
||||
@ -1123,7 +1123,7 @@ public final class Descriptors {
|
||||
private JavaType javaType;
|
||||
|
||||
public FieldDescriptorProto.Type toProto() {
|
||||
return FieldDescriptorProto.Type.valueOf(ordinal() + 1);
|
||||
return FieldDescriptorProto.Type.forNumber(ordinal() + 1);
|
||||
}
|
||||
public JavaType getJavaType() { return javaType; }
|
||||
|
||||
|
@ -1,3 +1,33 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
|
@ -1019,7 +1019,9 @@ public abstract class GeneratedMessage extends AbstractMessage
|
||||
verifyContainingType(field);
|
||||
final Object value = extensions.getField(field);
|
||||
if (value == null) {
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (field.isRepeated()) {
|
||||
return Collections.emptyList();
|
||||
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
// Lacking an ExtensionRegistry, we have no way to determine the
|
||||
// extension's real type, so we return a DynamicMessage.
|
||||
return DynamicMessage.getDefaultInstance(field.getMessageType());
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
|
||||
import com.google.protobuf.Internal.BooleanList;
|
||||
import com.google.protobuf.Internal.DoubleList;
|
||||
import com.google.protobuf.Internal.FloatList;
|
||||
@ -39,6 +40,7 @@ import com.google.protobuf.Internal.ProtobufList;
|
||||
import com.google.protobuf.WireFormat.FieldType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectStreamException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
@ -57,10 +59,7 @@ import java.util.Map;
|
||||
public abstract class GeneratedMessageLite<
|
||||
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
|
||||
BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
|
||||
extends AbstractMessageLite
|
||||
implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
extends AbstractMessageLite {
|
||||
|
||||
/** For use by generated code only. Lazily initialized to reduce allocations. */
|
||||
protected UnknownFieldSetLite unknownFields = null;
|
||||
@ -83,6 +82,24 @@ public abstract class GeneratedMessageLite<
|
||||
return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* A reflective toString function. This is primarily intended as a developer aid, while keeping
|
||||
* binary size down. The first line of the {@code toString()} representation includes a commented
|
||||
* version of {@code super.toString()} to act as an indicator that this should not be relied on
|
||||
* for comparisons.
|
||||
* <p>
|
||||
* NOTE: This method relies on the field getter methods not being stripped or renamed by proguard.
|
||||
* If they are, the fields will not be included in the returned string representation.
|
||||
* <p>
|
||||
* NOTE: This implementation is liable to change in the future, and should not be relied on in
|
||||
* code.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return MessageLiteToString.toString(this, super.toString());
|
||||
}
|
||||
|
||||
|
||||
// The general strategy for unknown fields is to use an UnknownFieldSetLite that is treated as
|
||||
// mutable during the parsing constructor and immutable after. This allows us to avoid
|
||||
// any unnecessary intermediary allocations while reducing the generated code size.
|
||||
@ -303,10 +320,9 @@ public abstract class GeneratedMessageLite<
|
||||
throws java.io.IOException {
|
||||
MessageType parsedMessage = null;
|
||||
try {
|
||||
parsedMessage =
|
||||
(MessageType) getDefaultInstanceForType().getParserForType().parsePartialFrom(
|
||||
input, extensionRegistry);
|
||||
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
|
||||
parsedMessage = parsePartialFrom(
|
||||
(MessageType) getDefaultInstanceForType(), input, extensionRegistry);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
parsedMessage = (MessageType) e.getUnfinishedMessage();
|
||||
throw e;
|
||||
} finally {
|
||||
@ -562,7 +578,6 @@ public abstract class GeneratedMessageLite<
|
||||
return extensions.isInitialized();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected final void doneParsing() {
|
||||
super.doneParsing();
|
||||
@ -1049,7 +1064,12 @@ public abstract class GeneratedMessageLite<
|
||||
* A serialized (serializable) form of the generated message. Stores the
|
||||
* message as a class name and a byte array.
|
||||
*/
|
||||
static final class SerializedForm implements Serializable {
|
||||
protected static final class SerializedForm implements Serializable {
|
||||
|
||||
public static SerializedForm of(MessageLite message) {
|
||||
return new SerializedForm(message);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
|
||||
private final String messageClassName;
|
||||
@ -1093,16 +1113,6 @@ public abstract class GeneratedMessageLite<
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces this object in the output stream with a serialized form.
|
||||
* Part of Java's serialization magic. Generated sub-classes must override
|
||||
* this method by calling {@code return super.writeReplace();}
|
||||
* @return a SerializedForm of this message
|
||||
*/
|
||||
protected Object writeReplace() throws ObjectStreamException {
|
||||
return new SerializedForm(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the {@link Extension} is Lite and returns it as a
|
||||
@ -1135,45 +1145,6 @@ public abstract class GeneratedMessageLite<
|
||||
message.dynamicMethod(MethodToInvoke.MAKE_IMMUTABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* A static helper method for parsing a partial from input using the extension registry and the
|
||||
* instance.
|
||||
*/
|
||||
static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
|
||||
T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
try {
|
||||
return (T) instance.dynamicMethod(
|
||||
MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry);
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getCause() instanceof InvalidProtocolBufferException) {
|
||||
throw (InvalidProtocolBufferException) e.getCause();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Parser} implementation that delegates to the default instance.
|
||||
* <p>
|
||||
* For use by generated code only.
|
||||
*/
|
||||
protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
|
||||
extends AbstractParser<T> {
|
||||
|
||||
private T defaultInstance;
|
||||
|
||||
public DefaultInstanceBasedParser(T defaultInstance) {
|
||||
this.defaultInstance = defaultInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
protected static IntList newIntList() {
|
||||
return new IntArrayList();
|
||||
}
|
||||
@ -1269,8 +1240,218 @@ public abstract class GeneratedMessageLite<
|
||||
protected static <E> ProtobufList<E> emptyProtobufList() {
|
||||
return ProtobufArrayList.emptyList();
|
||||
}
|
||||
|
||||
|
||||
protected static LazyStringArrayList emptyLazyStringArrayList() {
|
||||
return LazyStringArrayList.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Parser} implementation that delegates to the default instance.
|
||||
* <p>
|
||||
* For use by generated code only.
|
||||
*/
|
||||
protected static class DefaultInstanceBasedParser<T extends GeneratedMessageLite<T, ?>>
|
||||
extends AbstractParser<T> {
|
||||
|
||||
private T defaultInstance;
|
||||
|
||||
public DefaultInstanceBasedParser(T defaultInstance) {
|
||||
this.defaultInstance = defaultInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parsePartialFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return GeneratedMessageLite.parsePartialFrom(defaultInstance, input, extensionRegistry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A static helper method for parsing a partial from input using the extension registry and the
|
||||
* instance.
|
||||
*/
|
||||
// TODO(dweis): Should this verify that the last tag was 0?
|
||||
static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
|
||||
T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
T result;
|
||||
try {
|
||||
result = (T) instance.dynamicMethod(
|
||||
MethodToInvoke.PARSE_PARTIAL_FROM, input, extensionRegistry);
|
||||
} catch (RuntimeException e) {
|
||||
if (e.getCause() instanceof InvalidProtocolBufferException) {
|
||||
throw (InvalidProtocolBufferException) e.getCause();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
|
||||
T defaultInstance,
|
||||
CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parsePartialFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to check if message is initialized.
|
||||
*
|
||||
* @throws InvalidProtocolBufferException if it is not initialized.
|
||||
* @return The message to check.
|
||||
*/
|
||||
private static <T extends GeneratedMessageLite<T, ?>> T checkMessageInitialized(T message)
|
||||
throws InvalidProtocolBufferException {
|
||||
if (message != null && !message.isInitialized()) {
|
||||
throw message.newUninitializedMessageException()
|
||||
.asInvalidProtocolBufferException()
|
||||
.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, ByteString data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parseFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry()));
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
|
||||
}
|
||||
|
||||
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
|
||||
// ByteString.
|
||||
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
|
||||
T defaultInstance, ByteString data, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
T message;
|
||||
try {
|
||||
CodedInputStream input = data.newCodedInput();
|
||||
message = parsePartialFrom(defaultInstance, input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// This is a special case since we want to verify that the last tag is 0. We assume we exhaust the
|
||||
// ByteString.
|
||||
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialFrom(
|
||||
T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
T message;
|
||||
try {
|
||||
CodedInputStream input = CodedInputStream.newInstance(data);
|
||||
message = parsePartialFrom(defaultInstance, input, extensionRegistry);
|
||||
try {
|
||||
input.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, byte[] data)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(defaultInstance, data, ExtensionRegistryLite.getEmptyRegistry()));
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, byte[] data, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(parsePartialFrom(defaultInstance, data, extensionRegistry));
|
||||
}
|
||||
|
||||
// Does not validate last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input),
|
||||
ExtensionRegistryLite.getEmptyRegistry()));
|
||||
}
|
||||
|
||||
// Does not validate last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(defaultInstance, CodedInputStream.newInstance(input), extensionRegistry));
|
||||
}
|
||||
|
||||
// Does not validate last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, CodedInputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return parseFrom(defaultInstance, input, ExtensionRegistryLite.getEmptyRegistry());
|
||||
}
|
||||
|
||||
// Does not validate last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseFrom(
|
||||
T defaultInstance, CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialFrom(defaultInstance, input, extensionRegistry));
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseDelimitedFrom(
|
||||
T defaultInstance, InputStream input)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialDelimitedFrom(defaultInstance, input,
|
||||
ExtensionRegistryLite.getEmptyRegistry()));
|
||||
}
|
||||
|
||||
// Validates last tag.
|
||||
protected static <T extends GeneratedMessageLite<T, ?>> T parseDelimitedFrom(
|
||||
T defaultInstance, InputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
return checkMessageInitialized(
|
||||
parsePartialDelimitedFrom(defaultInstance, input, extensionRegistry));
|
||||
}
|
||||
|
||||
private static <T extends GeneratedMessageLite<T, ?>> T parsePartialDelimitedFrom(
|
||||
T defaultInstance,
|
||||
InputStream input,
|
||||
ExtensionRegistryLite extensionRegistry)
|
||||
throws InvalidProtocolBufferException {
|
||||
int size;
|
||||
try {
|
||||
int firstByte = input.read();
|
||||
if (firstByte == -1) {
|
||||
return null;
|
||||
}
|
||||
size = CodedInputStream.readRawVarint32(firstByte, input);
|
||||
} catch (IOException e) {
|
||||
throw new InvalidProtocolBufferException(e.getMessage());
|
||||
}
|
||||
InputStream limitedInput = new LimitedInputStream(input, size);
|
||||
CodedInputStream codedInput = CodedInputStream.newInstance(limitedInput);
|
||||
T message = parsePartialFrom(defaultInstance, codedInput, extensionRegistry);
|
||||
try {
|
||||
codedInput.checkLastTagWas(0);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw e.setUnfinishedMessage(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
@ -51,10 +51,12 @@ import java.util.Set;
|
||||
*
|
||||
* @author kenton@google.com (Kenton Varda)
|
||||
*/
|
||||
public class Internal {
|
||||
public final class Internal {
|
||||
|
||||
protected static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
protected static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
private Internal() {}
|
||||
|
||||
static final Charset UTF_8 = Charset.forName("UTF-8");
|
||||
static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||
|
||||
/**
|
||||
* Helper called by generated code to construct default values for string
|
||||
@ -406,6 +408,7 @@ public class Internal {
|
||||
public static final CodedInputStream EMPTY_CODED_INPUT_STREAM =
|
||||
CodedInputStream.newInstance(EMPTY_BYTE_ARRAY);
|
||||
|
||||
|
||||
/**
|
||||
* Provides an immutable view of {@code List<T>} around a {@code List<F>}.
|
||||
*
|
||||
|
@ -39,14 +39,14 @@ import java.util.Map.Entry;
|
||||
*
|
||||
* Most of key methods are implemented in {@link LazyFieldLite} but this class
|
||||
* can contain default instance of the message to provide {@code hashCode()},
|
||||
* {@code equals()} and {@code toString()}.
|
||||
* {@code euqals()} and {@code toString()}.
|
||||
*
|
||||
* @author xiangl@google.com (Xiang Li)
|
||||
*/
|
||||
public class LazyField extends LazyFieldLite {
|
||||
|
||||
/**
|
||||
* Carry a message's default instance which is used by {@code hashCode()}, {@code equals()} and
|
||||
* Carry a message's default instance which is used by {@code hashCode()}, {@code euqals()} and
|
||||
* {@code toString()}.
|
||||
*/
|
||||
private final MessageLite defaultInstance;
|
||||
|
@ -30,14 +30,26 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* LazyFieldLite encapsulates the logic of lazily parsing message fields. It stores
|
||||
* the message in a ByteString initially and then parse it on-demand.
|
||||
* the message in a ByteString initially and then parses it on-demand.
|
||||
*
|
||||
* LazyField is thread-compatible e.g. concurrent read are safe, however,
|
||||
* synchronizations are needed under read/write situations.
|
||||
* LazyFieldLite is thread-compatible: concurrent reads are safe once the proto that this
|
||||
* LazyFieldLite is a part of is no longer being mutated by its Builder. However, explicit
|
||||
* synchronization is needed under read/write situations.
|
||||
*
|
||||
* This class is internal implementation detail, so you don't need to use it directly.
|
||||
* When a LazyFieldLite is used in the context of a MessageLite object, its behavior is considered
|
||||
* to be immutable and none of the setter methods in its API are expected to be invoked. All of the
|
||||
* getters are expected to be thread-safe. When used in the context of a MessageLite.Builder,
|
||||
* setters can be invoked, but there is no guarantee of thread safety.
|
||||
*
|
||||
* TODO(yatin,dweis): Consider splitting this class's functionality and put the mutable methods
|
||||
* into a separate builder class to allow us to give stronger compile-time guarantees.
|
||||
*
|
||||
* This class is internal implementation detail of the protobuf library, so you don't need to use it
|
||||
* directly.
|
||||
*
|
||||
* @author xiangl@google.com (Xiang Li)
|
||||
*/
|
||||
@ -46,8 +58,34 @@ public class LazyFieldLite {
|
||||
ExtensionRegistryLite.getEmptyRegistry();
|
||||
|
||||
/**
|
||||
* A delayed-parsed version of the bytes. When this is non-null then {@code extensionRegistry } is
|
||||
* also non-null and {@code value} and {@code memoizedBytes} are null.
|
||||
* The value associated with the LazyFieldLite object is stored in one or more of the following
|
||||
* three fields (delayedBytes, value, memoizedBytes). They should together be interpreted as
|
||||
* follows.
|
||||
* 1) delayedBytes can be non-null, while value and memoizedBytes is null. The object will be in
|
||||
* this state while the value for the object has not yet been parsed.
|
||||
* 2) Both delayedBytes and value are non-null. The object transitions to this state as soon as
|
||||
* some caller needs to access the value (by invoking getValue()).
|
||||
* 3) memoizedBytes is merely an optimization for calls to LazyFieldLite.toByteString() to avoid
|
||||
* recomputing the ByteString representation on each call. Instead, when the value is parsed
|
||||
* from delayedBytes, we will also assign the contents of delayedBytes to memoizedBytes (since
|
||||
* that is the ByteString representation of value).
|
||||
* 4) Finally, if the LazyFieldLite was created directly with a parsed MessageLite value, then
|
||||
* delayedBytes will be null, and memoizedBytes will be initialized only upon the first call to
|
||||
* LazyFieldLite.toByteString().
|
||||
*
|
||||
* Given the above conditions, any caller that needs a serialized representation of this object
|
||||
* must first check if the memoizedBytes or delayedBytes ByteString is non-null and use it
|
||||
* directly; if both of those are null, it can look at the parsed value field. Similarly, any
|
||||
* caller that needs a parsed value must first check if the value field is already non-null, if
|
||||
* not it must parse the value from delayedBytes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A delayed-parsed version of the contents of this field. When this field is non-null, then the
|
||||
* "value" field is allowed to be null until the time that the value needs to be read.
|
||||
*
|
||||
* When delayedBytes is non-null then {@code extensionRegistry} is required to also be non-null.
|
||||
* {@code value} and {@code memoizedBytes} will be initialized lazily.
|
||||
*/
|
||||
private ByteString delayedBytes;
|
||||
|
||||
@ -60,12 +98,15 @@ public class LazyFieldLite {
|
||||
private ExtensionRegistryLite extensionRegistry;
|
||||
|
||||
/**
|
||||
* The parsed value. When this is non-null then {@code delayedBytes} will be null.
|
||||
* The parsed value. When this is null and a caller needs access to the MessageLite value, then
|
||||
* {@code delayedBytes} will be parsed lazily at that time.
|
||||
*/
|
||||
protected volatile MessageLite value;
|
||||
|
||||
/**
|
||||
* The memoized bytes for {@code value}. Will be null when {@code value} is null.
|
||||
* The memoized bytes for {@code value}. This is an optimization for the toByteString() method to
|
||||
* not have to recompute its return-value on each invocation.
|
||||
* TODO(yatin): Figure out whether this optimization is actually necessary.
|
||||
*/
|
||||
private volatile ByteString memoizedBytes;
|
||||
|
||||
@ -230,6 +271,46 @@ public class LazyFieldLite {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges another instance's contents from a stream.
|
||||
*
|
||||
* <p>LazyField is not thread-safe for write access. Synchronizations are needed
|
||||
* under read/write situations.
|
||||
*/
|
||||
public void mergeFrom(CodedInputStream input, ExtensionRegistryLite extensionRegistry)
|
||||
throws IOException {
|
||||
if (this.containsDefaultInstance()) {
|
||||
setByteString(input.readBytes(), extensionRegistry);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the other field has an extension registry but this does not, copy over the other extension
|
||||
// registry.
|
||||
if (this.extensionRegistry == null) {
|
||||
this.extensionRegistry = extensionRegistry;
|
||||
}
|
||||
|
||||
// In the case that both of them are not parsed we simply concatenate the bytes to save time. In
|
||||
// the (probably rare) case that they have different extension registries there is a chance that
|
||||
// some of the extensions may be dropped, but the tradeoff of making this operation fast seems
|
||||
// to outway the benefits of combining the extension registries, which is not normally done for
|
||||
// lite protos anyways.
|
||||
if (this.delayedBytes != null) {
|
||||
setByteString(this.delayedBytes.concat(input.readBytes()), this.extensionRegistry);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are parsed and both contain data. We won't drop any extensions here directly, but in the
|
||||
// case that the extension registries are not the same then we might in the future if we
|
||||
// need to serialize and parse a message again.
|
||||
try {
|
||||
setValue(value.toBuilder().mergeFrom(input, extensionRegistry).build());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Nothing is logged and no exceptions are thrown. Clients will be unaware that a proto
|
||||
// was invalid.
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageLite mergeValueAndBytes(
|
||||
MessageLite value, ByteString otherBytes, ExtensionRegistryLite extensionRegistry) {
|
||||
@ -259,10 +340,10 @@ public class LazyFieldLite {
|
||||
* parsed. Be careful when using this method.
|
||||
*/
|
||||
public int getSerializedSize() {
|
||||
if (delayedBytes != null) {
|
||||
return delayedBytes.size();
|
||||
} else if (memoizedBytes != null) {
|
||||
if (memoizedBytes != null) {
|
||||
return memoizedBytes.size();
|
||||
} else if (delayedBytes != null) {
|
||||
return delayedBytes.size();
|
||||
} else if (value != null) {
|
||||
return value.getSerializedSize();
|
||||
} else {
|
||||
@ -274,12 +355,12 @@ public class LazyFieldLite {
|
||||
* Returns a BytesString for this field in a thread-safe way.
|
||||
*/
|
||||
public ByteString toByteString() {
|
||||
if (delayedBytes != null) {
|
||||
return delayedBytes;
|
||||
}
|
||||
if (memoizedBytes != null) {
|
||||
return memoizedBytes;
|
||||
}
|
||||
if (delayedBytes != null) {
|
||||
return delayedBytes;
|
||||
}
|
||||
synchronized (this) {
|
||||
if (memoizedBytes != null) {
|
||||
return memoizedBytes;
|
||||
@ -311,18 +392,15 @@ public class LazyFieldLite {
|
||||
.parseFrom(delayedBytes, extensionRegistry);
|
||||
this.value = parsedValue;
|
||||
this.memoizedBytes = delayedBytes;
|
||||
this.delayedBytes = null;
|
||||
} else {
|
||||
this.value = defaultInstance;
|
||||
this.memoizedBytes = ByteString.EMPTY;
|
||||
this.delayedBytes = null;
|
||||
}
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Nothing is logged and no exceptions are thrown. Clients will be unaware that this proto
|
||||
// was invalid.
|
||||
this.value = defaultInstance;
|
||||
this.memoizedBytes = ByteString.EMPTY;
|
||||
this.delayedBytes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,12 +30,12 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.RandomAccess;
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,200 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helps generate {@link String} representations of {@link MessageLite} protos.
|
||||
*/
|
||||
final class MessageLiteToString {
|
||||
/**
|
||||
* Suffix for *_FIELD_NUMBER fields. This is used to reflectively detect proto fields that should
|
||||
* be toString()ed.
|
||||
*/
|
||||
private static final String FIELD_NUMBER_NAME_SUFFIX = "_FIELD_NUMBER";
|
||||
|
||||
/**
|
||||
* Returns a {@link String} representation of the {@link MessageLite} object. The first line of
|
||||
* the {@code String} representation representation includes a comment string to uniquely identify
|
||||
* the objcet instance. This acts as an indicator that this should not be relied on for
|
||||
* comparisons.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
static String toString(MessageLite messageLite, String commentString) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
buffer.append("# ").append(commentString);
|
||||
reflectivePrintWithIndent(messageLite, buffer, 0);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reflectively prints the {@link MessageLite} to the buffer at given {@code indent} level.
|
||||
*
|
||||
* @param buffer the buffer to write to
|
||||
* @param indent the number of spaces to indent the proto by
|
||||
*/
|
||||
private static void reflectivePrintWithIndent(
|
||||
MessageLite messageLite, StringBuilder buffer, int indent) {
|
||||
// Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(), and
|
||||
// getFooList() which might be useful for building an object's string representation.
|
||||
Map<String, Method> nameToNoArgMethod = new HashMap<String, Method>();
|
||||
for (Method method : messageLite.getClass().getDeclaredMethods()) {
|
||||
if (method.getParameterTypes().length == 0) {
|
||||
nameToNoArgMethod.put(method.getName(), method);
|
||||
}
|
||||
}
|
||||
|
||||
for (Field field : messageLite.getClass().getDeclaredFields()) {
|
||||
String fieldName = field.getName();
|
||||
// Skip all fields that aren't in a format like "FOO_BAR_FIELD_NUMBER"
|
||||
if (!fieldName.endsWith(FIELD_NUMBER_NAME_SUFFIX)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For "FOO_BAR_FIELD_NUMBER" his would be "FOO_BAR"
|
||||
String upperUnderscore =
|
||||
fieldName.substring(0, fieldName.length() - FIELD_NUMBER_NAME_SUFFIX.length());
|
||||
|
||||
// For "FOO_BAR_FIELD_NUMBER" his would be "FooBar"
|
||||
String upperCamelCaseName = upperUnderscoreToUpperCamel(upperUnderscore);
|
||||
|
||||
// Try to reflectively get the value and toString() the field as if it were optional. This
|
||||
// only works if the method names have not be proguarded out or renamed.
|
||||
Method getMethod = nameToNoArgMethod.get("get" + upperCamelCaseName);
|
||||
Method hasMethod = nameToNoArgMethod.get("has" + upperCamelCaseName);
|
||||
if (getMethod != null && hasMethod != null) {
|
||||
if ((Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite)) {
|
||||
printField(
|
||||
buffer,
|
||||
indent,
|
||||
upperUnderscore.toLowerCase(),
|
||||
GeneratedMessageLite.invokeOrDie(getMethod, messageLite));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to reflectively get the value and toString() the field as if it were repeated. This
|
||||
// only works if the method names have not be proguarded out or renamed.
|
||||
Method listMethod = nameToNoArgMethod.get("get" + upperCamelCaseName + "List");
|
||||
if (listMethod != null) {
|
||||
printField(
|
||||
buffer,
|
||||
indent,
|
||||
upperUnderscore.toLowerCase(),
|
||||
GeneratedMessageLite.invokeOrDie(listMethod, messageLite));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (messageLite instanceof GeneratedMessageLite.ExtendableMessage) {
|
||||
Iterator<Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object>> iter =
|
||||
((GeneratedMessageLite.ExtendableMessage<?, ?>) messageLite).extensions.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<GeneratedMessageLite.ExtensionDescriptor, Object> entry = iter.next();
|
||||
printField(buffer, indent, "[" + entry.getKey().getNumber() + "]", entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
if (((GeneratedMessageLite) messageLite).unknownFields != null) {
|
||||
((GeneratedMessageLite) messageLite).unknownFields.printWithIndent(buffer, indent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a text proto field.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*
|
||||
* @param buffer the buffer to write to
|
||||
* @param indent the number of spaces the proto should be indented by
|
||||
* @param name the field name (in lower underscore case)
|
||||
* @param object the object value of the field
|
||||
*/
|
||||
static final void printField(StringBuilder buffer, int indent, String name, Object object) {
|
||||
if (object instanceof List<?>) {
|
||||
List<?> list = (List<?>) object;
|
||||
for (Object entry : list) {
|
||||
printField(buffer, indent, name, entry);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.append('\n');
|
||||
for (int i = 0; i < indent; i++) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append(name);
|
||||
|
||||
if (object instanceof String) {
|
||||
buffer.append(": \"").append(TextFormatEscaper.escapeText((String) object)).append('"');
|
||||
} else if (object instanceof ByteString) {
|
||||
buffer.append(": \"").append(TextFormatEscaper.escapeBytes((ByteString) object)).append('"');
|
||||
} else if (object instanceof GeneratedMessageLite) {
|
||||
buffer.append(" {");
|
||||
reflectivePrintWithIndent((GeneratedMessageLite) object, buffer, indent + 2);
|
||||
buffer.append("\n");
|
||||
for (int i = 0; i < indent; i++) {
|
||||
buffer.append(' ');
|
||||
}
|
||||
buffer.append("}");
|
||||
} else {
|
||||
buffer.append(": ").append(object.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A Guava-less implementation of:
|
||||
* {@code CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, upperUnderscore)}
|
||||
*/
|
||||
private static String upperUnderscoreToUpperCamel(String upperUnderscore) {
|
||||
String upperCamelCaseName = "";
|
||||
boolean nextCharacterShouldBeUpper = true;
|
||||
for (int i = 0; i < upperUnderscore.length(); i++) {
|
||||
char ch = upperUnderscore.charAt(i);
|
||||
if (ch == '_') {
|
||||
nextCharacterShouldBeUpper = true;
|
||||
} else if (nextCharacterShouldBeUpper){
|
||||
upperCamelCaseName += Character.toUpperCase(ch);
|
||||
nextCharacterShouldBeUpper = false;
|
||||
} else {
|
||||
upperCamelCaseName += Character.toLowerCase(ch);
|
||||
}
|
||||
}
|
||||
return upperCamelCaseName;
|
||||
}
|
||||
}
|
@ -30,15 +30,14 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.InvalidMarkException;
|
||||
import java.nio.channels.Channels;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -54,7 +53,7 @@ final class NioByteString extends ByteString.LeafByteString {
|
||||
throw new NullPointerException("buffer");
|
||||
}
|
||||
|
||||
this.buffer = buffer.slice();
|
||||
this.buffer = buffer.slice().order(ByteOrder.nativeOrder());
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
@ -119,7 +118,7 @@ final class NioByteString extends ByteString.LeafByteString {
|
||||
|
||||
@Override
|
||||
public void writeTo(OutputStream out) throws IOException {
|
||||
writeToInternal(out, buffer.position(), buffer.remaining());
|
||||
out.write(toByteArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -137,14 +136,12 @@ final class NioByteString extends ByteString.LeafByteString {
|
||||
return;
|
||||
}
|
||||
|
||||
// Slow path
|
||||
if (out instanceof FileOutputStream || numberToWrite >= 8192) {
|
||||
// Use a channel to write out the ByteBuffer.
|
||||
Channels.newChannel(out).write(slice(sourceOffset, sourceOffset + numberToWrite));
|
||||
} else {
|
||||
// Just copy the data to an array and write it.
|
||||
out.write(toByteArray());
|
||||
}
|
||||
ByteBufferWriter.write(slice(sourceOffset, sourceOffset + numberToWrite), out);
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeTo(ByteOutput output) throws IOException {
|
||||
output.writeLazy(buffer.slice());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,46 +156,30 @@ final class NioByteString extends ByteString.LeafByteString {
|
||||
|
||||
@Override
|
||||
protected String toStringInternal(Charset charset) {
|
||||
byte[] bytes;
|
||||
int offset;
|
||||
final byte[] bytes;
|
||||
final int offset;
|
||||
final int length;
|
||||
if (buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
offset = buffer.arrayOffset() + buffer.position();
|
||||
length = buffer.remaining();
|
||||
} else {
|
||||
// TODO(nathanmittler): Can we optimize this?
|
||||
bytes = toByteArray();
|
||||
offset = 0;
|
||||
length = bytes.length;
|
||||
}
|
||||
return new String(bytes, offset, size(), charset);
|
||||
return new String(bytes, offset, length, charset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidUtf8() {
|
||||
// TODO(nathanmittler): add a ByteBuffer fork for Utf8.isValidUtf8 to avoid the copy
|
||||
byte[] bytes;
|
||||
int startIndex;
|
||||
if (buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
startIndex = buffer.arrayOffset() + buffer.position();
|
||||
} else {
|
||||
bytes = toByteArray();
|
||||
startIndex = 0;
|
||||
}
|
||||
return Utf8.isValidUtf8(bytes, startIndex, startIndex + size());
|
||||
return Utf8.isValidUtf8(buffer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int partialIsValidUtf8(int state, int offset, int length) {
|
||||
// TODO(nathanmittler): TODO add a ByteBuffer fork for Utf8.partialIsValidUtf8 to avoid the copy
|
||||
byte[] bytes;
|
||||
int startIndex;
|
||||
if (buffer.hasArray()) {
|
||||
bytes = buffer.array();
|
||||
startIndex = buffer.arrayOffset() + buffer.position();
|
||||
} else {
|
||||
bytes = toByteArray();
|
||||
startIndex = 0;
|
||||
}
|
||||
return Utf8.partialIsValidUtf8(state, bytes, startIndex, startIndex + size());
|
||||
return Utf8.partialIsValidUtf8(state, buffer, offset, offset + length);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
|
@ -48,10 +48,11 @@ import java.util.Stack;
|
||||
/**
|
||||
* Class to represent {@code ByteStrings} formed by concatenation of other
|
||||
* ByteStrings, without copying the data in the pieces. The concatenation is
|
||||
* represented as a tree whose leaf nodes are each a {@link LiteralByteString}.
|
||||
* represented as a tree whose leaf nodes are each a
|
||||
* {@link com.google.protobuf.ByteString.LeafByteString}.
|
||||
*
|
||||
* <p>Most of the operation here is inspired by the now-famous paper <a
|
||||
* href="http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* href="https://web.archive.org/web/20060202015456/http://www.cs.ubc.ca/local/reading/proceedings/spe91-95/spe/vol25/issue12/spe986.pdf">
|
||||
* BAP95 </a> Ropes: an Alternative to Strings hans-j. boehm, russ atkinson and
|
||||
* michael plass
|
||||
*
|
||||
@ -139,8 +140,9 @@ final class RopeByteString extends ByteString {
|
||||
/**
|
||||
* Concatenate the given strings while performing various optimizations to
|
||||
* slow the growth rate of tree depth and tree node count. The result is
|
||||
* either a {@link LiteralByteString} or a {@link RopeByteString}
|
||||
* depending on which optimizations, if any, were applied.
|
||||
* either a {@link com.google.protobuf.ByteString.LeafByteString} or a
|
||||
* {@link RopeByteString} depending on which optimizations, if any, were
|
||||
* applied.
|
||||
*
|
||||
* <p>Small pieces of length less than {@link
|
||||
* ByteString#CONCATENATE_BY_COPY_SIZE} may be copied by value here, as in
|
||||
@ -294,8 +296,7 @@ final class RopeByteString extends ByteString {
|
||||
*
|
||||
* <p>Substrings of {@code length < 2} should result in at most a single
|
||||
* recursive call chain, terminating at a leaf node. Thus the result will be a
|
||||
* {@link LiteralByteString}. {@link #RopeByteString(ByteString,
|
||||
* ByteString)}.
|
||||
* {@link com.google.protobuf.ByteString.LeafByteString}.
|
||||
*
|
||||
* @param beginIndex start at this index
|
||||
* @param endIndex the last character is the one before this index
|
||||
@ -368,7 +369,7 @@ final class RopeByteString extends ByteString {
|
||||
|
||||
@Override
|
||||
public List<ByteBuffer> asReadOnlyByteBufferList() {
|
||||
// Walk through the list of LiteralByteString's that make up this
|
||||
// Walk through the list of LeafByteString's that make up this
|
||||
// rope, and add each one as a read-only ByteBuffer.
|
||||
List<ByteBuffer> result = new ArrayList<ByteBuffer>();
|
||||
PieceIterator pieces = new PieceIterator(this);
|
||||
@ -399,6 +400,12 @@ final class RopeByteString extends ByteString {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void writeTo(ByteOutput output) throws IOException {
|
||||
left.writeTo(output);
|
||||
right.writeTo(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String toStringInternal(Charset charset) {
|
||||
return new String(toByteArray(), charset);
|
||||
@ -709,9 +716,10 @@ final class RopeByteString extends ByteString {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next item and advances one {@code LiteralByteString}.
|
||||
* Returns the next item and advances one
|
||||
* {@link com.google.protobuf.ByteString.LeafByteString}.
|
||||
*
|
||||
* @return next non-empty LiteralByteString or {@code null}
|
||||
* @return next non-empty LeafByteString or {@code null}
|
||||
*/
|
||||
@Override
|
||||
public LeafByteString next() {
|
||||
|
@ -35,12 +35,12 @@ import java.util.AbstractSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* A custom map implementation from FieldDescriptor to Object optimized to
|
||||
|
@ -425,7 +425,7 @@ public final class TextFormat {
|
||||
case STRING:
|
||||
generator.print("\"");
|
||||
generator.print(escapeNonAscii
|
||||
? escapeText((String) value)
|
||||
? TextFormatEscaper.escapeText((String) value)
|
||||
: escapeDoubleQuotesAndBackslashes((String) value)
|
||||
.replace("\n", "\\n"));
|
||||
generator.print("\"");
|
||||
@ -661,6 +661,14 @@ public final class TextFormat {
|
||||
nextToken();
|
||||
}
|
||||
|
||||
int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
int getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
/** Are we at the end of the input? */
|
||||
public boolean atEnd() {
|
||||
return currentToken.length() == 0;
|
||||
@ -1074,7 +1082,7 @@ public final class TextFormat {
|
||||
private ParseException floatParseException(final NumberFormatException e) {
|
||||
return parseException("Couldn't parse number: " + e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link UnknownFieldParseException} with the line and column
|
||||
* numbers of the previous token in the description, and the unknown field
|
||||
@ -1133,7 +1141,7 @@ public final class TextFormat {
|
||||
return column;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thrown when encountering an unknown field while parsing
|
||||
* a text format message.
|
||||
@ -1257,11 +1265,14 @@ public final class TextFormat {
|
||||
|
||||
private final boolean allowUnknownFields;
|
||||
private final SingularOverwritePolicy singularOverwritePolicy;
|
||||
private TextFormatParseInfoTree.Builder parseInfoTreeBuilder;
|
||||
|
||||
private Parser(boolean allowUnknownFields,
|
||||
SingularOverwritePolicy singularOverwritePolicy) {
|
||||
private Parser(
|
||||
boolean allowUnknownFields, SingularOverwritePolicy singularOverwritePolicy,
|
||||
TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
|
||||
this.allowUnknownFields = allowUnknownFields;
|
||||
this.singularOverwritePolicy = singularOverwritePolicy;
|
||||
this.parseInfoTreeBuilder = parseInfoTreeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1278,6 +1289,7 @@ public final class TextFormat {
|
||||
private boolean allowUnknownFields = false;
|
||||
private SingularOverwritePolicy singularOverwritePolicy =
|
||||
SingularOverwritePolicy.ALLOW_SINGULAR_OVERWRITES;
|
||||
private TextFormatParseInfoTree.Builder parseInfoTreeBuilder = null;
|
||||
|
||||
|
||||
/**
|
||||
@ -1288,8 +1300,15 @@ public final class TextFormat {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setParseInfoTreeBuilder(
|
||||
TextFormatParseInfoTree.Builder parseInfoTreeBuilder) {
|
||||
this.parseInfoTreeBuilder = parseInfoTreeBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Parser build() {
|
||||
return new Parser(allowUnknownFields, singularOverwritePolicy);
|
||||
return new Parser(
|
||||
allowUnknownFields, singularOverwritePolicy, parseInfoTreeBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1380,7 +1399,21 @@ public final class TextFormat {
|
||||
final ExtensionRegistry extensionRegistry,
|
||||
final MessageReflection.MergeTarget target)
|
||||
throws ParseException {
|
||||
mergeField(tokenizer, extensionRegistry, target, parseInfoTreeBuilder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single field from {@code tokenizer} and merge it into
|
||||
* {@code builder}.
|
||||
*/
|
||||
private void mergeField(final Tokenizer tokenizer,
|
||||
final ExtensionRegistry extensionRegistry,
|
||||
final MessageReflection.MergeTarget target,
|
||||
TextFormatParseInfoTree.Builder parseTreeBuilder)
|
||||
throws ParseException {
|
||||
FieldDescriptor field = null;
|
||||
int startLine = tokenizer.getLine();
|
||||
int startColumn = tokenizer.getColumn();
|
||||
final Descriptor type = target.getDescriptorForType();
|
||||
ExtensionRegistry.ExtensionInfo extension = null;
|
||||
|
||||
@ -1472,14 +1505,51 @@ public final class TextFormat {
|
||||
// Handle potential ':'.
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
tokenizer.tryConsume(":"); // optional
|
||||
if (parseTreeBuilder != null) {
|
||||
TextFormatParseInfoTree.Builder childParseTreeBuilder =
|
||||
parseTreeBuilder.getBuilderForSubMessageField(field);
|
||||
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
|
||||
childParseTreeBuilder);
|
||||
} else {
|
||||
consumeFieldValues(tokenizer, extensionRegistry, target, field, extension,
|
||||
parseTreeBuilder);
|
||||
}
|
||||
} else {
|
||||
tokenizer.consume(":"); // required
|
||||
consumeFieldValues(
|
||||
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
|
||||
}
|
||||
|
||||
if (parseTreeBuilder != null) {
|
||||
parseTreeBuilder.setLocation(
|
||||
field, TextFormatParseLocation.create(startLine, startColumn));
|
||||
}
|
||||
|
||||
// For historical reasons, fields may optionally be separated by commas or
|
||||
// semicolons.
|
||||
if (!tokenizer.tryConsume(";")) {
|
||||
tokenizer.tryConsume(",");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a one or more field values from {@code tokenizer} and merge it into
|
||||
* {@code builder}.
|
||||
*/
|
||||
private void consumeFieldValues(
|
||||
final Tokenizer tokenizer,
|
||||
final ExtensionRegistry extensionRegistry,
|
||||
final MessageReflection.MergeTarget target,
|
||||
final FieldDescriptor field,
|
||||
final ExtensionRegistry.ExtensionInfo extension,
|
||||
final TextFormatParseInfoTree.Builder parseTreeBuilder)
|
||||
throws ParseException {
|
||||
// Support specifying repeated field values as a comma-separated list.
|
||||
// Ex."foo: [1, 2, 3]"
|
||||
if (field.isRepeated() && tokenizer.tryConsume("[")) {
|
||||
while (true) {
|
||||
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension);
|
||||
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension,
|
||||
parseTreeBuilder);
|
||||
if (tokenizer.tryConsume("]")) {
|
||||
// End of list.
|
||||
break;
|
||||
@ -1487,13 +1557,8 @@ public final class TextFormat {
|
||||
tokenizer.consume(",");
|
||||
}
|
||||
} else {
|
||||
consumeFieldValue(tokenizer, extensionRegistry, target, field, extension);
|
||||
}
|
||||
|
||||
// For historical reasons, fields may optionally be separated by commas or
|
||||
// semicolons.
|
||||
if (!tokenizer.tryConsume(";")) {
|
||||
tokenizer.tryConsume(",");
|
||||
consumeFieldValue(
|
||||
tokenizer, extensionRegistry, target, field, extension, parseTreeBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1506,7 +1571,8 @@ public final class TextFormat {
|
||||
final ExtensionRegistry extensionRegistry,
|
||||
final MessageReflection.MergeTarget target,
|
||||
final FieldDescriptor field,
|
||||
final ExtensionRegistry.ExtensionInfo extension)
|
||||
final ExtensionRegistry.ExtensionInfo extension,
|
||||
final TextFormatParseInfoTree.Builder parseTreeBuilder)
|
||||
throws ParseException {
|
||||
Object value = null;
|
||||
|
||||
@ -1528,7 +1594,7 @@ public final class TextFormat {
|
||||
throw tokenizer.parseException(
|
||||
"Expected \"" + endToken + "\".");
|
||||
}
|
||||
mergeField(tokenizer, extensionRegistry, subField);
|
||||
mergeField(tokenizer, extensionRegistry, subField, parseTreeBuilder);
|
||||
}
|
||||
|
||||
value = subField.finish();
|
||||
@ -1704,11 +1770,6 @@ public final class TextFormat {
|
||||
// Some of these methods are package-private because Descriptors.java uses
|
||||
// them.
|
||||
|
||||
private interface ByteSequence {
|
||||
int size();
|
||||
byte byteAt(int offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes bytes in the format used in protocol buffer text format, which
|
||||
* is the same as the format used for C string literals. All bytes
|
||||
@ -1717,74 +1778,15 @@ public final class TextFormat {
|
||||
* which no defined short-hand escape sequence is defined will be escaped
|
||||
* using 3-digit octal sequences.
|
||||
*/
|
||||
public static String escapeBytes(final ByteSequence input) {
|
||||
final StringBuilder builder = new StringBuilder(input.size());
|
||||
for (int i = 0; i < input.size(); i++) {
|
||||
final byte b = input.byteAt(i);
|
||||
switch (b) {
|
||||
// Java does not recognize \a or \v, apparently.
|
||||
case 0x07: builder.append("\\a"); break;
|
||||
case '\b': builder.append("\\b"); break;
|
||||
case '\f': builder.append("\\f"); break;
|
||||
case '\n': builder.append("\\n"); break;
|
||||
case '\r': builder.append("\\r"); break;
|
||||
case '\t': builder.append("\\t"); break;
|
||||
case 0x0b: builder.append("\\v"); break;
|
||||
case '\\': builder.append("\\\\"); break;
|
||||
case '\'': builder.append("\\\'"); break;
|
||||
case '"' : builder.append("\\\""); break;
|
||||
default:
|
||||
// Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
|
||||
// printable. Other byte values must be escaped.
|
||||
if (b >= 0x20 && b <= 0x7e) {
|
||||
builder.append((char) b);
|
||||
} else {
|
||||
builder.append('\\');
|
||||
builder.append((char) ('0' + ((b >>> 6) & 3)));
|
||||
builder.append((char) ('0' + ((b >>> 3) & 7)));
|
||||
builder.append((char) ('0' + (b & 7)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes bytes in the format used in protocol buffer text format, which
|
||||
* is the same as the format used for C string literals. All bytes
|
||||
* that are not printable 7-bit ASCII characters are escaped, as well as
|
||||
* backslash, single-quote, and double-quote characters. Characters for
|
||||
* which no defined short-hand escape sequence is defined will be escaped
|
||||
* using 3-digit octal sequences.
|
||||
*/
|
||||
public static String escapeBytes(final ByteString input) {
|
||||
return escapeBytes(new ByteSequence() {
|
||||
@Override
|
||||
public int size() {
|
||||
return input.size();
|
||||
}
|
||||
@Override
|
||||
public byte byteAt(int offset) {
|
||||
return input.byteAt(offset);
|
||||
}
|
||||
});
|
||||
public static String escapeBytes(ByteString input) {
|
||||
return TextFormatEscaper.escapeBytes(input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #escapeBytes(ByteString)}, but used for byte array.
|
||||
*/
|
||||
public static String escapeBytes(final byte[] input) {
|
||||
return escapeBytes(new ByteSequence() {
|
||||
@Override
|
||||
public int size() {
|
||||
return input.length;
|
||||
}
|
||||
@Override
|
||||
public byte byteAt(int offset) {
|
||||
return input[offset];
|
||||
}
|
||||
});
|
||||
public static String escapeBytes(byte[] input) {
|
||||
return TextFormatEscaper.escapeBytes(input);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1868,7 +1870,9 @@ public final class TextFormat {
|
||||
}
|
||||
}
|
||||
|
||||
return ByteString.copyFrom(result, 0, pos);
|
||||
return result.length == pos
|
||||
? ByteString.wrap(result) // This reference has not been out of our control.
|
||||
: ByteString.copyFrom(result, 0, pos);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1896,7 +1900,7 @@ public final class TextFormat {
|
||||
* Escape double quotes and backslashes in a String for unicode output of a message.
|
||||
*/
|
||||
public static String escapeDoubleQuotesAndBackslashes(final String input) {
|
||||
return input.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
return TextFormatEscaper.escapeDoubleQuotesAndBackslashes(input);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,137 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
/**
|
||||
* Provide text format escaping support for proto2 instances.
|
||||
*/
|
||||
final class TextFormatEscaper {
|
||||
private TextFormatEscaper() {}
|
||||
|
||||
private interface ByteSequence {
|
||||
int size();
|
||||
byte byteAt(int offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes bytes in the format used in protocol buffer text format, which
|
||||
* is the same as the format used for C string literals. All bytes
|
||||
* that are not printable 7-bit ASCII characters are escaped, as well as
|
||||
* backslash, single-quote, and double-quote characters. Characters for
|
||||
* which no defined short-hand escape sequence is defined will be escaped
|
||||
* using 3-digit octal sequences.
|
||||
*/
|
||||
static String escapeBytes(final ByteSequence input) {
|
||||
final StringBuilder builder = new StringBuilder(input.size());
|
||||
for (int i = 0; i < input.size(); i++) {
|
||||
final byte b = input.byteAt(i);
|
||||
switch (b) {
|
||||
// Java does not recognize \a or \v, apparently.
|
||||
case 0x07: builder.append("\\a"); break;
|
||||
case '\b': builder.append("\\b"); break;
|
||||
case '\f': builder.append("\\f"); break;
|
||||
case '\n': builder.append("\\n"); break;
|
||||
case '\r': builder.append("\\r"); break;
|
||||
case '\t': builder.append("\\t"); break;
|
||||
case 0x0b: builder.append("\\v"); break;
|
||||
case '\\': builder.append("\\\\"); break;
|
||||
case '\'': builder.append("\\\'"); break;
|
||||
case '"' : builder.append("\\\""); break;
|
||||
default:
|
||||
// Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
|
||||
// printable. Other byte values must be escaped.
|
||||
if (b >= 0x20 && b <= 0x7e) {
|
||||
builder.append((char) b);
|
||||
} else {
|
||||
builder.append('\\');
|
||||
builder.append((char) ('0' + ((b >>> 6) & 3)));
|
||||
builder.append((char) ('0' + ((b >>> 3) & 7)));
|
||||
builder.append((char) ('0' + (b & 7)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes bytes in the format used in protocol buffer text format, which
|
||||
* is the same as the format used for C string literals. All bytes
|
||||
* that are not printable 7-bit ASCII characters are escaped, as well as
|
||||
* backslash, single-quote, and double-quote characters. Characters for
|
||||
* which no defined short-hand escape sequence is defined will be escaped
|
||||
* using 3-digit octal sequences.
|
||||
*/
|
||||
static String escapeBytes(final ByteString input) {
|
||||
return escapeBytes(new ByteSequence() {
|
||||
@Override
|
||||
public int size() {
|
||||
return input.size();
|
||||
}
|
||||
@Override
|
||||
public byte byteAt(int offset) {
|
||||
return input.byteAt(offset);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #escapeBytes(ByteString)}, but used for byte array.
|
||||
*/
|
||||
static String escapeBytes(final byte[] input) {
|
||||
return escapeBytes(new ByteSequence() {
|
||||
@Override
|
||||
public int size() {
|
||||
return input.length;
|
||||
}
|
||||
@Override
|
||||
public byte byteAt(int offset) {
|
||||
return input[offset];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #escapeBytes(ByteString)}, but escapes a text string.
|
||||
* Non-ASCII characters are first encoded as UTF-8, then each byte is escaped
|
||||
* individually as a 3-digit octal escape. Yes, it's weird.
|
||||
*/
|
||||
static String escapeText(final String input) {
|
||||
return escapeBytes(ByteString.copyFromUtf8(input));
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape double quotes and backslashes in a String for unicode output of a message.
|
||||
*/
|
||||
static String escapeDoubleQuotesAndBackslashes(final String input) {
|
||||
return input.replace("\\", "\\\\").replace("\"", "\\\"");
|
||||
}
|
||||
}
|
@ -0,0 +1,225 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
||||
/**
|
||||
* Data structure which is populated with the locations of each field value parsed from the text.
|
||||
*
|
||||
* <p>The locations of primary fields values are retrieved by {@code getLocation} or
|
||||
* {@code getLocations}. The locations of sub message values are within nested
|
||||
* {@code TextFormatParseInfoTree}s and are retrieve by {@getNestedTree} or {code @getNestedTrees}.
|
||||
*
|
||||
* <p>The {@code TextFormatParseInfoTree} is created by a Builder.
|
||||
*/
|
||||
public class TextFormatParseInfoTree {
|
||||
|
||||
// Defines a mapping between each field's descriptor to the list of locations where
|
||||
// its value(s) were was encountered.
|
||||
private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
|
||||
|
||||
// Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for
|
||||
// sub message location information.
|
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField;
|
||||
|
||||
/**
|
||||
* Construct a {@code TextFormatParseInfoTree}.
|
||||
*
|
||||
* @param locationsFromField a map of fields to location in the source code
|
||||
* @param subtreeBuildersFromField a map of fields to parse tree location information builders
|
||||
*/
|
||||
private TextFormatParseInfoTree(
|
||||
Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField,
|
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) {
|
||||
|
||||
// The maps are unmodifiable. The values in the maps are unmodifiable.
|
||||
Map<FieldDescriptor, List<TextFormatParseLocation>> locs =
|
||||
new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
|
||||
for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) {
|
||||
locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue()));
|
||||
}
|
||||
this.locationsFromField = Collections.unmodifiableMap(locs);
|
||||
|
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs =
|
||||
new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>();
|
||||
for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) {
|
||||
List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>();
|
||||
for (Builder subBuilder : kv.getValue()) {
|
||||
submessagesOfField.add(subBuilder.build());
|
||||
}
|
||||
subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField));
|
||||
}
|
||||
this.subtreesFromField = Collections.unmodifiableMap(subs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all the locations of a field.
|
||||
*
|
||||
* @param fieldDescriptor the the @{link FieldDescriptor} of the desired field
|
||||
* @return a list of the locations of values of the field. If there are not values
|
||||
* or the field doesn't exist, an empty list is returned.
|
||||
*/
|
||||
public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) {
|
||||
List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor);
|
||||
return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the location in the source of a field's value.
|
||||
*
|
||||
* <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed
|
||||
* text.
|
||||
*
|
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired field
|
||||
* @param index the index of the value.
|
||||
* @return the {@link TextFormatParseLocation} of the value
|
||||
* @throws IllegalArgumentException index is out of range
|
||||
*/
|
||||
public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) {
|
||||
return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a list of all the location information trees for a sub message field.
|
||||
*
|
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired field
|
||||
* @return A list of {@link TextFormatParseInfoTree}
|
||||
*/
|
||||
public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) {
|
||||
List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor);
|
||||
return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parse info tree for the given field, which must be a message type.
|
||||
*
|
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message
|
||||
* @param index the index of message value.
|
||||
* @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field
|
||||
* doesn't exist or the index is out of range.
|
||||
* @throws IllegalArgumentException if index is out of range
|
||||
*/
|
||||
public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) {
|
||||
return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a builder for a {@code ParseInfoTree}.
|
||||
*
|
||||
* @return the builder
|
||||
*/
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) {
|
||||
if (index >= list.size() || index < 0) {
|
||||
throw new IllegalArgumentException(String.format("Illegal index field: %s, index %d",
|
||||
fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index));
|
||||
}
|
||||
return list.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for a {@link TextFormatParseInfoTree}.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField;
|
||||
|
||||
// Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for
|
||||
// sub message location information.
|
||||
private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField;
|
||||
|
||||
/**
|
||||
* Create a root level {@ParseInfoTree} builder.
|
||||
*/
|
||||
private Builder() {
|
||||
locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>();
|
||||
subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Record the starting location of a single value for a field.
|
||||
*
|
||||
* @param fieldDescriptor the field
|
||||
* @param location source code location information
|
||||
*/
|
||||
public Builder setLocation(
|
||||
final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) {
|
||||
List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor);
|
||||
if (fieldLocations == null) {
|
||||
fieldLocations = new ArrayList<TextFormatParseLocation>();
|
||||
locationsFromField.put(fieldDescriptor, fieldLocations);
|
||||
}
|
||||
fieldLocations.add(location);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set for a sub message.
|
||||
*
|
||||
* <p>A new builder is created for a sub message. The builder that is returned is a new builder.
|
||||
* The return is <emph>not</emph> the invoked {@code builder.getBuilderForSubMessageField}.
|
||||
*
|
||||
* @param fieldDescriptor the field whose value is the submessage
|
||||
* @return a new Builder for the sub message
|
||||
*/
|
||||
public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) {
|
||||
List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor);
|
||||
if (submessageBuilders == null) {
|
||||
submessageBuilders = new ArrayList<Builder>();
|
||||
subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders);
|
||||
}
|
||||
Builder subtreeBuilder = new Builder();
|
||||
submessageBuilders.add(subtreeBuilder);
|
||||
return subtreeBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@code TextFormatParseInfoTree}.
|
||||
*
|
||||
* @return the {@code TextFormatParseInfoTree}
|
||||
*/
|
||||
public TextFormatParseInfoTree build() {
|
||||
return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* A location in the source code.
|
||||
*
|
||||
* <p>A location is the starting line number and starting column number.
|
||||
*/
|
||||
public final class TextFormatParseLocation {
|
||||
|
||||
/**
|
||||
* The empty location.
|
||||
*/
|
||||
public static final TextFormatParseLocation EMPTY = new TextFormatParseLocation(-1, -1);
|
||||
|
||||
/**
|
||||
* Create a location.
|
||||
*
|
||||
* @param line the starting line number
|
||||
* @param column the starting column number
|
||||
* @return a {@code ParseLocation}
|
||||
*/
|
||||
static TextFormatParseLocation create(int line, int column) {
|
||||
if (line == -1 && column == -1) {
|
||||
return EMPTY;
|
||||
}
|
||||
if (line < 0 || column < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("line and column values must be >= 0: line %d, column: %d", line, column));
|
||||
}
|
||||
return new TextFormatParseLocation(line, column);
|
||||
}
|
||||
|
||||
private final int line;
|
||||
private final int column;
|
||||
|
||||
private TextFormatParseLocation(int line, int column) {
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public int getColumn() {
|
||||
return column;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("ParseLocation{line=%d, column=%d}", line, column);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof TextFormatParseLocation)) {
|
||||
return false;
|
||||
}
|
||||
TextFormatParseLocation that = (TextFormatParseLocation) o;
|
||||
return (this.line == that.getLine())
|
||||
&& (this.column == that.getColumn());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int[] values = {line, column};
|
||||
return Arrays.hashCode(values);
|
||||
}
|
||||
}
|
@ -61,15 +61,6 @@ public final class UnknownFieldSetLite {
|
||||
public static UnknownFieldSetLite getDefaultInstance() {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an empty {@code UnknownFieldSetLite.Builder}.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
public static Builder newBuilder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new mutable instance.
|
||||
@ -262,6 +253,21 @@ public final class UnknownFieldSetLite {
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a String representation of the unknown field set.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*
|
||||
* @param buffer the buffer to write to
|
||||
* @param indent the number of spaces the fields should be indented by
|
||||
*/
|
||||
final void printWithIndent(StringBuilder buffer, int indent) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
int fieldNumber = WireFormat.getTagFieldNumber(tags[i]);
|
||||
MessageLiteToString.printField(buffer, indent, String.valueOf(fieldNumber), objects[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeField(int tag, Object value) {
|
||||
ensureCapacity();
|
||||
|
||||
@ -369,90 +375,4 @@ public final class UnknownFieldSetLite {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder for {@link UnknownFieldSetLite}s.
|
||||
*
|
||||
* <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
// TODO(dweis): Update the mutable API to no longer need this builder and delete.
|
||||
public static final class Builder {
|
||||
|
||||
private UnknownFieldSetLite set;
|
||||
|
||||
private Builder() {
|
||||
this.set = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures internal state is initialized for use.
|
||||
*/
|
||||
private void ensureNotBuilt() {
|
||||
if (set == null) {
|
||||
set = new UnknownFieldSetLite();
|
||||
}
|
||||
|
||||
set.checkMutable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single field from {@code input} and merge it into this set.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*
|
||||
* @param tag The field's tag number, which was already parsed.
|
||||
* @return {@code false} if the tag is an end group tag.
|
||||
*/
|
||||
boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException {
|
||||
ensureNotBuilt();
|
||||
return set.mergeFieldFrom(tag, input);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for merging a new field containing a single varint
|
||||
* value. This is used in particular when an unknown enum value is
|
||||
* encountered.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
Builder mergeVarintField(int fieldNumber, int value) {
|
||||
ensureNotBuilt();
|
||||
set.mergeVarintField(fieldNumber, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method for merging a length-delimited field.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
public Builder mergeLengthDelimitedField(final int fieldNumber, final ByteString value) {
|
||||
ensureNotBuilt();
|
||||
set.mergeLengthDelimitedField(fieldNumber, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the {@link UnknownFieldSetLite} and return it.
|
||||
*
|
||||
* <p>Once {@code build()} has been called, the {@code Builder} will no
|
||||
* longer be usable. Calling any method after {@code build()} will result
|
||||
* in undefined behavior and can cause an
|
||||
* {@code UnsupportedOperationException} to be thrown.
|
||||
*
|
||||
* <p>For use by generated code only.
|
||||
*/
|
||||
public UnknownFieldSetLite build() {
|
||||
if (set == null) {
|
||||
return DEFAULT_INSTANCE;
|
||||
}
|
||||
|
||||
set.checkMutable();
|
||||
set.makeImmutable();
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
@ -49,8 +50,8 @@ public final class UnsafeByteOperations {
|
||||
/**
|
||||
* An unsafe operation that returns a {@link ByteString} that is backed by the provided buffer.
|
||||
*
|
||||
* @param buffer the Java NIO buffer to be wrapped.
|
||||
* @return a {@link ByteString} backed by the provided buffer.
|
||||
* @param buffer the Java NIO buffer to be wrapped
|
||||
* @return a {@link ByteString} backed by the provided buffer
|
||||
*/
|
||||
public static ByteString unsafeWrap(ByteBuffer buffer) {
|
||||
if (buffer.hasArray()) {
|
||||
@ -60,4 +61,24 @@ public final class UnsafeByteOperations {
|
||||
return new NioByteString(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given {@link ByteString} to the provided {@link ByteOutput}. Calling this method may
|
||||
* result in multiple operations on the target {@link ByteOutput}
|
||||
* (i.e. for roped {@link ByteString}s).
|
||||
*
|
||||
* <p>This method exposes the internal backing buffer(s) of the {@link ByteString} to the {@link
|
||||
* ByteOutput} in order to avoid additional copying overhead. It would be possible for a malicious
|
||||
* {@link ByteOutput} to corrupt the {@link ByteString}. Use with caution!
|
||||
*
|
||||
* <p> NOTE: The {@link ByteOutput} <strong>MUST NOT</strong> modify the provided buffers. Doing
|
||||
* so may result in corrupted data, which would be difficult to debug.
|
||||
*
|
||||
* @param bytes the {@link ByteString} to be written
|
||||
* @param output the output to receive the bytes
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public static void unsafeWriteTo(ByteString bytes, ByteOutput output) throws IOException {
|
||||
bytes.writeTo(output);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -40,7 +40,6 @@ import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Map;
|
||||
|
@ -75,6 +75,51 @@ public class AnyTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testCustomTypeUrls() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
TestAny container = TestAny.newBuilder()
|
||||
.setValue(Any.pack(message, "xxx.com")).build();
|
||||
|
||||
assertEquals(
|
||||
"xxx.com/" + TestAllTypes.getDescriptor().getFullName(),
|
||||
container.getValue().getTypeUrl());
|
||||
|
||||
assertTrue(container.getValue().is(TestAllTypes.class));
|
||||
assertFalse(container.getValue().is(TestAny.class));
|
||||
|
||||
TestAllTypes result = container.getValue().unpack(TestAllTypes.class);
|
||||
TestUtil.assertAllFieldsSet(result);
|
||||
|
||||
container = TestAny.newBuilder()
|
||||
.setValue(Any.pack(message, "yyy.com/")).build();
|
||||
|
||||
assertEquals(
|
||||
"yyy.com/" + TestAllTypes.getDescriptor().getFullName(),
|
||||
container.getValue().getTypeUrl());
|
||||
|
||||
assertTrue(container.getValue().is(TestAllTypes.class));
|
||||
assertFalse(container.getValue().is(TestAny.class));
|
||||
|
||||
result = container.getValue().unpack(TestAllTypes.class);
|
||||
TestUtil.assertAllFieldsSet(result);
|
||||
|
||||
container = TestAny.newBuilder()
|
||||
.setValue(Any.pack(message, "")).build();
|
||||
|
||||
assertEquals(
|
||||
"/" + TestAllTypes.getDescriptor().getFullName(),
|
||||
container.getValue().getTypeUrl());
|
||||
|
||||
assertTrue(container.getValue().is(TestAllTypes.class));
|
||||
assertFalse(container.getValue().is(TestAny.class));
|
||||
|
||||
result = container.getValue().unpack(TestAllTypes.class);
|
||||
TestUtil.assertAllFieldsSet(result);
|
||||
}
|
||||
|
||||
public void testCachedUnpackResult() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
TestUtil.setAllFields(builder);
|
||||
|
@ -37,7 +37,6 @@ import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
|
||||
/**
|
||||
* This class tests {@link BoundedByteString}, which extends {@link LiteralByteString},
|
||||
* by inheriting the tests from {@link LiteralByteStringTest}. The only method which
|
||||
|
@ -0,0 +1,81 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Tests for {@link ByteBufferWriter}.
|
||||
*/
|
||||
public class ByteBufferWriterTest extends TestCase {
|
||||
|
||||
public void testHeapBuffer() throws IOException {
|
||||
// Test a small and large buffer.
|
||||
testWrite(ByteBuffer.allocate(100));
|
||||
testWrite(ByteBuffer.allocate(1024 * 100));
|
||||
}
|
||||
|
||||
public void testDirectBuffer() throws IOException {
|
||||
// Test a small and large buffer.
|
||||
testWrite(ByteBuffer.allocateDirect(100));
|
||||
testWrite(ByteBuffer.allocateDirect(1024 * 100));
|
||||
}
|
||||
|
||||
private void testWrite(ByteBuffer buffer) throws IOException {
|
||||
fillRandom(buffer);
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream(buffer.remaining());
|
||||
ByteBufferWriter.write(buffer, os);
|
||||
assertEquals(0, buffer.position());
|
||||
assertTrue(Arrays.equals(toArray(buffer), os.toByteArray()));
|
||||
}
|
||||
|
||||
private void fillRandom(ByteBuffer buf) {
|
||||
byte[] bytes = new byte[buf.remaining()];
|
||||
new Random().nextBytes(bytes);
|
||||
buf.put(bytes);
|
||||
buf.flip();
|
||||
return;
|
||||
}
|
||||
|
||||
private byte[] toArray(ByteBuffer buf) {
|
||||
int originalPosition = buf.position();
|
||||
byte[] bytes = new byte[buf.remaining()];
|
||||
buf.get(bytes);
|
||||
buf.position(originalPosition);
|
||||
return bytes;
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
@ -757,4 +758,17 @@ public class ByteStringTest extends TestCase {
|
||||
assertEquals((byte) 2, result[dataSize - dataSize / 2]);
|
||||
assertEquals((byte) 2, result[dataSize - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests ByteString uses Arrays based byte copier when running under Hotstop VM.
|
||||
*/
|
||||
public void testByteArrayCopier() throws Exception {
|
||||
Field field = ByteString.class.getDeclaredField("byteArrayCopier");
|
||||
field.setAccessible(true);
|
||||
Object byteArrayCopier = field.get(null);
|
||||
assertNotNull(byteArrayCopier);
|
||||
assertTrue(
|
||||
byteArrayCopier.toString(),
|
||||
byteArrayCopier.getClass().getSimpleName().endsWith("ArraysByteArrayCopier"));
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import proto2_test_check_utf8.TestCheckUtf8.BytesWrapper;
|
||||
import proto2_test_check_utf8.TestCheckUtf8.StringWrapper;
|
||||
import proto2_test_check_utf8_size.TestCheckUtf8Size.BytesWrapperSize;
|
||||
import proto2_test_check_utf8_size.TestCheckUtf8Size.StringWrapperSize;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
|
@ -547,6 +547,56 @@ public class CodedInputStreamTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testReadString() throws Exception {
|
||||
String lorem = "Lorem ipsum dolor sit amet ";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < 4096; i += lorem.length()) {
|
||||
builder.append(lorem);
|
||||
}
|
||||
lorem = builder.toString().substring(0, 4096);
|
||||
byte[] bytes = lorem.getBytes("UTF-8");
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(bytes.length);
|
||||
output.writeRawBytes(bytes);
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input =
|
||||
CodedInputStream.newInstance(
|
||||
new ByteArrayInputStream(rawOutput.toByteString().toByteArray()));
|
||||
assertEquals(tag, input.readTag());
|
||||
String text = input.readString();
|
||||
assertEquals(lorem, text);
|
||||
}
|
||||
|
||||
public void testReadStringRequireUtf8() throws Exception {
|
||||
String lorem = "Lorem ipsum dolor sit amet ";
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < 4096; i += lorem.length()) {
|
||||
builder.append(lorem);
|
||||
}
|
||||
lorem = builder.toString().substring(0, 4096);
|
||||
byte[] bytes = lorem.getBytes("UTF-8");
|
||||
ByteString.Output rawOutput = ByteString.newOutput();
|
||||
CodedOutputStream output = CodedOutputStream.newInstance(rawOutput, bytes.length);
|
||||
|
||||
int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED);
|
||||
output.writeRawVarint32(tag);
|
||||
output.writeRawVarint32(bytes.length);
|
||||
output.writeRawBytes(bytes);
|
||||
output.flush();
|
||||
|
||||
CodedInputStream input =
|
||||
CodedInputStream.newInstance(
|
||||
new ByteArrayInputStream(rawOutput.toByteString().toByteArray()));
|
||||
assertEquals(tag, input.readTag());
|
||||
String text = input.readStringRequireUtf8();
|
||||
assertEquals(lorem, text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that if we readString invalid UTF-8 bytes, no exception
|
||||
* is thrown. Instead, the invalid bytes are replaced with the Unicode
|
||||
|
@ -36,6 +36,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Test field deprecation
|
||||
*
|
||||
|
@ -59,7 +59,6 @@ import protobuf_unittest.UnittestProto.TestMultipleExtensionRanges;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestReservedFields;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
@ -573,6 +572,42 @@ public class DescriptorsTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnknownFieldsDenied() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
|
||||
try {
|
||||
Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0]);
|
||||
fail("DescriptorValidationException expected");
|
||||
} catch (DescriptorValidationException e) {
|
||||
assertTrue(e.getMessage().indexOf("Bar") != -1);
|
||||
assertTrue(e.getMessage().indexOf("is not defined") != -1);
|
||||
}
|
||||
}
|
||||
|
||||
public void testUnknownFieldsAllowed() throws Exception {
|
||||
FileDescriptorProto fooProto = FileDescriptorProto.newBuilder()
|
||||
.setName("foo.proto")
|
||||
.addDependency("bar.proto")
|
||||
.addMessageType(DescriptorProto.newBuilder()
|
||||
.setName("Foo")
|
||||
.addField(FieldDescriptorProto.newBuilder()
|
||||
.setLabel(FieldDescriptorProto.Label.LABEL_OPTIONAL)
|
||||
.setTypeName("Bar")
|
||||
.setName("bar")
|
||||
.setNumber(1)))
|
||||
.build();
|
||||
Descriptors.FileDescriptor.buildFrom(fooProto, new FileDescriptor[0], true);
|
||||
}
|
||||
|
||||
public void testHiddenDependency() throws Exception {
|
||||
FileDescriptorProto barProto = FileDescriptorProto.newBuilder()
|
||||
.setName("bar.proto")
|
||||
|
@ -33,13 +33,13 @@ package com.google.protobuf;
|
||||
import com.google.protobuf.Descriptors.EnumDescriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.Descriptors.OneofDescriptor;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
|
76
java/core/src/test/java/com/google/protobuf/EnumTest.java
Normal file
76
java/core/src/test/java/com/google/protobuf/EnumTest.java
Normal file
@ -0,0 +1,76 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.UnittestLite.ForeignEnumLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class EnumTest extends TestCase {
|
||||
|
||||
public void testForNumber() {
|
||||
ForeignEnum e = ForeignEnum.forNumber(ForeignEnum.FOREIGN_BAR.getNumber());
|
||||
assertEquals(ForeignEnum.FOREIGN_BAR, e);
|
||||
|
||||
e = ForeignEnum.forNumber(1000);
|
||||
assertEquals(null, e);
|
||||
}
|
||||
|
||||
public void testForNumber_oneof() {
|
||||
TestAllTypes.OneofFieldCase e = TestAllTypes.OneofFieldCase.forNumber(
|
||||
TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber());
|
||||
assertEquals(TestAllTypes.OneofFieldCase.ONEOF_NESTED_MESSAGE, e);
|
||||
|
||||
e = TestAllTypes.OneofFieldCase.forNumber(1000);
|
||||
assertEquals(null, e);
|
||||
}
|
||||
|
||||
public void testForNumberLite() {
|
||||
ForeignEnumLite e = ForeignEnumLite.forNumber(ForeignEnumLite.FOREIGN_LITE_BAR.getNumber());
|
||||
assertEquals(ForeignEnumLite.FOREIGN_LITE_BAR, e);
|
||||
|
||||
e = ForeignEnumLite.forNumber(1000);
|
||||
assertEquals(null, e);
|
||||
}
|
||||
|
||||
public void testForNumberLite_oneof() {
|
||||
TestAllTypesLite.OneofFieldCase e = TestAllTypesLite.OneofFieldCase.forNumber(
|
||||
TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE.getNumber());
|
||||
assertEquals(TestAllTypesLite.OneofFieldCase.ONEOF_NESTED_MESSAGE, e);
|
||||
|
||||
e = TestAllTypesLite.OneofFieldCase.forNumber(1000);
|
||||
assertEquals(null, e);
|
||||
}
|
||||
}
|
||||
|
@ -44,9 +44,9 @@ import junit.framework.TestCase;
|
||||
* non-message fields.
|
||||
*/
|
||||
public class FieldPresenceTest extends TestCase {
|
||||
private static boolean hasMethod(Class clazz, String name) {
|
||||
private static boolean hasMethod(Class<?> clazz, String name) {
|
||||
try {
|
||||
if (clazz.getMethod(name, new Class[]{}) != null) {
|
||||
if (clazz.getMethod(name) != null) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@ -56,90 +56,90 @@ public class FieldPresenceTest extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isHasMethodRemoved(
|
||||
Class classWithFieldPresence,
|
||||
Class classWithoutFieldPresence,
|
||||
private static void assertHasMethodRemoved(
|
||||
Class<?> classWithFieldPresence,
|
||||
Class<?> classWithoutFieldPresence,
|
||||
String camelName) {
|
||||
return hasMethod(classWithFieldPresence, "get" + camelName)
|
||||
&& hasMethod(classWithFieldPresence, "has" + camelName)
|
||||
&& hasMethod(classWithoutFieldPresence, "get" + camelName)
|
||||
&& !hasMethod(classWithoutFieldPresence, "has" + camelName);
|
||||
assertTrue(hasMethod(classWithFieldPresence, "get" + camelName));
|
||||
assertTrue(hasMethod(classWithFieldPresence, "has" + camelName));
|
||||
assertTrue(hasMethod(classWithoutFieldPresence, "get" + camelName));
|
||||
assertFalse(hasMethod(classWithoutFieldPresence, "has" + camelName));
|
||||
}
|
||||
|
||||
public void testHasMethod() {
|
||||
// Optional non-message fields don't have a hasFoo() method generated.
|
||||
assertTrue(isHasMethodRemoved(
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OptionalInt32"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalInt32");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OptionalString"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalString");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OptionalBytes"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalBytes");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OptionalNestedEnum"));
|
||||
"OptionalNestedEnum");
|
||||
|
||||
assertTrue(isHasMethodRemoved(
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OptionalInt32"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalInt32");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OptionalString"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalString");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OptionalBytes"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OptionalBytes");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OptionalNestedEnum"));
|
||||
"OptionalNestedEnum");
|
||||
|
||||
// message fields still have the hasFoo() method generated.
|
||||
assertFalse(TestAllTypes.newBuilder().build().hasOptionalNestedMessage());
|
||||
assertFalse(TestAllTypes.newBuilder().hasOptionalNestedMessage());
|
||||
|
||||
// oneof fields don't have hasFoo() methods (even for message types).
|
||||
assertTrue(isHasMethodRemoved(
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OneofUint32"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofUint32");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OneofString"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofString");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OneofBytes"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofBytes");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.class,
|
||||
TestAllTypes.class,
|
||||
"OneofNestedMessage"));
|
||||
"OneofNestedMessage");
|
||||
|
||||
assertTrue(isHasMethodRemoved(
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OneofUint32"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofUint32");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OneofString"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofString");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OneofBytes"));
|
||||
assertTrue(isHasMethodRemoved(
|
||||
"OneofBytes");
|
||||
assertHasMethodRemoved(
|
||||
UnittestProto.TestAllTypes.Builder.class,
|
||||
TestAllTypes.Builder.class,
|
||||
"OneofNestedMessage"));
|
||||
"OneofNestedMessage");
|
||||
}
|
||||
|
||||
public void testOneofEquals() throws Exception {
|
||||
@ -232,24 +232,24 @@ public class FieldPresenceTest extends TestCase {
|
||||
assertTrue(message.hasField(optionalNestedEnumField));
|
||||
assertEquals(4, message.getAllFields().size());
|
||||
}
|
||||
|
||||
|
||||
public void testMessageField() {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
assertFalse(builder.hasOptionalNestedMessage());
|
||||
assertFalse(builder.build().hasOptionalNestedMessage());
|
||||
|
||||
|
||||
TestAllTypes.NestedMessage.Builder nestedBuilder =
|
||||
builder.getOptionalNestedMessageBuilder();
|
||||
assertTrue(builder.hasOptionalNestedMessage());
|
||||
assertTrue(builder.build().hasOptionalNestedMessage());
|
||||
|
||||
|
||||
nestedBuilder.setValue(1);
|
||||
assertEquals(1, builder.build().getOptionalNestedMessage().getValue());
|
||||
|
||||
|
||||
builder.clearOptionalNestedMessage();
|
||||
assertFalse(builder.hasOptionalNestedMessage());
|
||||
assertFalse(builder.build().hasOptionalNestedMessage());
|
||||
|
||||
|
||||
// Unlike non-message fields, if we set a message field to its default value (i.e.,
|
||||
// default instance), the field should be seen as present.
|
||||
builder.setOptionalNestedMessage(TestAllTypes.NestedMessage.getDefaultInstance());
|
||||
@ -340,7 +340,7 @@ public class FieldPresenceTest extends TestCase {
|
||||
assertTrue(builder.buildPartial().isInitialized());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Test that unknown fields are dropped.
|
||||
public void testUnknownFields() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -348,7 +348,7 @@ public class FieldPresenceTest extends TestCase {
|
||||
builder.addRepeatedInt32(5678);
|
||||
TestAllTypes message = builder.build();
|
||||
ByteString data = message.toByteString();
|
||||
|
||||
|
||||
TestOptionalFieldsOnly optionalOnlyMessage =
|
||||
TestOptionalFieldsOnly.parseFrom(data);
|
||||
// UnknownFieldSet should be empty.
|
||||
@ -360,7 +360,7 @@ public class FieldPresenceTest extends TestCase {
|
||||
// The repeated field is discarded because it's unknown to the optional-only
|
||||
// message.
|
||||
assertEquals(0, message.getRepeatedInt32Count());
|
||||
|
||||
|
||||
DynamicMessage dynamicOptionalOnlyMessage =
|
||||
DynamicMessage.getDefaultInstance(
|
||||
TestOptionalFieldsOnly.getDescriptor())
|
||||
|
@ -620,6 +620,21 @@ public class GeneratedMessageTest extends TestCase {
|
||||
TestUtil.assertExtensionsClear(TestAllExtensions.newBuilder().build());
|
||||
}
|
||||
|
||||
public void testUnsetRepeatedExtensionGetField() {
|
||||
TestAllExtensions message = TestAllExtensions.getDefaultInstance();
|
||||
Object value;
|
||||
|
||||
value = message.getField(UnittestProto.repeatedStringExtension.getDescriptor());
|
||||
assertTrue(value instanceof List);
|
||||
assertTrue(((List<?>) value).isEmpty());
|
||||
assertIsUnmodifiable((List<?>) value);
|
||||
|
||||
value = message.getField(UnittestProto.repeatedNestedMessageExtension.getDescriptor());
|
||||
assertTrue(value instanceof List);
|
||||
assertTrue(((List<?>) value).isEmpty());
|
||||
assertIsUnmodifiable((List<?>) value);
|
||||
}
|
||||
|
||||
public void testExtensionReflectionGetters() throws Exception {
|
||||
TestAllExtensions.Builder builder = TestAllExtensions.newBuilder();
|
||||
TestUtil.setAllExtensions(builder);
|
||||
|
@ -30,12 +30,18 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.DIRECT_NIO_FACTORY;
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT;
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT;
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.HEAP_NIO_FACTORY;
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.LITERAL_FACTORY;
|
||||
import static com.google.protobuf.IsValidUtf8TestUtil.testBytes;
|
||||
|
||||
import com.google.protobuf.IsValidUtf8TestUtil.ByteStringFactory;
|
||||
import com.google.protobuf.IsValidUtf8TestUtil.Shard;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Tests cases for {@link ByteString#isValidUtf8()}. This includes three
|
||||
* brute force tests that actually test every permutation of one byte, two byte,
|
||||
@ -51,31 +57,33 @@ import java.io.UnsupportedEncodingException;
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
public class IsValidUtf8Test extends TestCase {
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_1Byte() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(1,
|
||||
IsValidUtf8TestUtil.EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
public void testIsValidUtf8_1Byte() {
|
||||
testBytes(LITERAL_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(HEAP_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(DIRECT_NIO_FACTORY, 1, EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all two byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_2Bytes() throws UnsupportedEncodingException {
|
||||
IsValidUtf8TestUtil.testBytes(2,
|
||||
IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
public void testIsValidUtf8_2Bytes() {
|
||||
testBytes(LITERAL_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(HEAP_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(DIRECT_NIO_FACTORY, 2, IsValidUtf8TestUtil.EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that round tripping of all three byte permutations work.
|
||||
*/
|
||||
public void testIsValidUtf8_3Bytes() throws UnsupportedEncodingException {
|
||||
public void testIsValidUtf8_3Bytes() {
|
||||
// Travis' OOM killer doesn't like this test
|
||||
if (System.getenv("TRAVIS") == null) {
|
||||
IsValidUtf8TestUtil.testBytes(3,
|
||||
IsValidUtf8TestUtil.EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(LITERAL_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(HEAP_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
testBytes(DIRECT_NIO_FACTORY, 3, EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,8 +93,7 @@ public class IsValidUtf8Test extends TestCase {
|
||||
* {@link IsValidUtf8FourByteTest} is used for full coverage. This method
|
||||
* tests specific four-byte cases.
|
||||
*/
|
||||
public void testIsValidUtf8_4BytesSamples()
|
||||
throws UnsupportedEncodingException {
|
||||
public void testIsValidUtf8_4BytesSamples() {
|
||||
// Valid 4 byte.
|
||||
assertValidUtf8(0xF0, 0xA4, 0xAD, 0xA2);
|
||||
|
||||
@ -119,9 +126,7 @@ public class IsValidUtf8Test extends TestCase {
|
||||
assertTrue(asBytes("\u024B62\u024B62").isValidUtf8());
|
||||
|
||||
// Mixed string
|
||||
assertTrue(
|
||||
asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62")
|
||||
.isValidUtf8());
|
||||
assertTrue(asBytes("a\u020ac\u00a2b\\u024B62u020acc\u00a2de\u024B62").isValidUtf8());
|
||||
|
||||
// Not a valid string
|
||||
assertInvalidUtf8(-1, 0, -1, 0);
|
||||
@ -135,36 +140,35 @@ public class IsValidUtf8Test extends TestCase {
|
||||
return realBytes;
|
||||
}
|
||||
|
||||
private ByteString toByteString(int... bytes) {
|
||||
return ByteString.copyFrom(toByteArray(bytes));
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int[] bytes, boolean not) {
|
||||
private void assertValidUtf8(ByteStringFactory factory, int[] bytes, boolean not) {
|
||||
byte[] realBytes = toByteArray(bytes);
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes));
|
||||
assertTrue(not ^ Utf8.isValidUtf8(realBytes, 0, bytes.length));
|
||||
ByteString lit = ByteString.copyFrom(realBytes);
|
||||
ByteString sub = lit.substring(0, bytes.length);
|
||||
assertTrue(not ^ lit.isValidUtf8());
|
||||
ByteString leaf = factory.newByteString(realBytes);
|
||||
ByteString sub = leaf.substring(0, bytes.length);
|
||||
assertTrue(not ^ leaf.isValidUtf8());
|
||||
assertTrue(not ^ sub.isValidUtf8());
|
||||
ByteString[] ropes = {
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, lit),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
|
||||
RopeByteString.newInstanceForTest(lit, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, lit)
|
||||
};
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, leaf),
|
||||
RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
|
||||
RopeByteString.newInstanceForTest(leaf, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
|
||||
RopeByteString.newInstanceForTest(sub, leaf)};
|
||||
for (ByteString rope : ropes) {
|
||||
assertTrue(not ^ rope.isValidUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertValidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, false);
|
||||
assertValidUtf8(LITERAL_FACTORY, bytes, false);
|
||||
assertValidUtf8(HEAP_NIO_FACTORY, bytes, false);
|
||||
assertValidUtf8(DIRECT_NIO_FACTORY, bytes, false);
|
||||
}
|
||||
|
||||
private void assertInvalidUtf8(int... bytes) {
|
||||
assertValidUtf8(bytes, true);
|
||||
assertValidUtf8(LITERAL_FACTORY, bytes, true);
|
||||
assertValidUtf8(HEAP_NIO_FACTORY, bytes, true);
|
||||
assertValidUtf8(DIRECT_NIO_FACTORY, bytes, true);
|
||||
}
|
||||
|
||||
private static ByteString asBytes(String s) {
|
||||
@ -177,7 +181,6 @@ public class IsValidUtf8Test extends TestCase {
|
||||
for (Shard shard : IsValidUtf8TestUtil.FOUR_BYTE_SHARDS) {
|
||||
actual += shard.expected;
|
||||
}
|
||||
assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT,
|
||||
actual);
|
||||
assertEquals(IsValidUtf8TestUtil.EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT, actual);
|
||||
}
|
||||
}
|
||||
|
@ -30,9 +30,13 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
@ -52,64 +56,105 @@ import java.util.logging.Logger;
|
||||
* @author jonp@google.com (Jon Perlow)
|
||||
* @author martinrb@google.com (Martin Buchholz)
|
||||
*/
|
||||
class IsValidUtf8TestUtil {
|
||||
private static Logger logger = Logger.getLogger(
|
||||
IsValidUtf8TestUtil.class.getName());
|
||||
final class IsValidUtf8TestUtil {
|
||||
private static Logger logger = Logger.getLogger(IsValidUtf8TestUtil.class.getName());
|
||||
|
||||
private IsValidUtf8TestUtil() {}
|
||||
|
||||
static interface ByteStringFactory {
|
||||
ByteString newByteString(byte[] bytes);
|
||||
}
|
||||
|
||||
static final ByteStringFactory LITERAL_FACTORY = new ByteStringFactory() {
|
||||
@Override
|
||||
public ByteString newByteString(byte[] bytes) {
|
||||
return ByteString.wrap(bytes);
|
||||
}
|
||||
};
|
||||
|
||||
static final ByteStringFactory HEAP_NIO_FACTORY = new ByteStringFactory() {
|
||||
@Override
|
||||
public ByteString newByteString(byte[] bytes) {
|
||||
return new NioByteString(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
};
|
||||
|
||||
private static ThreadLocal<SoftReference<ByteBuffer>> directBuffer =
|
||||
new ThreadLocal<SoftReference<ByteBuffer>>();
|
||||
|
||||
/**
|
||||
* Factory for direct {@link ByteBuffer} instances. To reduce direct memory usage, this
|
||||
* uses a thread local direct buffer. This means that each call will overwrite the buffer's
|
||||
* contents from the previous call, so the calling code must be careful not to continue using
|
||||
* a buffer returned from a previous invocation.
|
||||
*/
|
||||
static final ByteStringFactory DIRECT_NIO_FACTORY = new ByteStringFactory() {
|
||||
@Override
|
||||
public ByteString newByteString(byte[] bytes) {
|
||||
SoftReference<ByteBuffer> ref = directBuffer.get();
|
||||
ByteBuffer buffer = ref == null ? null : ref.get();
|
||||
if (buffer == null || buffer.capacity() < bytes.length) {
|
||||
buffer = ByteBuffer.allocateDirect(bytes.length);
|
||||
directBuffer.set(new SoftReference<ByteBuffer>(buffer));
|
||||
}
|
||||
buffer.clear();
|
||||
buffer.put(bytes);
|
||||
buffer.flip();
|
||||
return new NioByteString(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
// 128 - [chars 0x0000 to 0x007f]
|
||||
static long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
|
||||
static final long ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x007f - 0x0000 + 1;
|
||||
|
||||
// 128
|
||||
static long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
static final long EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT = ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1920 [chars 0x0080 to 0x07FF]
|
||||
static long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
|
||||
static final long TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x07FF - 0x0080 + 1;
|
||||
|
||||
// 18,304
|
||||
static long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
static final long EXPECTED_TWO_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// Both bytes are one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 2) +
|
||||
// The possible number of two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 2048
|
||||
static long THREE_BYTE_SURROGATES = 2 * 1024;
|
||||
static final long THREE_BYTE_SURROGATES = 2 * 1024;
|
||||
|
||||
// 61,440 [chars 0x0800 to 0xFFFF, minus surrogates]
|
||||
static long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
|
||||
static final long THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS =
|
||||
0xFFFF - 0x0800 + 1 - THREE_BYTE_SURROGATES;
|
||||
|
||||
// 2,650,112
|
||||
static long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
static final long EXPECTED_THREE_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 3) +
|
||||
// One two byte character and a one byte character
|
||||
2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Three byte characters
|
||||
2 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Three byte characters
|
||||
THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
// 1,048,576 [chars 0x10000L to 0x10FFFF]
|
||||
static long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
|
||||
static final long FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS = 0x10FFFF - 0x10000L + 1;
|
||||
|
||||
// 289,571,839
|
||||
static long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
static final long EXPECTED_FOUR_BYTE_ROUNDTRIPPABLE_COUNT =
|
||||
// All one byte characters
|
||||
(long) Math.pow(EXPECTED_ONE_BYTE_ROUNDTRIPPABLE_COUNT, 4) +
|
||||
// One and three byte characters
|
||||
2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
2 * THREE_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Two two byte characters
|
||||
TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
// Permutations of one and two byte characters
|
||||
3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS *
|
||||
ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS +
|
||||
3 * TWO_BYTE_ROUNDTRIPPABLE_CHARACTERS * ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS
|
||||
* ONE_BYTE_ROUNDTRIPPABLE_CHARACTERS
|
||||
+
|
||||
// Four byte characters
|
||||
FOUR_BYTE_ROUNDTRIPPABLE_CHARACTERS;
|
||||
|
||||
static class Shard {
|
||||
static final class Shard {
|
||||
final long index;
|
||||
final long start;
|
||||
final long lim;
|
||||
@ -138,7 +183,7 @@ class IsValidUtf8TestUtil {
|
||||
|
||||
// 97-111 are all 2342912
|
||||
for (int i = 97; i <= 111; i++) {
|
||||
expected[i] = 2342912;
|
||||
expected[i] = 2342912;
|
||||
}
|
||||
|
||||
// 113-117 are all 1048576
|
||||
@ -158,22 +203,18 @@ class IsValidUtf8TestUtil {
|
||||
return expected;
|
||||
}
|
||||
|
||||
static final List<Shard> FOUR_BYTE_SHARDS = generateFourByteShards(
|
||||
128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
|
||||
static final List<Shard> FOUR_BYTE_SHARDS =
|
||||
generateFourByteShards(128, FOUR_BYTE_SHARDS_EXPECTED_ROUNTRIPPABLES);
|
||||
|
||||
|
||||
private static List<Shard> generateFourByteShards(
|
||||
int numShards, long[] expected) {
|
||||
private static List<Shard> generateFourByteShards(int numShards, long[] expected) {
|
||||
assertEquals(numShards, expected.length);
|
||||
List<Shard> shards = new ArrayList<Shard>(numShards);
|
||||
long LIM = 1L << 32;
|
||||
long increment = LIM / numShards;
|
||||
assertTrue(LIM % numShards == 0);
|
||||
for (int i = 0; i < numShards; i++) {
|
||||
shards.add(new Shard(i,
|
||||
increment * i,
|
||||
increment * (i + 1),
|
||||
expected[i]));
|
||||
shards.add(new Shard(i, increment * i, increment * (i + 1), expected[i]));
|
||||
}
|
||||
return shards;
|
||||
}
|
||||
@ -182,12 +223,12 @@ class IsValidUtf8TestUtil {
|
||||
* Helper to run the loop to test all the permutations for the number of bytes
|
||||
* specified.
|
||||
*
|
||||
* @param factory the factory for {@link ByteString} instances.
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount)
|
||||
throws UnsupportedEncodingException {
|
||||
testBytes(numBytes, expectedCount, 0, -1);
|
||||
static void testBytes(ByteStringFactory factory, int numBytes, long expectedCount) {
|
||||
testBytes(factory, numBytes, expectedCount, 0, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,14 +236,15 @@ class IsValidUtf8TestUtil {
|
||||
* specified. This overload is useful for debugging to get the loop to start
|
||||
* at a certain character.
|
||||
*
|
||||
* @param factory the factory for {@link ByteString} instances.
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
static void testBytes(int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
static void testBytes(
|
||||
ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) {
|
||||
Random rnd = new Random();
|
||||
byte[] bytes = new byte[numBytes];
|
||||
|
||||
@ -217,7 +259,7 @@ class IsValidUtf8TestUtil {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
ByteString bs = ByteString.copyFrom(bytes);
|
||||
ByteString bs = factory.newByteString(bytes);
|
||||
boolean isRoundTrippable = bs.isValidUtf8();
|
||||
String s = new String(bytes, Internal.UTF_8);
|
||||
byte[] bytesReencoded = s.getBytes(Internal.UTF_8);
|
||||
@ -236,14 +278,15 @@ class IsValidUtf8TestUtil {
|
||||
int i = rnd.nextInt(numBytes);
|
||||
int j = rnd.nextInt(numBytes);
|
||||
if (j < i) {
|
||||
int tmp = i; i = j; j = tmp;
|
||||
int tmp = i;
|
||||
i = j;
|
||||
j = tmp;
|
||||
}
|
||||
int state1 = Utf8.partialIsValidUtf8(Utf8.COMPLETE, bytes, 0, i);
|
||||
int state2 = Utf8.partialIsValidUtf8(state1, bytes, i, j);
|
||||
int state3 = Utf8.partialIsValidUtf8(state2, bytes, j, numBytes);
|
||||
if (isRoundTrippable != (state3 == Utf8.COMPLETE)) {
|
||||
System.out.printf("state=%04x %04x %04x i=%d j=%d%n",
|
||||
state1, state2, state3, i, j);
|
||||
System.out.printf("state=%04x %04x %04x i=%d j=%d%n", state1, state2, state3, i, j);
|
||||
outputFailure(byteChar, bytes, bytesReencoded);
|
||||
}
|
||||
assertEquals(isRoundTrippable, (state3 == Utf8.COMPLETE));
|
||||
@ -251,36 +294,24 @@ class IsValidUtf8TestUtil {
|
||||
// Test ropes built out of small partial sequences
|
||||
ByteString rope = RopeByteString.newInstanceForTest(
|
||||
bs.substring(0, i),
|
||||
RopeByteString.newInstanceForTest(
|
||||
bs.substring(i, j),
|
||||
bs.substring(j, numBytes)));
|
||||
RopeByteString.newInstanceForTest(bs.substring(i, j), bs.substring(j, numBytes)));
|
||||
assertSame(RopeByteString.class, rope.getClass());
|
||||
|
||||
ByteString[] byteStrings = { bs, bs.substring(0, numBytes), rope };
|
||||
ByteString[] byteStrings = {bs, bs.substring(0, numBytes), rope};
|
||||
for (ByteString x : byteStrings) {
|
||||
assertEquals(isRoundTrippable,
|
||||
x.isValidUtf8());
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
|
||||
assertEquals(isRoundTrippable, x.isValidUtf8());
|
||||
assertEquals(state3, x.partialIsValidUtf8(Utf8.COMPLETE, 0, numBytes));
|
||||
|
||||
assertEquals(state1,
|
||||
x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state1,
|
||||
x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state2,
|
||||
x.partialIsValidUtf8(state1, i, j - i));
|
||||
assertEquals(state2,
|
||||
x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
|
||||
assertEquals(state3,
|
||||
x.partialIsValidUtf8(state2, j, numBytes - j));
|
||||
assertEquals(state3,
|
||||
x.substring(j, numBytes)
|
||||
.partialIsValidUtf8(state2, 0, numBytes - j));
|
||||
assertEquals(state1, x.partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state1, x.substring(0, i).partialIsValidUtf8(Utf8.COMPLETE, 0, i));
|
||||
assertEquals(state2, x.partialIsValidUtf8(state1, i, j - i));
|
||||
assertEquals(state2, x.substring(i, j).partialIsValidUtf8(state1, 0, j - i));
|
||||
assertEquals(state3, x.partialIsValidUtf8(state2, j, numBytes - j));
|
||||
assertEquals(state3, x.substring(j, numBytes).partialIsValidUtf8(state2, 0, numBytes - j));
|
||||
}
|
||||
|
||||
// ByteString reduplication should not affect its UTF-8 validity.
|
||||
ByteString ropeADope =
|
||||
RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
|
||||
ByteString ropeADope = RopeByteString.newInstanceForTest(bs, bs.substring(0, numBytes));
|
||||
assertEquals(isRoundTrippable, ropeADope.isValidUtf8());
|
||||
|
||||
if (isRoundTrippable) {
|
||||
@ -288,8 +319,7 @@ class IsValidUtf8TestUtil {
|
||||
}
|
||||
count++;
|
||||
if (byteChar != 0 && byteChar % 1000000L == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000L) +
|
||||
" million characters");
|
||||
logger.info("Processed " + (byteChar / 1000000L) + " million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
@ -303,25 +333,26 @@ class IsValidUtf8TestUtil {
|
||||
* actual String class, it's possible for incompatibilities to develop
|
||||
* (although unlikely).
|
||||
*
|
||||
* @param factory the factory for {@link ByteString} instances.
|
||||
* @param numBytes the number of bytes in the byte array
|
||||
* @param expectedCount the expected number of roundtrippable permutations
|
||||
* @param start the starting bytes encoded as a long as big-endian
|
||||
* @param lim the limit of bytes to process encoded as a long as big-endian,
|
||||
* or -1 to mean the max limit for numBytes
|
||||
*/
|
||||
void testBytesUsingByteBuffers(
|
||||
int numBytes, long expectedCount, long start, long lim)
|
||||
throws UnsupportedEncodingException {
|
||||
CharsetDecoder decoder = Internal.UTF_8.newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
CharsetEncoder encoder = Internal.UTF_8.newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
static void testBytesUsingByteBuffers(
|
||||
ByteStringFactory factory, int numBytes, long expectedCount, long start, long lim) {
|
||||
CharsetDecoder decoder =
|
||||
Internal.UTF_8.newDecoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
CharsetEncoder encoder =
|
||||
Internal.UTF_8.newEncoder()
|
||||
.onMalformedInput(CodingErrorAction.REPLACE)
|
||||
.onUnmappableCharacter(CodingErrorAction.REPLACE);
|
||||
byte[] bytes = new byte[numBytes];
|
||||
int maxChars = (int) (decoder.maxCharsPerByte() * numBytes) + 1;
|
||||
char[] charsDecoded =
|
||||
new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
|
||||
char[] charsDecoded = new char[(int) (decoder.maxCharsPerByte() * numBytes) + 1];
|
||||
int maxBytes = (int) (encoder.maxBytesPerChar() * maxChars) + 1;
|
||||
byte[] bytesReencoded = new byte[maxBytes];
|
||||
|
||||
@ -347,7 +378,7 @@ class IsValidUtf8TestUtil {
|
||||
bytes[bytes.length - i - 1] = (byte) tmpByteChar;
|
||||
tmpByteChar = tmpByteChar >> 8;
|
||||
}
|
||||
boolean isRoundTrippable = ByteString.copyFrom(bytes).isValidUtf8();
|
||||
boolean isRoundTrippable = factory.newByteString(bytes).isValidUtf8();
|
||||
CoderResult result = decoder.decode(bb, cb, true);
|
||||
assertFalse(result.isError());
|
||||
result = decoder.flush(cb);
|
||||
@ -382,8 +413,7 @@ class IsValidUtf8TestUtil {
|
||||
countRoundTripped++;
|
||||
}
|
||||
if (byteChar != 0 && byteChar % 1000000 == 0) {
|
||||
logger.info("Processed " + (byteChar / 1000000) +
|
||||
" million characters");
|
||||
logger.info("Processed " + (byteChar / 1000000) + " million characters");
|
||||
}
|
||||
}
|
||||
logger.info("Round tripped " + countRoundTripped + " of " + count);
|
||||
@ -394,10 +424,9 @@ class IsValidUtf8TestUtil {
|
||||
outputFailure(byteChar, bytes, after, after.length);
|
||||
}
|
||||
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after,
|
||||
int len) {
|
||||
fail("Failure: (" + Long.toHexString(byteChar) + ") " +
|
||||
toHexString(bytes) + " => " + toHexString(after, len));
|
||||
private static void outputFailure(long byteChar, byte[] bytes, byte[] after, int len) {
|
||||
fail("Failure: (" + Long.toHexString(byteChar) + ") " + toHexString(bytes) + " => "
|
||||
+ toHexString(after, len));
|
||||
}
|
||||
|
||||
private static String toHexString(byte[] b) {
|
||||
@ -416,5 +445,4 @@ class IsValidUtf8TestUtil {
|
||||
s.append("\"");
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -36,9 +36,10 @@ import static protobuf_unittest.UnittestProto.optionalInt64Extension;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
|
||||
import java.io.IOException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link LazyFieldLite}.
|
||||
*
|
||||
|
@ -33,9 +33,10 @@ package com.google.protobuf;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
|
||||
import java.io.IOException;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Unit test for {@link LazyField}.
|
||||
*
|
||||
|
@ -33,6 +33,8 @@ package com.google.protobuf;
|
||||
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar;
|
||||
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime;
|
||||
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo;
|
||||
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals;
|
||||
import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
@ -83,6 +85,16 @@ public class LiteEqualsAndHashTest extends TestCase {
|
||||
assertFalse(bar.equals(barPrime));
|
||||
}
|
||||
|
||||
public void testOneofEquals() throws Exception {
|
||||
TestOneofEquals.Builder builder = TestOneofEquals.newBuilder();
|
||||
TestOneofEquals message1 = builder.build();
|
||||
// Set message2's name field to default value. The two messages should be different when we
|
||||
// check with the oneof case.
|
||||
builder.setName("");
|
||||
TestOneofEquals message2 = builder.build();
|
||||
assertFalse(message1.equals(message2));
|
||||
}
|
||||
|
||||
public void testEqualsAndHashCodeWithUnknownFields() throws InvalidProtocolBufferException {
|
||||
Foo fooWithOnlyValue = Foo.newBuilder()
|
||||
.setValue(1)
|
||||
@ -105,4 +117,9 @@ public class LiteEqualsAndHashTest extends TestCase {
|
||||
assertFalse(o1.equals(o2));
|
||||
assertFalse(o1.hashCode() == o2.hashCode());
|
||||
}
|
||||
|
||||
public void testRecursiveHashcode() {
|
||||
// This tests that we don't infinite loop.
|
||||
TestRecursiveOneof.getDefaultInstance().hashCode();
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.NotSerializableException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
|
||||
@ -128,33 +129,7 @@ public class LiteTest extends TestCase {
|
||||
assertEquals(7, message2.getExtension(
|
||||
UnittestLite.optionalNestedMessageExtensionLite).getBb());
|
||||
}
|
||||
|
||||
public void testSerialize() throws Exception {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
TestAllTypesLite expected =
|
||||
TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(123)
|
||||
.addRepeatedString("hello")
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
ObjectOutputStream out = new ObjectOutputStream(baos);
|
||||
try {
|
||||
out.writeObject(expected);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ObjectInputStream in = new ObjectInputStream(bais);
|
||||
TestAllTypesLite actual = (TestAllTypesLite) in.readObject();
|
||||
assertEquals(expected.getOptionalInt32(), actual.getOptionalInt32());
|
||||
assertEquals(expected.getRepeatedStringCount(),
|
||||
actual.getRepeatedStringCount());
|
||||
assertEquals(expected.getRepeatedString(0),
|
||||
actual.getRepeatedString(0));
|
||||
assertEquals(expected.getOptionalNestedMessage().getBb(),
|
||||
actual.getOptionalNestedMessage().getBb());
|
||||
}
|
||||
|
||||
|
||||
public void testClone() {
|
||||
TestAllTypesLite.Builder expected = TestAllTypesLite.newBuilder()
|
||||
@ -1459,4 +1434,168 @@ public class LiteTest extends TestCase {
|
||||
11, (int) extendableMessage.getExtension(
|
||||
UnittestLite.optionalFixed32ExtensionLite));
|
||||
}
|
||||
|
||||
public void testToStringDefaultInstance() throws Exception {
|
||||
assertToStringEquals("", TestAllTypesLite.getDefaultInstance());
|
||||
}
|
||||
|
||||
public void testToStringPrimitives() throws Exception {
|
||||
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalInt32(1)
|
||||
.setOptionalInt64(9223372036854775807L)
|
||||
.build();
|
||||
assertToStringEquals("optional_int32: 1\noptional_int64: 9223372036854775807", proto);
|
||||
|
||||
proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalBool(true)
|
||||
.setOptionalNestedEnum(TestAllTypesLite.NestedEnum.BAZ)
|
||||
.build();
|
||||
assertToStringEquals("optional_bool: true\noptional_nested_enum: BAZ", proto);
|
||||
|
||||
proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalFloat(2.72f)
|
||||
.setOptionalDouble(3.14)
|
||||
.build();
|
||||
assertToStringEquals("optional_float: 2.72\noptional_double: 3.14", proto);
|
||||
}
|
||||
|
||||
public void testToStringStringFields() throws Exception {
|
||||
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalString("foo\"bar\nbaz\\")
|
||||
.build();
|
||||
assertToStringEquals("optional_string: \"foo\\\"bar\\nbaz\\\\\"", proto);
|
||||
|
||||
proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalString("\u6587")
|
||||
.build();
|
||||
assertToStringEquals("optional_string: \"\\346\\226\\207\"", proto);
|
||||
}
|
||||
|
||||
public void testToStringNestedMessage() throws Exception {
|
||||
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalNestedMessage(TestAllTypesLite.NestedMessage.getDefaultInstance())
|
||||
.build();
|
||||
assertToStringEquals("optional_nested_message {\n}", proto);
|
||||
|
||||
proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalNestedMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.build();
|
||||
assertToStringEquals("optional_nested_message {\n bb: 7\n}", proto);
|
||||
}
|
||||
|
||||
public void testToStringRepeatedFields() throws Exception {
|
||||
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
|
||||
.addRepeatedInt32(32)
|
||||
.addRepeatedInt32(32)
|
||||
.addRepeatedInt64(64)
|
||||
.build();
|
||||
assertToStringEquals("repeated_int32: 32\nrepeated_int32: 32\nrepeated_int64: 64", proto);
|
||||
|
||||
proto = TestAllTypesLite.newBuilder()
|
||||
.addRepeatedLazyMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7))
|
||||
.addRepeatedLazyMessage(
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(8))
|
||||
.build();
|
||||
assertToStringEquals(
|
||||
"repeated_lazy_message {\n bb: 7\n}\nrepeated_lazy_message {\n bb: 8\n}",
|
||||
proto);
|
||||
}
|
||||
|
||||
public void testToStringForeignFields() throws Exception {
|
||||
TestAllTypesLite proto = TestAllTypesLite.newBuilder()
|
||||
.setOptionalForeignEnum(ForeignEnumLite.FOREIGN_LITE_BAR)
|
||||
.setOptionalForeignMessage(
|
||||
ForeignMessageLite.newBuilder()
|
||||
.setC(3))
|
||||
.build();
|
||||
assertToStringEquals(
|
||||
"optional_foreign_message {\n c: 3\n}\noptional_foreign_enum: FOREIGN_LITE_BAR",
|
||||
proto);
|
||||
}
|
||||
|
||||
public void testToStringExtensions() throws Exception {
|
||||
TestAllExtensionsLite message = TestAllExtensionsLite.newBuilder()
|
||||
.setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "spam")
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "eggs")
|
||||
.setExtension(UnittestLite.optionalNestedEnumExtensionLite,
|
||||
TestAllTypesLite.NestedEnum.BAZ)
|
||||
.setExtension(UnittestLite.optionalNestedMessageExtensionLite,
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
|
||||
.build();
|
||||
assertToStringEquals(
|
||||
"[1]: 123\n[18] {\n bb: 7\n}\n[21]: 3\n[44]: \"spam\"\n[44]: \"eggs\"",
|
||||
message);
|
||||
}
|
||||
|
||||
public void testToStringUnknownFields() throws Exception {
|
||||
TestAllExtensionsLite messageWithExtensions = TestAllExtensionsLite.newBuilder()
|
||||
.setExtension(UnittestLite.optionalInt32ExtensionLite, 123)
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "spam")
|
||||
.addExtension(UnittestLite.repeatedStringExtensionLite, "eggs")
|
||||
.setExtension(UnittestLite.optionalNestedEnumExtensionLite,
|
||||
TestAllTypesLite.NestedEnum.BAZ)
|
||||
.setExtension(UnittestLite.optionalNestedMessageExtensionLite,
|
||||
TestAllTypesLite.NestedMessage.newBuilder().setBb(7).build())
|
||||
.build();
|
||||
TestAllExtensionsLite messageWithUnknownFields = TestAllExtensionsLite.parseFrom(
|
||||
messageWithExtensions.toByteArray());
|
||||
assertToStringEquals(
|
||||
"1: 123\n18: \"\\b\\a\"\n21: 3\n44: \"spam\"\n44: \"eggs\"",
|
||||
messageWithUnknownFields);
|
||||
}
|
||||
|
||||
// Asserts that the toString() representation of the message matches the expected. This verifies
|
||||
// the first line starts with a comment; but, does not factor in said comment as part of the
|
||||
// comparison as it contains unstable addresses.
|
||||
private static void assertToStringEquals(String expected, MessageLite message) {
|
||||
String toString = message.toString();
|
||||
assertEquals('#', toString.charAt(0));
|
||||
if (toString.indexOf("\n") >= 0) {
|
||||
toString = toString.substring(toString.indexOf("\n") + 1);
|
||||
} else {
|
||||
toString = "";
|
||||
}
|
||||
assertEquals(expected, toString);
|
||||
}
|
||||
|
||||
public void testParseLazy() throws Exception {
|
||||
ByteString bb = TestAllTypesLite.newBuilder()
|
||||
.setOptionalLazyMessage(NestedMessage.newBuilder()
|
||||
.setBb(11)
|
||||
.build())
|
||||
.build().toByteString();
|
||||
ByteString cc = TestAllTypesLite.newBuilder()
|
||||
.setOptionalLazyMessage(NestedMessage.newBuilder()
|
||||
.setCc(22)
|
||||
.build())
|
||||
.build().toByteString();
|
||||
|
||||
ByteString concat = bb.concat(cc);
|
||||
TestAllTypesLite message = TestAllTypesLite.parseFrom(concat);
|
||||
|
||||
assertEquals(11, message.getOptionalLazyMessage().getBb());
|
||||
assertEquals(22L, message.getOptionalLazyMessage().getCc());
|
||||
}
|
||||
|
||||
public void testParseLazy_oneOf() throws Exception {
|
||||
ByteString bb = TestAllTypesLite.newBuilder()
|
||||
.setOneofLazyNestedMessage(NestedMessage.newBuilder()
|
||||
.setBb(11)
|
||||
.build())
|
||||
.build().toByteString();
|
||||
ByteString cc = TestAllTypesLite.newBuilder()
|
||||
.setOneofLazyNestedMessage(NestedMessage.newBuilder()
|
||||
.setCc(22)
|
||||
.build())
|
||||
.build().toByteString();
|
||||
|
||||
ByteString concat = bb.concat(cc);
|
||||
TestAllTypesLite message = TestAllTypesLite.parseFrom(concat);
|
||||
|
||||
assertEquals(11, message.getOneofLazyNestedMessage().getBb());
|
||||
assertEquals(22L, message.getOneofLazyNestedMessage().getCc());
|
||||
}
|
||||
}
|
||||
|
@ -47,9 +47,9 @@ import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Test {@link LiteralByteString} by setting up a reference string in {@link #setUp()}.
|
||||
* This class is designed to be extended for testing extensions of {@link LiteralByteString}
|
||||
* such as {@link BoundedByteString}, see {@link BoundedByteStringTest}.
|
||||
* Test {@code LiteralByteString} by setting up a reference string in {@link #setUp()}.
|
||||
* This class is designed to be extended for testing extensions of {@code LiteralByteString}
|
||||
* such as {@code BoundedByteString}, see {@link BoundedByteStringTest}.
|
||||
*
|
||||
* @author carlanton@google.com (Carl Haverl)
|
||||
*/
|
||||
@ -304,25 +304,75 @@ public class LiteralByteStringTest extends TestCase {
|
||||
Arrays.equals(referenceBytes, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo_mutating() throws IOException {
|
||||
public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
for (int x = 0; x < len; ++x) {
|
||||
b[off + x] = (byte) 0;
|
||||
}
|
||||
Arrays.fill(b, off, off + len, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
// Purposefully left blank.
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
stringUnderTest.writeTo(os);
|
||||
byte[] newBytes = stringUnderTest.toByteArray();
|
||||
assertTrue(classUnderTest + ".writeTo() must not grant access to underlying array",
|
||||
Arrays.equals(referenceBytes, newBytes));
|
||||
Arrays.equals(referenceBytes, stringUnderTest.toByteArray()));
|
||||
}
|
||||
|
||||
public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
Arrays.fill(b, off, off + len, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
stringUnderTest.writeToInternal(os, 0, stringUnderTest.size());
|
||||
byte[] allZeros = new byte[stringUnderTest.size()];
|
||||
assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array",
|
||||
Arrays.equals(allZeros, stringUnderTest.toByteArray()));
|
||||
}
|
||||
|
||||
public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException {
|
||||
ByteOutput out = new ByteOutput() {
|
||||
@Override
|
||||
public void write(byte value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] value, int offset, int length) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLazy(byte[] value, int offset, int length) throws IOException {
|
||||
Arrays.fill(value, offset, offset + length, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLazy(ByteBuffer value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
stringUnderTest.writeTo(out);
|
||||
byte[] allZeros = new byte[stringUnderTest.size()];
|
||||
assertTrue(classUnderTest + ".writeToInternal() must grant access to underlying array",
|
||||
Arrays.equals(allZeros, stringUnderTest.toByteArray()));
|
||||
}
|
||||
|
||||
public void testNewOutput() throws IOException {
|
||||
|
@ -432,7 +432,7 @@ public class MapForProto2LiteTest extends TestCase {
|
||||
assertEquals(1, messageWithUnknownEnums.getInt32ToInt32Field().get(1).intValue());
|
||||
assertEquals(54321, messageWithUnknownEnums.getInt32ToInt32Field().get(2).intValue());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void testIterationOrder() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
|
@ -36,7 +36,6 @@ import map_test.MapForProto2TestProto.TestMap.MessageValue;
|
||||
import map_test.MapForProto2TestProto.TestMap.MessageWithRequiredFields;
|
||||
import map_test.MapForProto2TestProto.TestRecursiveMap;
|
||||
import map_test.MapForProto2TestProto.TestUnknownEnumValue;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -37,7 +37,6 @@ import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import map_test.MapTestProto.TestMap;
|
||||
import map_test.MapTestProto.TestMap.MessageValue;
|
||||
import map_test.MapTestProto.TestOnChangeEventPropagation;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -30,11 +30,11 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto.TestRequiredForeign;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
@ -35,8 +35,8 @@ import protobuf_unittest.Wheel;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test cases that exercise end-to-end use cases involving
|
||||
|
@ -41,6 +41,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.BufferOverflowException;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -56,11 +57,12 @@ public class NioByteStringTest extends TestCase {
|
||||
private static final String CLASSNAME = NioByteString.class.getSimpleName();
|
||||
private static final byte[] BYTES = ByteStringTest.getTestBytes(1234, 11337766L);
|
||||
private static final int EXPECTED_HASH = ByteString.wrap(BYTES).hashCode();
|
||||
private static final ByteBuffer BUFFER = ByteBuffer.wrap(BYTES.clone());
|
||||
private static final ByteString TEST_STRING = new NioByteString(BUFFER);
|
||||
|
||||
private final ByteBuffer backingBuffer = ByteBuffer.wrap(BYTES.clone());
|
||||
private final ByteString testString = new NioByteString(backingBuffer);
|
||||
|
||||
public void testExpectedType() {
|
||||
String actualClassName = getActualClassName(TEST_STRING);
|
||||
String actualClassName = getActualClassName(testString);
|
||||
assertEquals(CLASSNAME + " should match type exactly", CLASSNAME, actualClassName);
|
||||
}
|
||||
|
||||
@ -73,14 +75,14 @@ public class NioByteStringTest extends TestCase {
|
||||
public void testByteAt() {
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < BYTES.length; ++i) {
|
||||
stillEqual = (BYTES[i] == TEST_STRING.byteAt(i));
|
||||
stillEqual = (BYTES[i] == testString.byteAt(i));
|
||||
}
|
||||
assertTrue(CLASSNAME + " must capture the right bytes", stillEqual);
|
||||
}
|
||||
|
||||
public void testByteIterator() {
|
||||
boolean stillEqual = true;
|
||||
ByteString.ByteIterator iter = TEST_STRING.iterator();
|
||||
ByteString.ByteIterator iter = testString.iterator();
|
||||
for (int i = 0; stillEqual && i < BYTES.length; ++i) {
|
||||
stillEqual = (iter.hasNext() && BYTES[i] == iter.nextByte());
|
||||
}
|
||||
@ -98,7 +100,7 @@ public class NioByteStringTest extends TestCase {
|
||||
public void testByteIterable() {
|
||||
boolean stillEqual = true;
|
||||
int j = 0;
|
||||
for (byte quantum : TEST_STRING) {
|
||||
for (byte quantum : testString) {
|
||||
stillEqual = (BYTES[j] == quantum);
|
||||
++j;
|
||||
}
|
||||
@ -108,15 +110,15 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
public void testSize() {
|
||||
assertEquals(CLASSNAME + " must have the expected size", BYTES.length,
|
||||
TEST_STRING.size());
|
||||
testString.size());
|
||||
}
|
||||
|
||||
public void testGetTreeDepth() {
|
||||
assertEquals(CLASSNAME + " must have depth 0", 0, TEST_STRING.getTreeDepth());
|
||||
assertEquals(CLASSNAME + " must have depth 0", 0, testString.getTreeDepth());
|
||||
}
|
||||
|
||||
public void testIsBalanced() {
|
||||
assertTrue(CLASSNAME + " is technically balanced", TEST_STRING.isBalanced());
|
||||
assertTrue(CLASSNAME + " is technically balanced", testString.isBalanced());
|
||||
}
|
||||
|
||||
public void testCopyTo_ByteArrayOffsetLength() {
|
||||
@ -124,7 +126,7 @@ public class NioByteStringTest extends TestCase {
|
||||
int length = 100;
|
||||
byte[] destination = new byte[destinationOffset + length];
|
||||
int sourceOffset = 213;
|
||||
TEST_STRING.copyTo(destination, sourceOffset, destinationOffset, length);
|
||||
testString.copyTo(destination, sourceOffset, destinationOffset, length);
|
||||
boolean stillEqual = true;
|
||||
for (int i = 0; stillEqual && i < length; ++i) {
|
||||
stillEqual = BYTES[i + sourceOffset] == destination[i + destinationOffset];
|
||||
@ -139,7 +141,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy one too many bytes
|
||||
TEST_STRING.copyTo(destination, TEST_STRING.size() + 1 - length,
|
||||
testString.copyTo(destination, testString.size() + 1 - length,
|
||||
destinationOffset, length);
|
||||
fail("Should have thrown an exception when copying too many bytes of a "
|
||||
+ CLASSNAME);
|
||||
@ -149,7 +151,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy with illegal negative sourceOffset
|
||||
TEST_STRING.copyTo(destination, -1, destinationOffset, length);
|
||||
testString.copyTo(destination, -1, destinationOffset, length);
|
||||
fail("Should have thrown an exception when given a negative sourceOffset in "
|
||||
+ CLASSNAME);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
@ -158,7 +160,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy with illegal negative destinationOffset
|
||||
TEST_STRING.copyTo(destination, 0, -1, length);
|
||||
testString.copyTo(destination, 0, -1, length);
|
||||
fail("Should have thrown an exception when given a negative destinationOffset in "
|
||||
+ CLASSNAME);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
@ -167,7 +169,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy with illegal negative size
|
||||
TEST_STRING.copyTo(destination, 0, 0, -1);
|
||||
testString.copyTo(destination, 0, 0, -1);
|
||||
fail("Should have thrown an exception when given a negative size in "
|
||||
+ CLASSNAME);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
@ -176,7 +178,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large sourceOffset
|
||||
TEST_STRING.copyTo(destination, 2 * TEST_STRING.size(), 0, length);
|
||||
testString.copyTo(destination, 2 * testString.size(), 0, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ CLASSNAME);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
@ -185,7 +187,7 @@ public class NioByteStringTest extends TestCase {
|
||||
|
||||
try {
|
||||
// Copy with illegal too-large destinationOffset
|
||||
TEST_STRING.copyTo(destination, 0, 2 * destination.length, length);
|
||||
testString.copyTo(destination, 0, 2 * destination.length, length);
|
||||
fail("Should have thrown an exception when the destinationOffset is too large in "
|
||||
+ CLASSNAME);
|
||||
} catch (IndexOutOfBoundsException expected) {
|
||||
@ -196,21 +198,21 @@ public class NioByteStringTest extends TestCase {
|
||||
public void testCopyTo_ByteBuffer() {
|
||||
// Same length.
|
||||
ByteBuffer myBuffer = ByteBuffer.allocate(BYTES.length);
|
||||
TEST_STRING.copyTo(myBuffer);
|
||||
testString.copyTo(myBuffer);
|
||||
myBuffer.flip();
|
||||
assertEquals(CLASSNAME + ".copyTo(ByteBuffer) must give back the same bytes",
|
||||
BUFFER, myBuffer);
|
||||
backingBuffer, myBuffer);
|
||||
|
||||
// Target buffer bigger than required.
|
||||
myBuffer = ByteBuffer.allocate(TEST_STRING.size() + 1);
|
||||
TEST_STRING.copyTo(myBuffer);
|
||||
myBuffer = ByteBuffer.allocate(testString.size() + 1);
|
||||
testString.copyTo(myBuffer);
|
||||
myBuffer.flip();
|
||||
assertEquals(BUFFER, myBuffer);
|
||||
assertEquals(backingBuffer, myBuffer);
|
||||
|
||||
// Target buffer has no space.
|
||||
myBuffer = ByteBuffer.allocate(0);
|
||||
try {
|
||||
TEST_STRING.copyTo(myBuffer);
|
||||
testString.copyTo(myBuffer);
|
||||
fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
|
||||
} catch (BufferOverflowException e) {
|
||||
// Expected.
|
||||
@ -219,7 +221,7 @@ public class NioByteStringTest extends TestCase {
|
||||
// Target buffer too small.
|
||||
myBuffer = ByteBuffer.allocate(1);
|
||||
try {
|
||||
TEST_STRING.copyTo(myBuffer);
|
||||
testString.copyTo(myBuffer);
|
||||
fail("Should have thrown an exception when target ByteBuffer has insufficient capacity");
|
||||
} catch (BufferOverflowException e) {
|
||||
// Expected.
|
||||
@ -227,26 +229,26 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testMarkSupported() {
|
||||
InputStream stream = TEST_STRING.newInput();
|
||||
InputStream stream = testString.newInput();
|
||||
assertTrue(CLASSNAME + ".newInput() must support marking", stream.markSupported());
|
||||
}
|
||||
|
||||
public void testMarkAndReset() throws IOException {
|
||||
int fraction = TEST_STRING.size() / 3;
|
||||
int fraction = testString.size() / 3;
|
||||
|
||||
InputStream stream = TEST_STRING.newInput();
|
||||
stream.mark(TEST_STRING.size()); // First, mark() the end.
|
||||
InputStream stream = testString.newInput();
|
||||
stream.mark(testString.size()); // First, mark() the end.
|
||||
|
||||
skipFully(stream, fraction); // Skip a large fraction, but not all.
|
||||
assertEquals(
|
||||
CLASSNAME + ": after skipping to the 'middle', half the bytes are available",
|
||||
(TEST_STRING.size() - fraction), stream.available());
|
||||
(testString.size() - fraction), stream.available());
|
||||
stream.reset();
|
||||
assertEquals(
|
||||
CLASSNAME + ": after resetting, all bytes are available",
|
||||
TEST_STRING.size(), stream.available());
|
||||
testString.size(), stream.available());
|
||||
|
||||
skipFully(stream, TEST_STRING.size()); // Skip to the end.
|
||||
skipFully(stream, testString.size()); // Skip to the end.
|
||||
assertEquals(
|
||||
CLASSNAME + ": after skipping to the end, no more bytes are available",
|
||||
0, stream.available());
|
||||
@ -284,7 +286,7 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBuffer() {
|
||||
ByteBuffer byteBuffer = TEST_STRING.asReadOnlyByteBuffer();
|
||||
ByteBuffer byteBuffer = testString.asReadOnlyByteBuffer();
|
||||
byte[] roundTripBytes = new byte[BYTES.length];
|
||||
assertTrue(byteBuffer.remaining() == BYTES.length);
|
||||
assertTrue(byteBuffer.isReadOnly());
|
||||
@ -294,7 +296,7 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testAsReadOnlyByteBufferList() {
|
||||
List<ByteBuffer> byteBuffers = TEST_STRING.asReadOnlyByteBufferList();
|
||||
List<ByteBuffer> byteBuffers = testString.asReadOnlyByteBufferList();
|
||||
int bytesSeen = 0;
|
||||
byte[] roundTripBytes = new byte[BYTES.length];
|
||||
for (ByteBuffer byteBuffer : byteBuffers) {
|
||||
@ -310,25 +312,98 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testToByteArray() {
|
||||
byte[] roundTripBytes = TEST_STRING.toByteArray();
|
||||
byte[] roundTripBytes = testString.toByteArray();
|
||||
assertTrue(CLASSNAME + ".toByteArray() must give back the same bytes",
|
||||
Arrays.equals(BYTES, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteTo() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
TEST_STRING.writeTo(bos);
|
||||
testString.writeTo(bos);
|
||||
byte[] roundTripBytes = bos.toByteArray();
|
||||
assertTrue(CLASSNAME + ".writeTo() must give back the same bytes",
|
||||
Arrays.equals(BYTES, roundTripBytes));
|
||||
}
|
||||
|
||||
public void testWriteToShouldNotExposeInternalBufferToOutputStream() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
Arrays.fill(b, off, off + len, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
byte[] original = Arrays.copyOf(BYTES, BYTES.length);
|
||||
testString.writeTo(os);
|
||||
assertTrue(CLASSNAME + ".writeTo() must NOT grant access to underlying buffer",
|
||||
Arrays.equals(original, BYTES));
|
||||
}
|
||||
|
||||
public void testWriteToInternalShouldExposeInternalBufferToOutputStream() throws IOException {
|
||||
OutputStream os = new OutputStream() {
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) {
|
||||
Arrays.fill(b, off, off + len, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
testString.writeToInternal(os, 0, testString.size());
|
||||
byte[] allZeros = new byte[testString.size()];
|
||||
assertTrue(CLASSNAME + ".writeToInternal() must grant access to underlying buffer",
|
||||
Arrays.equals(allZeros, backingBuffer.array()));
|
||||
}
|
||||
|
||||
public void testWriteToShouldExposeInternalBufferToByteOutput() throws IOException {
|
||||
ByteOutput out = new ByteOutput() {
|
||||
@Override
|
||||
public void write(byte value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] value, int offset, int length) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLazy(byte[] value, int offset, int length) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ByteBuffer value) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLazy(ByteBuffer value) throws IOException {
|
||||
Arrays.fill(value.array(), value.arrayOffset(), value.arrayOffset() + value.limit(),
|
||||
(byte) 0);
|
||||
}
|
||||
};
|
||||
|
||||
testString.writeTo(out);
|
||||
byte[] allZeros = new byte[testString.size()];
|
||||
assertTrue(CLASSNAME + ".writeTo() must grant access to underlying buffer",
|
||||
Arrays.equals(allZeros, backingBuffer.array()));
|
||||
}
|
||||
|
||||
public void testNewOutput() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ByteString.Output output = ByteString.newOutput();
|
||||
TEST_STRING.writeTo(output);
|
||||
testString.writeTo(output);
|
||||
assertEquals("Output Size returns correct result",
|
||||
output.size(), TEST_STRING.size());
|
||||
output.size(), testString.size());
|
||||
output.writeTo(bos);
|
||||
assertTrue("Output.writeTo() must give back the same bytes",
|
||||
Arrays.equals(BYTES, bos.toByteArray()));
|
||||
@ -336,7 +411,7 @@ public class NioByteStringTest extends TestCase {
|
||||
// write the output stream to itself! This should cause it to double
|
||||
output.writeTo(output);
|
||||
assertEquals("Writing an output stream to itself is successful",
|
||||
TEST_STRING.concat(TEST_STRING), output.toByteString());
|
||||
testString.concat(testString), output.toByteString());
|
||||
|
||||
output.reset();
|
||||
assertEquals("Output.reset() resets the output", 0, output.size());
|
||||
@ -373,7 +448,7 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
TEST_STRING.toString("invalid");
|
||||
testString.toString("invalid");
|
||||
fail("Should have thrown an exception.");
|
||||
} catch (UnsupportedEncodingException expected) {
|
||||
// This is success
|
||||
@ -381,36 +456,36 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testEquals() {
|
||||
assertEquals(CLASSNAME + " must not equal null", false, TEST_STRING.equals(null));
|
||||
assertEquals(CLASSNAME + " must equal self", TEST_STRING, TEST_STRING);
|
||||
assertEquals(CLASSNAME + " must not equal null", false, testString.equals(null));
|
||||
assertEquals(CLASSNAME + " must equal self", testString, testString);
|
||||
assertFalse(CLASSNAME + " must not equal the empty string",
|
||||
TEST_STRING.equals(EMPTY));
|
||||
testString.equals(EMPTY));
|
||||
assertEquals(CLASSNAME + " empty strings must be equal",
|
||||
EMPTY, TEST_STRING.substring(55, 55));
|
||||
EMPTY, testString.substring(55, 55));
|
||||
assertEquals(CLASSNAME + " must equal another string with the same value",
|
||||
TEST_STRING, new NioByteString(BUFFER));
|
||||
testString, new NioByteString(backingBuffer));
|
||||
|
||||
byte[] mungedBytes = mungedBytes();
|
||||
assertFalse(CLASSNAME + " must not equal every string with the same length",
|
||||
TEST_STRING.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
|
||||
testString.equals(new NioByteString(ByteBuffer.wrap(mungedBytes))));
|
||||
}
|
||||
|
||||
public void testEqualsLiteralByteString() {
|
||||
ByteString literal = ByteString.copyFrom(BYTES);
|
||||
assertEquals(CLASSNAME + " must equal LiteralByteString with same value", literal,
|
||||
TEST_STRING);
|
||||
assertEquals(CLASSNAME + " must equal LiteralByteString with same value", TEST_STRING,
|
||||
testString);
|
||||
assertEquals(CLASSNAME + " must equal LiteralByteString with same value", testString,
|
||||
literal);
|
||||
assertFalse(CLASSNAME + " must not equal the empty string",
|
||||
TEST_STRING.equals(ByteString.EMPTY));
|
||||
testString.equals(ByteString.EMPTY));
|
||||
assertEquals(CLASSNAME + " empty strings must be equal",
|
||||
ByteString.EMPTY, TEST_STRING.substring(55, 55));
|
||||
ByteString.EMPTY, testString.substring(55, 55));
|
||||
|
||||
literal = ByteString.copyFrom(mungedBytes());
|
||||
assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
|
||||
TEST_STRING.equals(literal));
|
||||
testString.equals(literal));
|
||||
assertFalse(CLASSNAME + " must not equal every LiteralByteString with the same length",
|
||||
literal.equals(TEST_STRING));
|
||||
literal.equals(testString));
|
||||
}
|
||||
|
||||
public void testEqualsRopeByteString() {
|
||||
@ -419,22 +494,22 @@ public class NioByteStringTest extends TestCase {
|
||||
ByteString rope = p1.concat(p2);
|
||||
|
||||
assertEquals(CLASSNAME + " must equal RopeByteString with same value", rope,
|
||||
TEST_STRING);
|
||||
assertEquals(CLASSNAME + " must equal RopeByteString with same value", TEST_STRING,
|
||||
testString);
|
||||
assertEquals(CLASSNAME + " must equal RopeByteString with same value", testString,
|
||||
rope);
|
||||
assertFalse(CLASSNAME + " must not equal the empty string",
|
||||
TEST_STRING.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
|
||||
testString.equals(ByteString.EMPTY.concat(ByteString.EMPTY)));
|
||||
assertEquals(CLASSNAME + " empty strings must be equal",
|
||||
ByteString.EMPTY.concat(ByteString.EMPTY), TEST_STRING.substring(55, 55));
|
||||
ByteString.EMPTY.concat(ByteString.EMPTY), testString.substring(55, 55));
|
||||
|
||||
byte[] mungedBytes = mungedBytes();
|
||||
p1 = ByteString.copyFrom(mungedBytes, 0, 5);
|
||||
p2 = ByteString.copyFrom(mungedBytes, 5, mungedBytes.length - 5);
|
||||
rope = p1.concat(p2);
|
||||
assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
|
||||
TEST_STRING.equals(rope));
|
||||
testString.equals(rope));
|
||||
assertFalse(CLASSNAME + " must not equal every RopeByteString with the same length",
|
||||
rope.equals(TEST_STRING));
|
||||
rope.equals(testString));
|
||||
}
|
||||
|
||||
private byte[] mungedBytes() {
|
||||
@ -445,12 +520,12 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testHashCode() {
|
||||
int hash = TEST_STRING.hashCode();
|
||||
int hash = testString.hashCode();
|
||||
assertEquals(CLASSNAME + " must have expected hashCode", EXPECTED_HASH, hash);
|
||||
}
|
||||
|
||||
public void testPeekCachedHashCode() {
|
||||
ByteString newString = new NioByteString(BUFFER);
|
||||
ByteString newString = new NioByteString(backingBuffer);
|
||||
assertEquals(CLASSNAME + ".peekCachedHashCode() should return zero at first", 0,
|
||||
newString.peekCachedHashCode());
|
||||
newString.hashCode();
|
||||
@ -461,15 +536,15 @@ public class NioByteStringTest extends TestCase {
|
||||
public void testPartialHash() {
|
||||
// partialHash() is more strenuously tested elsewhere by testing hashes of substrings.
|
||||
// This test would fail if the expected hash were 1. It's not.
|
||||
int hash = TEST_STRING.partialHash(TEST_STRING.size(), 0, TEST_STRING.size());
|
||||
int hash = testString.partialHash(testString.size(), 0, testString.size());
|
||||
assertEquals(CLASSNAME + ".partialHash() must yield expected hashCode",
|
||||
EXPECTED_HASH, hash);
|
||||
}
|
||||
|
||||
public void testNewInput() throws IOException {
|
||||
InputStream input = TEST_STRING.newInput();
|
||||
InputStream input = testString.newInput();
|
||||
assertEquals("InputStream.available() returns correct value",
|
||||
TEST_STRING.size(), input.available());
|
||||
testString.size(), input.available());
|
||||
boolean stillEqual = true;
|
||||
for (byte referenceByte : BYTES) {
|
||||
int expectedInt = (referenceByte & 0xFF);
|
||||
@ -482,8 +557,8 @@ public class NioByteStringTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testNewInput_skip() throws IOException {
|
||||
InputStream input = TEST_STRING.newInput();
|
||||
int stringSize = TEST_STRING.size();
|
||||
InputStream input = testString.newInput();
|
||||
int stringSize = testString.size();
|
||||
int nearEndIndex = stringSize * 2 / 3;
|
||||
long skipped1 = input.skip(nearEndIndex);
|
||||
assertEquals("InputStream.skip()", skipped1, nearEndIndex);
|
||||
@ -492,7 +567,7 @@ public class NioByteStringTest extends TestCase {
|
||||
assertTrue("InputStream.mark() is available", input.markSupported());
|
||||
input.mark(0);
|
||||
assertEquals("InputStream.skip(), read()",
|
||||
TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
testString.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
assertEquals("InputStream.available()",
|
||||
stringSize - skipped1 - 1, input.available());
|
||||
long skipped2 = input.skip(stringSize);
|
||||
@ -504,11 +579,11 @@ public class NioByteStringTest extends TestCase {
|
||||
assertEquals("InputStream.reset() succeded",
|
||||
stringSize - skipped1, input.available());
|
||||
assertEquals("InputStream.reset(), read()",
|
||||
TEST_STRING.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
testString.byteAt(nearEndIndex) & 0xFF, input.read());
|
||||
}
|
||||
|
||||
public void testNewCodedInput() throws IOException {
|
||||
CodedInputStream cis = TEST_STRING.newCodedInput();
|
||||
CodedInputStream cis = testString.newCodedInput();
|
||||
byte[] roundTripBytes = cis.readRawBytes(BYTES.length);
|
||||
assertTrue(CLASSNAME + " must give the same bytes back from the CodedInputStream",
|
||||
Arrays.equals(BYTES, roundTripBytes));
|
||||
@ -521,22 +596,22 @@ public class NioByteStringTest extends TestCase {
|
||||
*/
|
||||
public void testConcat_empty() {
|
||||
assertSame(CLASSNAME + " concatenated with empty must give " + CLASSNAME,
|
||||
TEST_STRING.concat(EMPTY), TEST_STRING);
|
||||
testString.concat(EMPTY), testString);
|
||||
assertSame("empty concatenated with " + CLASSNAME + " must give " + CLASSNAME,
|
||||
EMPTY.concat(TEST_STRING), TEST_STRING);
|
||||
EMPTY.concat(testString), testString);
|
||||
}
|
||||
|
||||
public void testJavaSerialization() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(out);
|
||||
oos.writeObject(TEST_STRING);
|
||||
oos.writeObject(testString);
|
||||
oos.close();
|
||||
byte[] pickled = out.toByteArray();
|
||||
InputStream in = new ByteArrayInputStream(pickled);
|
||||
ObjectInputStream ois = new ObjectInputStream(in);
|
||||
Object o = ois.readObject();
|
||||
assertTrue("Didn't get a ByteString back", o instanceof ByteString);
|
||||
assertEquals("Should get an equal ByteString back", TEST_STRING, o);
|
||||
assertEquals("Should get an equal ByteString back", testString, o);
|
||||
}
|
||||
|
||||
private static ByteString forString(String str) {
|
||||
|
@ -1,18 +1,52 @@
|
||||
package com.google.protobuf;
|
||||
// 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.
|
||||
|
||||
import com.google.protobuf.DescriptorProtos.DescriptorProto;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.junit.Test;
|
||||
package com.google.protobuf;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import com.google.protobuf.DescriptorProtos.DescriptorProto;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Tests the exceptions thrown when parsing from a stream. The methods on the {@link Parser}
|
||||
* interface are specified to only throw {@link InvalidProtocolBufferException}. But we really want
|
||||
@ -22,6 +56,7 @@ import static org.junit.Assert.fail;
|
||||
*
|
||||
* @author jh@squareup.com (Joshua Humphries)
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class ParseExceptionsTest {
|
||||
|
||||
private interface ParseTester {
|
||||
@ -46,116 +81,143 @@ public class ParseExceptionsTest {
|
||||
|
||||
@Test public void message_parseFrom_InputStream() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(in);
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(in);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void message_parseFrom_CodedInputStream() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in),
|
||||
ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseFrom(
|
||||
CodedInputStream.newInstance(in), ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void message_parseDelimitedFrom_InputStream() {
|
||||
setupDelimited();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseDelimitedFrom(in);
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseDelimitedFrom(in);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
|
||||
setupDelimited();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeFrom_InputStream() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder().mergeFrom(in).build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder().mergeFrom(in).build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder()
|
||||
.mergeFrom(in, ExtensionRegistry.newInstance())
|
||||
.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeFrom_CodedInputStream() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
|
||||
setup();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder()
|
||||
.mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
return DescriptorProto.newBuilder()
|
||||
.mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance())
|
||||
.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeDelimitedFrom_InputStream() {
|
||||
setupDelimited();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
||||
builder.mergeDelimitedFrom(in);
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
||||
builder.mergeDelimitedFrom(in);
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
|
||||
setupDelimited();
|
||||
verifyExceptions(new ParseTester() {
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
||||
builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
verifyExceptions(
|
||||
new ParseTester() {
|
||||
@Override
|
||||
public DescriptorProto parse(InputStream in) throws IOException {
|
||||
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
|
||||
builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());
|
||||
return builder.build();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyExceptions(ParseTester parseTester) {
|
||||
|
@ -33,15 +33,15 @@ package com.google.protobuf;
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestParsingMergeLite;
|
||||
import protobuf_unittest.UnittestOptimizeFor;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor.TestRequiredOptimizedForSize;
|
||||
import protobuf_unittest.UnittestOptimizeFor;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestEmptyMessage;
|
||||
import protobuf_unittest.UnittestProto.TestParsingMerge;
|
||||
import protobuf_unittest.UnittestProto.TestRequired;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
@ -35,22 +35,22 @@ import com.google.protobuf.Descriptors.MethodDescriptor;
|
||||
import google.protobuf.no_generic_services_test.UnittestNoGenericServices;
|
||||
import protobuf_unittest.MessageWithNoOuter;
|
||||
import protobuf_unittest.ServiceWithNoOuter;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
import protobuf_unittest.UnittestProto.FooRequest;
|
||||
import protobuf_unittest.UnittestProto.FooResponse;
|
||||
import protobuf_unittest.UnittestProto.BarRequest;
|
||||
import protobuf_unittest.UnittestProto.BarResponse;
|
||||
import protobuf_unittest.UnittestProto.FooRequest;
|
||||
import protobuf_unittest.UnittestProto.FooResponse;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
import protobuf_unittest.UnittestProto.TestService;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.easymock.classextension.EasyMock;
|
||||
import org.easymock.classextension.IMocksControl;
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.easymock.classextension.IMocksControl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests services and stubs.
|
||||
*
|
||||
|
@ -30,205 +30,207 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import com.google.protobuf.UnittestLite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.OptionalGroup_extension_lite;
|
||||
import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite;
|
||||
import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalCordExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite;
|
||||
import static protobuf_unittest.UnittestProto.OptionalGroup_extension;
|
||||
import static protobuf_unittest.UnittestProto.RepeatedGroup_extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultCordExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
|
||||
// The static imports are to avoid 100+ char lines. The following is roughly equivalent to
|
||||
// import static protobuf_unittest.UnittestProto.*;
|
||||
import static protobuf_unittest.UnittestProto.defaultInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultNestedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultForeignEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultImportEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultStringPieceExtension;
|
||||
import static protobuf_unittest.UnittestProto.defaultCordExtension;
|
||||
|
||||
import static protobuf_unittest.UnittestProto.oneofUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.defaultUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.oneofBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.oneofNestedMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.oneofStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.oneofBytesExtension;
|
||||
|
||||
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.oneofUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalCordExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalGroupExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalCordExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalForeignEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalForeignMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalGroupExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalImportEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalImportMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalNestedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalNestedMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalPublicImportMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalLazyMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.optionalStringPieceExtension;
|
||||
|
||||
import static protobuf_unittest.UnittestProto.repeatedInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedGroupExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedForeignMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedImportMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedLazyMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedNestedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedStringPieceExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedCordExtension;
|
||||
|
||||
import static protobuf_unittest.UnittestProto.OptionalGroup_extension;
|
||||
import static protobuf_unittest.UnittestProto.RepeatedGroup_extension;
|
||||
|
||||
import static protobuf_unittest.UnittestProto.packedInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.optionalUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.packedEnumExtension;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.defaultInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.defaultCordExtensionLite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.oneofUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.oneofBytesExtensionLite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.optionalInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalGroupExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalForeignMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalPublicImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalLazyMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.optionalCordExtensionLite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.repeatedInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedStringExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedBytesExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedGroupExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedNestedMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedForeignMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedImportMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedLazyMessageExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedNestedEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedForeignEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedImportEnumExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedStringPieceExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.repeatedCordExtensionLite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.OptionalGroup_extension_lite;
|
||||
import static com.google.protobuf.UnittestLite.RepeatedGroup_extension_lite;
|
||||
|
||||
import static com.google.protobuf.UnittestLite.packedInt32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedInt64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedUint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedUint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSint32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSint64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSfixed32ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedSfixed64ExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedFloatExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedDoubleExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedBoolExtensionLite;
|
||||
import static com.google.protobuf.UnittestLite.packedEnumExtensionLite;
|
||||
import static protobuf_unittest.UnittestProto.packedSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.packedUint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedBoolExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedBytesExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedCordExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedDoubleExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedFloatExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedForeignEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedForeignMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedGroupExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedImportEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedImportMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedInt32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedInt64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedLazyMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedNestedEnumExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedNestedMessageExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSfixed32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSfixed64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedSint64Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedStringExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedStringPieceExtension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedUint32Extension;
|
||||
import static protobuf_unittest.UnittestProto.repeatedUint64Extension;
|
||||
|
||||
import com.google.protobuf.UnittestImportLite.ImportEnumLite;
|
||||
import com.google.protobuf.UnittestImportLite.ImportMessageLite;
|
||||
import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
|
||||
import com.google.protobuf.UnittestLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignEnumLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignMessageLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.test.UnittestImport.ImportEnum;
|
||||
import com.google.protobuf.test.UnittestImport.ImportMessage;
|
||||
import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensionsOrBuilder;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
@ -237,21 +239,6 @@ import protobuf_unittest.UnittestProto.TestOneof2;
|
||||
import protobuf_unittest.UnittestProto.TestPackedExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestProto.TestUnpackedTypes;
|
||||
import protobuf_unittest.UnittestProto.ForeignMessage;
|
||||
import protobuf_unittest.UnittestProto.ForeignEnum;
|
||||
import com.google.protobuf.test.UnittestImport.ImportEnum;
|
||||
import com.google.protobuf.test.UnittestImport.ImportMessage;
|
||||
import com.google.protobuf.test.UnittestImportPublic.PublicImportMessage;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllTypesLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLiteOrBuilder;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignMessageLite;
|
||||
import com.google.protobuf.UnittestLite.ForeignEnumLite;
|
||||
import com.google.protobuf.UnittestImportLite.ImportEnumLite;
|
||||
import com.google.protobuf.UnittestImportLite.ImportMessageLite;
|
||||
import com.google.protobuf.UnittestImportPublicLite.PublicImportMessageLite;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
|
@ -0,0 +1,182 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test @{link TextFormatParseInfoTree}.
|
||||
*/
|
||||
public class TextFormatParseInfoTreeTest extends TestCase {
|
||||
|
||||
private static final Descriptor DESCRIPTOR = TestAllTypes.getDescriptor();
|
||||
private static final FieldDescriptor OPTIONAL_INT32 =
|
||||
DESCRIPTOR.findFieldByName("optional_int32");
|
||||
private static final FieldDescriptor OPTIONAL_BOOLEAN =
|
||||
DESCRIPTOR.findFieldByName("optional_boolean");
|
||||
private static final FieldDescriptor REPEATED_INT32 =
|
||||
DESCRIPTOR.findFieldByName("repeated_int32");
|
||||
private static final FieldDescriptor OPTIONAL_NESTED_MESSAGE =
|
||||
DESCRIPTOR.findFieldByName("optional_nested_message");
|
||||
private static final FieldDescriptor REPEATED_NESTED_MESSAGE =
|
||||
DESCRIPTOR.findFieldByName("repeated_nested_message");
|
||||
private static final FieldDescriptor FIELD_BB =
|
||||
TestAllTypes.NestedMessage.getDescriptor().findFieldByName("bb");
|
||||
|
||||
private static final TextFormatParseLocation LOC0 = TextFormatParseLocation.create(1, 2);
|
||||
private static final TextFormatParseLocation LOC1 = TextFormatParseLocation.create(2, 3);
|
||||
|
||||
private TextFormatParseInfoTree.Builder rootBuilder;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
rootBuilder = TextFormatParseInfoTree.builder();
|
||||
}
|
||||
|
||||
public void testBuildEmptyParseTree() {
|
||||
TextFormatParseInfoTree tree = rootBuilder.build();
|
||||
assertTrue(tree.getLocations(null).isEmpty());
|
||||
}
|
||||
|
||||
public void testGetLocationReturnsSingleLocation() {
|
||||
rootBuilder.setLocation(OPTIONAL_INT32, LOC0);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertEquals(LOC0, root.getLocation(OPTIONAL_INT32, 0));
|
||||
assertEquals(1, root.getLocations(OPTIONAL_INT32).size());
|
||||
}
|
||||
|
||||
public void testGetLocationsReturnsNoParseLocationsForUnknownField() {
|
||||
assertTrue(rootBuilder.build().getLocations(OPTIONAL_INT32).isEmpty());
|
||||
rootBuilder.setLocation(OPTIONAL_BOOLEAN, LOC0);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertTrue(root.getLocations(OPTIONAL_INT32).isEmpty());
|
||||
assertEquals(LOC0, root.getLocations(OPTIONAL_BOOLEAN).get(0));
|
||||
}
|
||||
|
||||
public void testGetLocationThrowsIllegalArgumentExceptionForUnknownField() {
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
try {
|
||||
root.getNestedTree(OPTIONAL_INT32, 0);
|
||||
fail("Did not detect unknown field");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetLocationThrowsIllegalArgumentExceptionForInvalidIndex() {
|
||||
TextFormatParseInfoTree root = rootBuilder.setLocation(OPTIONAL_INT32, LOC0).build();
|
||||
try {
|
||||
root.getLocation(OPTIONAL_INT32, 1);
|
||||
fail("Invalid index not detected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
try {
|
||||
root.getLocation(OPTIONAL_INT32, -1);
|
||||
fail("Negative index not detected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetLocationsReturnsMultipleLocations() {
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0);
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC1);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertEquals(LOC0, root.getLocation(REPEATED_INT32, 0));
|
||||
assertEquals(LOC1, root.getLocation(REPEATED_INT32, 1));
|
||||
assertEquals(2, root.getLocations(REPEATED_INT32).size());
|
||||
}
|
||||
|
||||
public void testGetNestedTreeThrowsIllegalArgumentExceptionForUnknownField() {
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
try {
|
||||
root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 0);
|
||||
fail("Did not detect unknown field");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetNestedTreesReturnsNoParseInfoTreesForUnknownField() {
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertTrue(root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).isEmpty());
|
||||
}
|
||||
|
||||
public void testGetNestedTreeThrowsIllegalArgumentExceptionForInvalidIndex() {
|
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0);
|
||||
rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
try {
|
||||
root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 1);
|
||||
fail("Submessage index that is too large not detected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
try {
|
||||
rootBuilder.build().getNestedTree(OPTIONAL_NESTED_MESSAGE, -1);
|
||||
fail("Invalid submessage index (-1) not detected");
|
||||
} catch (IllegalArgumentException expected) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testGetNestedTreesReturnsSingleTree() {
|
||||
rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertEquals(1, root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).size());
|
||||
TextFormatParseInfoTree subtree = root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).get(0);
|
||||
assertNotNull(subtree);
|
||||
}
|
||||
|
||||
public void testGetNestedTreesReturnsMultipleTrees() {
|
||||
TextFormatParseInfoTree.Builder subtree1Builder =
|
||||
rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE);
|
||||
subtree1Builder.getBuilderForSubMessageField(FIELD_BB);
|
||||
subtree1Builder.getBuilderForSubMessageField(FIELD_BB);
|
||||
TextFormatParseInfoTree.Builder subtree2Builder =
|
||||
rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE);
|
||||
subtree2Builder.getBuilderForSubMessageField(FIELD_BB);
|
||||
TextFormatParseInfoTree root = rootBuilder.build();
|
||||
assertEquals(2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).size());
|
||||
assertEquals(
|
||||
2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(0).getNestedTrees(FIELD_BB).size());
|
||||
assertEquals(
|
||||
1, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(1).getNestedTrees(FIELD_BB).size());
|
||||
}
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Test @{link TextFormatParseLocation}.
|
||||
*/
|
||||
public class TextFormatParseLocationTest extends TestCase {
|
||||
|
||||
public void testCreateEmpty() {
|
||||
TextFormatParseLocation location = TextFormatParseLocation.create(-1, -1);
|
||||
assertEquals(TextFormatParseLocation.EMPTY, location);
|
||||
}
|
||||
|
||||
public void testCreate() {
|
||||
TextFormatParseLocation location = TextFormatParseLocation.create(2, 1);
|
||||
assertEquals(2, location.getLine());
|
||||
assertEquals(1, location.getColumn());
|
||||
}
|
||||
|
||||
public void testCreateThrowsIllegalArgumentExceptionForInvalidIndex() {
|
||||
try {
|
||||
TextFormatParseLocation.create(-1, 0);
|
||||
fail("Should throw IllegalArgumentException if line is less than 0");
|
||||
} catch (IllegalArgumentException unused) {
|
||||
// pass
|
||||
}
|
||||
try {
|
||||
TextFormatParseLocation.create(0, -1);
|
||||
fail("Should throw, column < 0");
|
||||
} catch (IllegalArgumentException unused) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
public void testHashCode() {
|
||||
TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1);
|
||||
TextFormatParseLocation loc1 = TextFormatParseLocation.create(2, 1);
|
||||
|
||||
assertEquals(loc0.hashCode(), loc1.hashCode());
|
||||
assertEquals(
|
||||
TextFormatParseLocation.EMPTY.hashCode(), TextFormatParseLocation.EMPTY.hashCode());
|
||||
}
|
||||
|
||||
public void testEquals() {
|
||||
TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1);
|
||||
TextFormatParseLocation loc1 = TextFormatParseLocation.create(1, 2);
|
||||
TextFormatParseLocation loc2 = TextFormatParseLocation.create(2, 2);
|
||||
TextFormatParseLocation loc3 = TextFormatParseLocation.create(2, 1);
|
||||
|
||||
assertEquals(loc0, loc3);
|
||||
assertNotSame(loc0, loc1);
|
||||
assertNotSame(loc0, loc2);
|
||||
assertNotSame(loc1, loc2);
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor;
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor;
|
||||
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
@ -45,6 +46,7 @@ import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Test case for {@link TextFormat}.
|
||||
@ -568,6 +570,16 @@ public class TextFormatTest extends TestCase {
|
||||
assertEquals(kEscapeTestString,
|
||||
TextFormat.unescapeText(kEscapeTestStringEscaped));
|
||||
|
||||
// Invariant
|
||||
assertEquals("hello",
|
||||
TextFormat.escapeBytes(bytes("hello")));
|
||||
assertEquals("hello",
|
||||
TextFormat.escapeText("hello"));
|
||||
assertEquals(bytes("hello"),
|
||||
TextFormat.unescapeBytes("hello"));
|
||||
assertEquals("hello",
|
||||
TextFormat.unescapeText("hello"));
|
||||
|
||||
// Unicode handling.
|
||||
assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234"));
|
||||
assertEquals("\\341\\210\\264",
|
||||
@ -811,7 +823,6 @@ public class TextFormatTest extends TestCase {
|
||||
|
||||
private void assertPrintFieldValue(String expect, Object value,
|
||||
String fieldName) throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
TextFormat.printFieldValue(
|
||||
TestAllTypes.getDescriptor().findFieldByName(fieldName),
|
||||
@ -1018,4 +1029,98 @@ public class TextFormatTest extends TestCase {
|
||||
assertFalse(oneof.hasFooString());
|
||||
assertTrue(oneof.hasFooInt());
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
// test location information
|
||||
|
||||
public void testParseInfoTreeBuilding() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
|
||||
Descriptor descriptor = TestAllTypes.getDescriptor();
|
||||
TextFormatParseInfoTree.Builder treeBuilder = TextFormatParseInfoTree.builder();
|
||||
// Set to allow unknown fields
|
||||
TextFormat.Parser parser =
|
||||
TextFormat.Parser.newBuilder()
|
||||
.setParseInfoTreeBuilder(treeBuilder)
|
||||
.build();
|
||||
|
||||
final String stringData =
|
||||
"optional_int32: 1\n"
|
||||
+ "optional_int64: 2\n"
|
||||
+ " optional_double: 2.4\n"
|
||||
+ "repeated_int32: 5\n"
|
||||
+ "repeated_int32: 10\n"
|
||||
+ "optional_nested_message <\n"
|
||||
+ " bb: 78\n"
|
||||
+ ">\n"
|
||||
+ "repeated_nested_message <\n"
|
||||
+ " bb: 79\n"
|
||||
+ ">\n"
|
||||
+ "repeated_nested_message <\n"
|
||||
+ " bb: 80\n"
|
||||
+ ">";
|
||||
|
||||
parser.merge(stringData, builder);
|
||||
TextFormatParseInfoTree tree = treeBuilder.build();
|
||||
|
||||
// Verify that the tree has the correct positions.
|
||||
assertLocation(tree, descriptor, "optional_int32", 0, 0, 0);
|
||||
assertLocation(tree, descriptor, "optional_int64", 0, 1, 0);
|
||||
assertLocation(tree, descriptor, "optional_double", 0, 2, 2);
|
||||
|
||||
assertLocation(tree, descriptor, "repeated_int32", 0, 3, 0);
|
||||
assertLocation(tree, descriptor, "repeated_int32", 1, 4, 0);
|
||||
|
||||
assertLocation(tree, descriptor, "optional_nested_message", 0, 5, 0);
|
||||
assertLocation(tree, descriptor, "repeated_nested_message", 0, 8, 0);
|
||||
assertLocation(tree, descriptor, "repeated_nested_message", 1, 11, 0);
|
||||
|
||||
// Check for fields not set. For an invalid field, the location returned should be -1, -1.
|
||||
assertLocation(tree, descriptor, "repeated_int64", 0, -1, -1);
|
||||
assertLocation(tree, descriptor, "repeated_int32", 6, -1, -1);
|
||||
|
||||
// Verify inside the nested message.
|
||||
FieldDescriptor nestedField = descriptor.findFieldByName("optional_nested_message");
|
||||
|
||||
TextFormatParseInfoTree nestedTree = tree.getNestedTrees(nestedField).get(0);
|
||||
assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 6, 2);
|
||||
|
||||
// Verify inside another nested message.
|
||||
nestedField = descriptor.findFieldByName("repeated_nested_message");
|
||||
nestedTree = tree.getNestedTrees(nestedField).get(0);
|
||||
assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 9, 2);
|
||||
|
||||
nestedTree = tree.getNestedTrees(nestedField).get(1);
|
||||
assertLocation(nestedTree, nestedField.getMessageType(), "bb", 0, 12, 2);
|
||||
|
||||
// Verify a NULL tree for an unknown nested field.
|
||||
try {
|
||||
tree.getNestedTree(nestedField, 2);
|
||||
fail("unknown nested field should throw");
|
||||
} catch (IllegalArgumentException unused) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
private void assertLocation(
|
||||
TextFormatParseInfoTree tree,
|
||||
final Descriptor descriptor,
|
||||
final String fieldName,
|
||||
int index,
|
||||
int line,
|
||||
int column) {
|
||||
List<TextFormatParseLocation> locs = tree.getLocations(descriptor.findFieldByName(fieldName));
|
||||
if (index < locs.size()) {
|
||||
TextFormatParseLocation location = locs.get(index);
|
||||
TextFormatParseLocation expected = TextFormatParseLocation.create(line, column);
|
||||
assertEquals(expected, location);
|
||||
} else if (line != -1 && column != -1) {
|
||||
fail(
|
||||
String.format(
|
||||
"Tree/descriptor/fieldname did not contain index %d, line %d column %d expected",
|
||||
index,
|
||||
line,
|
||||
column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,46 +47,6 @@ import java.io.IOException;
|
||||
* @author dweis@google.com (Daniel Weis)
|
||||
*/
|
||||
public class UnknownFieldSetLiteTest extends TestCase {
|
||||
|
||||
public void testNoDataIsDefaultInstance() {
|
||||
assertSame(
|
||||
UnknownFieldSetLite.getDefaultInstance(),
|
||||
UnknownFieldSetLite.newBuilder()
|
||||
.build());
|
||||
}
|
||||
|
||||
public void testBuilderReuse() throws IOException {
|
||||
UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
|
||||
builder.mergeVarintField(10, 2);
|
||||
builder.build();
|
||||
|
||||
try {
|
||||
builder.build();
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
builder.mergeFieldFrom(0, CodedInputStream.newInstance(new byte[0]));
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
try {
|
||||
builder.mergeVarintField(5, 1);
|
||||
fail();
|
||||
} catch (UnsupportedOperationException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
public void testBuilderReuse_empty() {
|
||||
UnknownFieldSetLite.Builder builder = UnknownFieldSetLite.newBuilder();
|
||||
builder.build();
|
||||
builder.build();
|
||||
}
|
||||
|
||||
public void testDefaultInstance() {
|
||||
UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance();
|
||||
|
@ -30,12 +30,11 @@
|
||||
|
||||
package com.google.protobuf;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
import protobuf_unittest.UnittestMset.RawMessageSet;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
|
||||
import protobuf_unittest.UnittestProto;
|
||||
import protobuf_unittest.UnittestProto.TestAllExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestAllTypes;
|
||||
@ -44,12 +43,13 @@ import protobuf_unittest.UnittestProto.TestOneof2;
|
||||
import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible;
|
||||
import protobuf_unittest.UnittestProto.TestPackedExtensions;
|
||||
import protobuf_unittest.UnittestProto.TestPackedTypes;
|
||||
import protobuf_unittest.UnittestMset.RawMessageSet;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
|
||||
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
|
||||
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
|
||||
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
|
||||
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests related to parsing and serialization.
|
||||
|
@ -39,6 +39,13 @@ package protobuf_unittest.lite_equals_and_hash;
|
||||
option java_generate_equals_and_hash = true;
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
message TestOneofEquals {
|
||||
oneof oneof_field {
|
||||
string name = 1;
|
||||
int32 value = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message Foo {
|
||||
optional int32 value = 1;
|
||||
repeated Bar bar = 2;
|
||||
@ -70,3 +77,8 @@ extend Foo {
|
||||
}
|
||||
}
|
||||
|
||||
message TestRecursiveOneof {
|
||||
oneof Foo {
|
||||
TestRecursiveOneof r = 1;
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,7 @@
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.4</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -60,10 +60,9 @@ import java.util.logging.Logger;
|
||||
* FieldMask in a message tree.
|
||||
*/
|
||||
class FieldMaskTree {
|
||||
private static final Logger logger =
|
||||
Logger.getLogger(FieldMaskTree.class.getName());
|
||||
|
||||
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
|
||||
private static final Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
|
||||
|
||||
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
|
||||
|
||||
private static class Node {
|
||||
public TreeMap<String, Node> children = new TreeMap<String, Node>();
|
||||
@ -73,12 +72,12 @@ class FieldMaskTree {
|
||||
|
||||
/** Creates an empty FieldMaskTree. */
|
||||
public FieldMaskTree() {}
|
||||
|
||||
|
||||
/** Creates a FieldMaskTree for a given FieldMask. */
|
||||
public FieldMaskTree(FieldMask mask) {
|
||||
mergeFromFieldMask(mask);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return FieldMaskUtil.toString(toFieldMask());
|
||||
@ -121,7 +120,7 @@ class FieldMaskTree {
|
||||
node.children.clear();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Merges all field paths in a FieldMask into this tree.
|
||||
*/
|
||||
@ -149,8 +148,7 @@ class FieldMaskTree {
|
||||
return;
|
||||
}
|
||||
for (Entry<String, Node> entry : node.children.entrySet()) {
|
||||
String childPath = path.isEmpty()
|
||||
? entry.getKey() : path + "." + entry.getKey();
|
||||
String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
|
||||
getFieldPaths(entry.getValue(), childPath, paths);
|
||||
}
|
||||
}
|
||||
@ -193,11 +191,10 @@ class FieldMaskTree {
|
||||
* Merges all fields specified by this FieldMaskTree from {@code source} to
|
||||
* {@code destination}.
|
||||
*/
|
||||
public void merge(Message source, Message.Builder destination,
|
||||
FieldMaskUtil.MergeOptions options) {
|
||||
public void merge(
|
||||
Message source, Message.Builder destination, FieldMaskUtil.MergeOptions options) {
|
||||
if (source.getDescriptorForType() != destination.getDescriptorForType()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot merge messages of different types.");
|
||||
throw new IllegalArgumentException("Cannot merge messages of different types.");
|
||||
}
|
||||
if (root.children.isEmpty()) {
|
||||
return;
|
||||
@ -208,30 +205,41 @@ class FieldMaskTree {
|
||||
/** Merges all fields specified by a sub-tree from {@code source} to
|
||||
* {@code destination}.
|
||||
*/
|
||||
private void merge(Node node, String path, Message source,
|
||||
Message.Builder destination, FieldMaskUtil.MergeOptions options) {
|
||||
private void merge(
|
||||
Node node,
|
||||
String path,
|
||||
Message source,
|
||||
Message.Builder destination,
|
||||
FieldMaskUtil.MergeOptions options) {
|
||||
assert source.getDescriptorForType() == destination.getDescriptorForType();
|
||||
|
||||
|
||||
Descriptor descriptor = source.getDescriptorForType();
|
||||
for (Entry<String, Node> entry : node.children.entrySet()) {
|
||||
FieldDescriptor field =
|
||||
descriptor.findFieldByName(entry.getKey());
|
||||
FieldDescriptor field = descriptor.findFieldByName(entry.getKey());
|
||||
if (field == null) {
|
||||
logger.warning("Cannot find field \"" + entry.getKey()
|
||||
+ "\" in message type " + descriptor.getFullName());
|
||||
logger.warning(
|
||||
"Cannot find field \""
|
||||
+ entry.getKey()
|
||||
+ "\" in message type "
|
||||
+ descriptor.getFullName());
|
||||
continue;
|
||||
}
|
||||
if (!entry.getValue().children.isEmpty()) {
|
||||
if (field.isRepeated()
|
||||
|| field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
logger.warning("Field \"" + field.getFullName() + "\" is not a "
|
||||
+ "singluar message field and cannot have sub-fields.");
|
||||
if (field.isRepeated() || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
|
||||
logger.warning(
|
||||
"Field \""
|
||||
+ field.getFullName()
|
||||
+ "\" is not a "
|
||||
+ "singluar message field and cannot have sub-fields.");
|
||||
continue;
|
||||
}
|
||||
String childPath = path.isEmpty()
|
||||
? entry.getKey() : path + "." + entry.getKey();
|
||||
merge(entry.getValue(), childPath, (Message) source.getField(field),
|
||||
destination.getFieldBuilder(field), options);
|
||||
String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey();
|
||||
merge(
|
||||
entry.getValue(),
|
||||
childPath,
|
||||
(Message) source.getField(field),
|
||||
destination.getFieldBuilder(field),
|
||||
options);
|
||||
continue;
|
||||
}
|
||||
if (field.isRepeated()) {
|
||||
@ -245,10 +253,15 @@ class FieldMaskTree {
|
||||
} else {
|
||||
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
|
||||
if (options.replaceMessageFields()) {
|
||||
destination.setField(field, source.getField(field));
|
||||
if (!source.hasField(field)) {
|
||||
destination.clearField(field);
|
||||
} else {
|
||||
destination.setField(field, source.getField(field));
|
||||
}
|
||||
} else {
|
||||
destination.getFieldBuilder(field).mergeFrom(
|
||||
(Message) source.getField(field));
|
||||
if (source.hasField(field)) {
|
||||
destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
destination.setField(field, source.getField(field));
|
||||
|
@ -32,6 +32,7 @@ package com.google.protobuf.util;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
@ -433,7 +434,7 @@ public class JsonFormat {
|
||||
private final Gson gson;
|
||||
|
||||
private static class GsonHolder {
|
||||
private static final Gson DEFAULT_GSON = new Gson();
|
||||
private static final Gson DEFAULT_GSON = new GsonBuilder().disableHtmlEscaping().create();
|
||||
}
|
||||
|
||||
PrinterImpl(
|
||||
@ -1066,6 +1067,15 @@ public class JsonFormat {
|
||||
parser.mergeStruct(json, builder);
|
||||
}
|
||||
});
|
||||
// Special-case ListValue.
|
||||
parsers.put(ListValue.getDescriptor().getFullName(),
|
||||
new WellKnownTypeParser() {
|
||||
@Override
|
||||
public void merge(ParserImpl parser, JsonElement json,
|
||||
Message.Builder builder) throws InvalidProtocolBufferException {
|
||||
parser.mergeListValue(json, builder);
|
||||
}
|
||||
});
|
||||
// Special-case Value.
|
||||
parsers.put(Value.getDescriptor().getFullName(),
|
||||
new WellKnownTypeParser() {
|
||||
@ -1213,6 +1223,16 @@ public class JsonFormat {
|
||||
}
|
||||
mergeMapField(field, json, builder);
|
||||
}
|
||||
|
||||
private void mergeListValue(JsonElement json, Message.Builder builder)
|
||||
throws InvalidProtocolBufferException {
|
||||
Descriptor descriptor = builder.getDescriptorForType();
|
||||
FieldDescriptor field = descriptor.findFieldByName("values");
|
||||
if (field == null) {
|
||||
throw new InvalidProtocolBufferException("Invalid ListValue type.");
|
||||
}
|
||||
mergeRepeatedField(field, json, builder);
|
||||
}
|
||||
|
||||
private void mergeValue(JsonElement json, Message.Builder builder)
|
||||
throws InvalidProtocolBufferException {
|
||||
@ -1237,9 +1257,7 @@ public class JsonFormat {
|
||||
} else if (json instanceof JsonArray) {
|
||||
FieldDescriptor field = type.findFieldByName("list_value");
|
||||
Message.Builder listBuilder = builder.newBuilderForField(field);
|
||||
FieldDescriptor listField =
|
||||
listBuilder.getDescriptorForType().findFieldByName("values");
|
||||
mergeRepeatedField(listField, json, listBuilder);
|
||||
merge(json, listBuilder);
|
||||
builder.setField(field, listBuilder.build());
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected json data: " + json);
|
||||
|
@ -61,19 +61,16 @@ public class FieldMaskTreeTest extends TestCase {
|
||||
tree.addFieldPath("bar");
|
||||
assertEquals("bar,foo", tree.toString());
|
||||
}
|
||||
|
||||
|
||||
public void testMergeFromFieldMask() throws Exception {
|
||||
FieldMaskTree tree = new FieldMaskTree(
|
||||
FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
assertEquals("bar.baz,bar.quz,foo", tree.toString());
|
||||
tree.mergeFromFieldMask(
|
||||
FieldMaskUtil.fromString("foo.bar,bar"));
|
||||
tree.mergeFromFieldMask(FieldMaskUtil.fromString("foo.bar,bar"));
|
||||
assertEquals("bar,foo", tree.toString());
|
||||
}
|
||||
|
||||
|
||||
public void testIntersectFieldPath() throws Exception {
|
||||
FieldMaskTree tree = new FieldMaskTree(
|
||||
FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
FieldMaskTree tree = new FieldMaskTree(FieldMaskUtil.fromString("foo,bar.baz,bar.quz"));
|
||||
FieldMaskTree result = new FieldMaskTree();
|
||||
// Empty path.
|
||||
tree.intersectFieldPath("", result);
|
||||
@ -96,16 +93,18 @@ public class FieldMaskTreeTest extends TestCase {
|
||||
}
|
||||
|
||||
public void testMerge() throws Exception {
|
||||
TestAllTypes value = TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1234)
|
||||
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
|
||||
.addRepeatedInt32(4321)
|
||||
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
|
||||
.build();
|
||||
NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
|
||||
.setPayload(value)
|
||||
.setChild(NestedTestAllTypes.newBuilder().setPayload(value))
|
||||
.build();
|
||||
TestAllTypes value =
|
||||
TestAllTypes.newBuilder()
|
||||
.setOptionalInt32(1234)
|
||||
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
|
||||
.addRepeatedInt32(4321)
|
||||
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
|
||||
.build();
|
||||
NestedTestAllTypes source =
|
||||
NestedTestAllTypes.newBuilder()
|
||||
.setPayload(value)
|
||||
.setChild(NestedTestAllTypes.newBuilder().setPayload(value))
|
||||
.build();
|
||||
// Now we have a message source with the following structure:
|
||||
// [root] -+- payload -+- optional_int32
|
||||
// | +- optional_nested_message
|
||||
@ -116,114 +115,127 @@ public class FieldMaskTreeTest extends TestCase {
|
||||
// +- optional_nested_message
|
||||
// +- repeated_int32
|
||||
// +- repeated_nested_message
|
||||
|
||||
|
||||
FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions();
|
||||
|
||||
|
||||
// Test merging each individual field.
|
||||
NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("payload.optional_int32")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload.optional_int32").merge(source, builder, options);
|
||||
NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().setOptionalInt32(1234);
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("payload.optional_nested_message")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("payload.optional_nested_message")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().setOptionalNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(5678));
|
||||
expected.getPayloadBuilder().setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().addRepeatedInt32(4321);
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_nested_message")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("payload.repeated_nested_message")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getPayloadBuilder().addRepeatedNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(8765));
|
||||
expected.getPayloadBuilder().addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("child.payload.optional_int32")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("child.payload.optional_int32")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234);
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("child.payload.optional_nested_message")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("child.payload.optional_nested_message")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().setOptionalNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(5678));
|
||||
expected
|
||||
.getChildBuilder()
|
||||
.getPayloadBuilder()
|
||||
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678));
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("child.payload.repeated_int32")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("child.payload.repeated_int32")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321);
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message")
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("child.payload.repeated_nested_message")
|
||||
.merge(source, builder, options);
|
||||
expected = NestedTestAllTypes.newBuilder();
|
||||
expected.getChildBuilder().getPayloadBuilder().addRepeatedNestedMessage(
|
||||
NestedMessage.newBuilder().setBb(8765));
|
||||
expected
|
||||
.getChildBuilder()
|
||||
.getPayloadBuilder()
|
||||
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765));
|
||||
assertEquals(expected.build(), builder.build());
|
||||
|
||||
|
||||
// Test merging all fields.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("child").addFieldPath("payload")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree()
|
||||
.addFieldPath("child")
|
||||
.addFieldPath("payload")
|
||||
.merge(source, builder, options);
|
||||
assertEquals(source, builder.build());
|
||||
|
||||
|
||||
// Test repeated options.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().addRepeatedInt32(1000);
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
|
||||
// Default behavior is to append repeated fields.
|
||||
assertEquals(2, builder.getPayload().getRepeatedInt32Count());
|
||||
assertEquals(1000, builder.getPayload().getRepeatedInt32(0));
|
||||
assertEquals(4321, builder.getPayload().getRepeatedInt32(1));
|
||||
// Change to replace repeated fields.
|
||||
options.setReplaceRepeatedFields(true);
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload.repeated_int32").merge(source, builder, options);
|
||||
assertEquals(1, builder.getPayload().getRepeatedInt32Count());
|
||||
assertEquals(4321, builder.getPayload().getRepeatedInt32(0));
|
||||
|
||||
|
||||
// Test message options.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
new FieldMaskTree().addFieldPath("payload")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
|
||||
// Default behavior is to merge message fields.
|
||||
assertEquals(1234, builder.getPayload().getOptionalInt32());
|
||||
assertEquals(2000, builder.getPayload().getOptionalUint32());
|
||||
|
||||
|
||||
// Test merging unset message fields.
|
||||
NestedTestAllTypes clearedSource = source.toBuilder().clearPayload().build();
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
|
||||
assertEquals(false, builder.hasPayload());
|
||||
|
||||
// Change to replace message fields.
|
||||
options.setReplaceMessageFields(true);
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
new FieldMaskTree().addFieldPath("payload")
|
||||
.merge(source, builder, options);
|
||||
new FieldMaskTree().addFieldPath("payload").merge(source, builder, options);
|
||||
assertEquals(1234, builder.getPayload().getOptionalInt32());
|
||||
assertEquals(0, builder.getPayload().getOptionalUint32());
|
||||
|
||||
// Test merging unset message fields.
|
||||
builder = NestedTestAllTypes.newBuilder();
|
||||
builder.getPayloadBuilder().setOptionalInt32(1000);
|
||||
builder.getPayloadBuilder().setOptionalUint32(2000);
|
||||
new FieldMaskTree().addFieldPath("payload").merge(clearedSource, builder, options);
|
||||
assertEquals(false, builder.hasPayload());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,11 +122,11 @@ public class JsonFormatTest extends TestCase {
|
||||
builder.addRepeatedNestedEnum(NestedEnum.BAZ);
|
||||
builder.addRepeatedNestedMessageBuilder().setValue(200);
|
||||
}
|
||||
|
||||
|
||||
private void assertRoundTripEquals(Message message) throws Exception {
|
||||
assertRoundTripEquals(message, TypeRegistry.getEmptyTypeRegistry());
|
||||
}
|
||||
|
||||
|
||||
private void assertRoundTripEquals(Message message, TypeRegistry registry) throws Exception {
|
||||
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
|
||||
JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry);
|
||||
@ -135,68 +135,68 @@ public class JsonFormatTest extends TestCase {
|
||||
Message parsedMessage = builder.build();
|
||||
assertEquals(message.toString(), parsedMessage.toString());
|
||||
}
|
||||
|
||||
|
||||
private String toJsonString(Message message) throws IOException {
|
||||
return JsonFormat.printer().print(message);
|
||||
}
|
||||
|
||||
|
||||
private void mergeFromJson(String json, Message.Builder builder) throws IOException {
|
||||
JsonFormat.parser().merge(json, builder);
|
||||
}
|
||||
|
||||
|
||||
public void testAllFields() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
setAllFields(builder);
|
||||
TestAllTypes message = builder.build();
|
||||
|
||||
assertEquals(
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"optionalInt32\": 1234,\n"
|
||||
+ " \"optionalInt64\": \"1234567890123456789\",\n"
|
||||
+ " \"optionalUint32\": 5678,\n"
|
||||
+ " \"optionalUint64\": \"2345678901234567890\",\n"
|
||||
+ " \"optionalSint32\": 9012,\n"
|
||||
+ " \"optionalSint64\": \"3456789012345678901\",\n"
|
||||
+ " \"optionalFixed32\": 3456,\n"
|
||||
+ " \"optionalFixed64\": \"4567890123456789012\",\n"
|
||||
+ " \"optionalSfixed32\": 7890,\n"
|
||||
+ " \"optionalSfixed64\": \"5678901234567890123\",\n"
|
||||
+ " \"optionalFloat\": 1.5,\n"
|
||||
+ " \"optionalDouble\": 1.25,\n"
|
||||
+ " \"optionalBool\": true,\n"
|
||||
+ " \"optionalString\": \"Hello world!\",\n"
|
||||
+ " \"optionalBytes\": \"AAEC\",\n"
|
||||
+ " \"optionalNestedMessage\": {\n"
|
||||
+ " \"value\": 100\n"
|
||||
+ " },\n"
|
||||
+ " \"optionalNestedEnum\": \"BAR\",\n"
|
||||
+ " \"repeatedInt32\": [1234, 234],\n"
|
||||
+ " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n"
|
||||
+ " \"repeatedUint32\": [5678, 678],\n"
|
||||
+ " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n"
|
||||
+ " \"repeatedSint32\": [9012, 10],\n"
|
||||
+ " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n"
|
||||
+ " \"repeatedFixed32\": [3456, 456],\n"
|
||||
+ " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n"
|
||||
+ " \"repeatedSfixed32\": [7890, 890],\n"
|
||||
+ " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n"
|
||||
+ " \"repeatedFloat\": [1.5, 11.5],\n"
|
||||
+ " \"repeatedDouble\": [1.25, 11.25],\n"
|
||||
+ " \"repeatedBool\": [true, true],\n"
|
||||
+ " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n"
|
||||
+ " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n"
|
||||
+ " \"repeatedNestedMessage\": [{\n"
|
||||
+ " \"value\": 100\n"
|
||||
+ " }, {\n"
|
||||
+ " \"value\": 200\n"
|
||||
+ " }],\n"
|
||||
+ " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n"
|
||||
+ "}",
|
||||
+ " \"optionalInt32\": 1234,\n"
|
||||
+ " \"optionalInt64\": \"1234567890123456789\",\n"
|
||||
+ " \"optionalUint32\": 5678,\n"
|
||||
+ " \"optionalUint64\": \"2345678901234567890\",\n"
|
||||
+ " \"optionalSint32\": 9012,\n"
|
||||
+ " \"optionalSint64\": \"3456789012345678901\",\n"
|
||||
+ " \"optionalFixed32\": 3456,\n"
|
||||
+ " \"optionalFixed64\": \"4567890123456789012\",\n"
|
||||
+ " \"optionalSfixed32\": 7890,\n"
|
||||
+ " \"optionalSfixed64\": \"5678901234567890123\",\n"
|
||||
+ " \"optionalFloat\": 1.5,\n"
|
||||
+ " \"optionalDouble\": 1.25,\n"
|
||||
+ " \"optionalBool\": true,\n"
|
||||
+ " \"optionalString\": \"Hello world!\",\n"
|
||||
+ " \"optionalBytes\": \"AAEC\",\n"
|
||||
+ " \"optionalNestedMessage\": {\n"
|
||||
+ " \"value\": 100\n"
|
||||
+ " },\n"
|
||||
+ " \"optionalNestedEnum\": \"BAR\",\n"
|
||||
+ " \"repeatedInt32\": [1234, 234],\n"
|
||||
+ " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n"
|
||||
+ " \"repeatedUint32\": [5678, 678],\n"
|
||||
+ " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n"
|
||||
+ " \"repeatedSint32\": [9012, 10],\n"
|
||||
+ " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n"
|
||||
+ " \"repeatedFixed32\": [3456, 456],\n"
|
||||
+ " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n"
|
||||
+ " \"repeatedSfixed32\": [7890, 890],\n"
|
||||
+ " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n"
|
||||
+ " \"repeatedFloat\": [1.5, 11.5],\n"
|
||||
+ " \"repeatedDouble\": [1.25, 11.25],\n"
|
||||
+ " \"repeatedBool\": [true, true],\n"
|
||||
+ " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n"
|
||||
+ " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n"
|
||||
+ " \"repeatedNestedMessage\": [{\n"
|
||||
+ " \"value\": 100\n"
|
||||
+ " }, {\n"
|
||||
+ " \"value\": 200\n"
|
||||
+ " }],\n"
|
||||
+ " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n"
|
||||
+ "}",
|
||||
toJsonString(message));
|
||||
|
||||
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testUnknownEnumValues() throws Exception {
|
||||
TestAllTypes message = TestAllTypes.newBuilder()
|
||||
.setOptionalNestedEnumValue(12345)
|
||||
@ -209,21 +209,22 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " \"repeatedNestedEnum\": [12345, \"FOO\"]\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
|
||||
|
||||
TestMap.Builder mapBuilder = TestMap.newBuilder();
|
||||
mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0);
|
||||
mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345);
|
||||
TestMap mapMessage = mapBuilder.build();
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"int32ToEnumMap\": {\n"
|
||||
+ " \"1\": \"FOO\",\n"
|
||||
+ " \"2\": 12345\n"
|
||||
+ " }\n"
|
||||
+ "}", toJsonString(mapMessage));
|
||||
"{\n"
|
||||
+ " \"int32ToEnumMap\": {\n"
|
||||
+ " \"1\": \"FOO\",\n"
|
||||
+ " \"2\": 12345\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
toJsonString(mapMessage));
|
||||
assertRoundTripEquals(mapMessage);
|
||||
}
|
||||
|
||||
|
||||
public void testSpecialFloatValues() throws Exception {
|
||||
TestAllTypes message = TestAllTypes.newBuilder()
|
||||
.addRepeatedFloat(Float.NaN)
|
||||
@ -238,10 +239,10 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n"
|
||||
+ " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n"
|
||||
+ "}", toJsonString(message));
|
||||
|
||||
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testParserAcceptStringForNumbericField() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -265,7 +266,7 @@ public class JsonFormatTest extends TestCase {
|
||||
assertEquals(1.25, message.getOptionalDouble());
|
||||
assertEquals(true, message.getOptionalBool());
|
||||
}
|
||||
|
||||
|
||||
public void testParserAcceptFloatingPointValueForIntegerField() throws Exception {
|
||||
// Test that numeric values like "1.000", "1e5" will also be accepted.
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -287,14 +288,14 @@ public class JsonFormatTest extends TestCase {
|
||||
assertEquals(expectedValues[i], builder.getRepeatedInt64(i));
|
||||
assertEquals(expectedValues[i], builder.getRepeatedUint64(i));
|
||||
}
|
||||
|
||||
|
||||
// Non-integers will still be rejected.
|
||||
assertRejects("optionalInt32", "1.5");
|
||||
assertRejects("optionalUint32", "1.5");
|
||||
assertRejects("optionalInt64", "1.5");
|
||||
assertRejects("optionalUint64", "1.5");
|
||||
}
|
||||
|
||||
|
||||
private void assertRejects(String name, String value) {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
try {
|
||||
@ -312,7 +313,7 @@ public class JsonFormatTest extends TestCase {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void assertAccepts(String name, String value) throws IOException {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
// Both numeric form and string form are accepted.
|
||||
@ -320,17 +321,17 @@ public class JsonFormatTest extends TestCase {
|
||||
builder.clear();
|
||||
mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder);
|
||||
}
|
||||
|
||||
|
||||
public void testParserRejectOutOfRangeNumericValues() throws Exception {
|
||||
assertAccepts("optionalInt32", String.valueOf(Integer.MAX_VALUE));
|
||||
assertAccepts("optionalInt32", String.valueOf(Integer.MIN_VALUE));
|
||||
assertRejects("optionalInt32", String.valueOf(Integer.MAX_VALUE + 1L));
|
||||
assertRejects("optionalInt32", String.valueOf(Integer.MIN_VALUE - 1L));
|
||||
|
||||
|
||||
assertAccepts("optionalUint32", String.valueOf(Integer.MAX_VALUE + 1L));
|
||||
assertRejects("optionalUint32", "123456789012345");
|
||||
assertRejects("optionalUint32", "-1");
|
||||
|
||||
|
||||
BigInteger one = new BigInteger("1");
|
||||
BigInteger maxLong = new BigInteger(String.valueOf(Long.MAX_VALUE));
|
||||
BigInteger minLong = new BigInteger(String.valueOf(Long.MIN_VALUE));
|
||||
@ -351,7 +352,7 @@ public class JsonFormatTest extends TestCase {
|
||||
assertAccepts("optionalFloat", String.valueOf(-Float.MAX_VALUE));
|
||||
assertRejects("optionalFloat", String.valueOf(Double.MAX_VALUE));
|
||||
assertRejects("optionalFloat", String.valueOf(-Double.MAX_VALUE));
|
||||
|
||||
|
||||
BigDecimal moreThanOne = new BigDecimal("1.000001");
|
||||
BigDecimal maxDouble = new BigDecimal(Double.MAX_VALUE);
|
||||
BigDecimal minDouble = new BigDecimal(-Double.MAX_VALUE);
|
||||
@ -360,7 +361,7 @@ public class JsonFormatTest extends TestCase {
|
||||
assertRejects("optionalDouble", maxDouble.multiply(moreThanOne).toString());
|
||||
assertRejects("optionalDouble", minDouble.multiply(moreThanOne).toString());
|
||||
}
|
||||
|
||||
|
||||
public void testParserAcceptNull() throws Exception {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -402,7 +403,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ "}", builder);
|
||||
TestAllTypes message = builder.build();
|
||||
assertEquals(TestAllTypes.getDefaultInstance(), message);
|
||||
|
||||
|
||||
// Repeated field elements cannot be null.
|
||||
try {
|
||||
builder = TestAllTypes.newBuilder();
|
||||
@ -414,7 +415,7 @@ public class JsonFormatTest extends TestCase {
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Exception expected.
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
builder = TestAllTypes.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -426,14 +427,14 @@ public class JsonFormatTest extends TestCase {
|
||||
// Exception expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testParserRejectDuplicatedFields() throws Exception {
|
||||
// TODO(xiaofeng): The parser we are currently using (GSON) will accept and keep the last
|
||||
// one if multiple entries have the same name. This is not the desired behavior but it can
|
||||
// only be fixed by using our own parser. Here we only test the cases where the names are
|
||||
// different but still referring to the same field.
|
||||
|
||||
// Duplicated optional fields.
|
||||
|
||||
// Duplicated optional fields.
|
||||
try {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -445,7 +446,7 @@ public class JsonFormatTest extends TestCase {
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Exception expected.
|
||||
}
|
||||
|
||||
|
||||
// Duplicated repeated fields.
|
||||
try {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -458,7 +459,7 @@ public class JsonFormatTest extends TestCase {
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Exception expected.
|
||||
}
|
||||
|
||||
|
||||
// Duplicated oneof fields.
|
||||
try {
|
||||
TestOneof.Builder builder = TestOneof.newBuilder();
|
||||
@ -472,7 +473,7 @@ public class JsonFormatTest extends TestCase {
|
||||
// Exception expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testMapFields() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
builder.getMutableInt32ToInt32Map().put(1, 10);
|
||||
@ -507,7 +508,7 @@ public class JsonFormatTest extends TestCase {
|
||||
8, NestedMessage.newBuilder().setValue(1234).build());
|
||||
builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR);
|
||||
TestMap message = builder.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"int32ToInt32Map\": {\n"
|
||||
@ -598,13 +599,13 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " }\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
|
||||
|
||||
// Test multiple entries.
|
||||
builder = TestMap.newBuilder();
|
||||
builder.getMutableInt32ToInt32Map().put(1, 2);
|
||||
builder.getMutableInt32ToInt32Map().put(3, 4);
|
||||
message = builder.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"int32ToInt32Map\": {\n"
|
||||
@ -614,7 +615,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testMapNullValueIsRejected() throws Exception {
|
||||
try {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
@ -627,7 +628,7 @@ public class JsonFormatTest extends TestCase {
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Exception expected.
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -640,7 +641,7 @@ public class JsonFormatTest extends TestCase {
|
||||
// Exception expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testParserAcceptNonQuotedObjectKey() throws Exception {
|
||||
TestMap.Builder builder = TestMap.newBuilder();
|
||||
mergeFromJson(
|
||||
@ -652,7 +653,7 @@ public class JsonFormatTest extends TestCase {
|
||||
assertEquals(2, message.getInt32ToInt32Map().get(1).intValue());
|
||||
assertEquals(3, message.getStringToInt32Map().get("hello").intValue());
|
||||
}
|
||||
|
||||
|
||||
public void testWrappers() throws Exception {
|
||||
TestWrappers.Builder builder = TestWrappers.newBuilder();
|
||||
builder.getBoolValueBuilder().setValue(false);
|
||||
@ -665,7 +666,7 @@ public class JsonFormatTest extends TestCase {
|
||||
builder.getStringValueBuilder().setValue("");
|
||||
builder.getBytesValueBuilder().setValue(ByteString.EMPTY);
|
||||
TestWrappers message = builder.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"int32Value\": 0,\n"
|
||||
@ -691,7 +692,7 @@ public class JsonFormatTest extends TestCase {
|
||||
builder.getStringValueBuilder().setValue("7");
|
||||
builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[]{8}));
|
||||
message = builder.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"int32Value\": 1,\n"
|
||||
@ -706,43 +707,43 @@ public class JsonFormatTest extends TestCase {
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testTimestamp() throws Exception {
|
||||
TestTimestamp message = TestTimestamp.newBuilder()
|
||||
.setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z"))
|
||||
.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testDuration() throws Exception {
|
||||
TestDuration message = TestDuration.newBuilder()
|
||||
.setDurationValue(TimeUtil.parseDuration("12345s"))
|
||||
.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"durationValue\": \"12345s\"\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testFieldMask() throws Exception {
|
||||
TestFieldMask message = TestFieldMask.newBuilder()
|
||||
.setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz"))
|
||||
.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"fieldMaskValue\": \"foo.bar,baz\"\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testStruct() throws Exception {
|
||||
// Build a struct with all possible values.
|
||||
TestStruct.Builder builder = TestStruct.newBuilder();
|
||||
@ -764,7 +765,7 @@ public class JsonFormatTest extends TestCase {
|
||||
structBuilder.getMutableFields().put(
|
||||
"list_value", Value.newBuilder().setListValue(listBuilder.build()).build());
|
||||
TestStruct message = builder.build();
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"structValue\": {\n"
|
||||
@ -778,7 +779,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " }\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
|
||||
|
||||
builder = TestStruct.newBuilder();
|
||||
builder.setValue(Value.newBuilder().setNullValueValue(0).build());
|
||||
message = builder.build();
|
||||
@ -787,12 +788,23 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " \"value\": null\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
|
||||
builder = TestStruct.newBuilder();
|
||||
listBuilder = builder.getListValueBuilder();
|
||||
listBuilder.addValues(Value.newBuilder().setNumberValue(31831.125).build());
|
||||
listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build());
|
||||
message = builder.build();
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"listValue\": [31831.125, null]\n"
|
||||
+ "}", toJsonString(message));
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
|
||||
public void testAnyFields() throws Exception {
|
||||
TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build();
|
||||
TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build();
|
||||
|
||||
|
||||
// A TypeRegistry must be provided in order to convert Any types.
|
||||
try {
|
||||
toJsonString(message);
|
||||
@ -800,11 +812,11 @@ public class JsonFormatTest extends TestCase {
|
||||
} catch (IOException e) {
|
||||
// Expected.
|
||||
}
|
||||
|
||||
|
||||
JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder()
|
||||
.add(TestAllTypes.getDescriptor()).build();
|
||||
JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry);
|
||||
|
||||
|
||||
assertEquals(
|
||||
"{\n"
|
||||
+ " \"anyValue\": {\n"
|
||||
@ -813,8 +825,8 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " }\n"
|
||||
+ "}" , printer.print(message));
|
||||
assertRoundTripEquals(message, registry);
|
||||
|
||||
|
||||
|
||||
|
||||
// Well-known types have a special formatting when embedded in Any.
|
||||
//
|
||||
// 1. Any in Any.
|
||||
@ -828,7 +840,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " }\n"
|
||||
+ "}", printer.print(anyMessage));
|
||||
assertRoundTripEquals(anyMessage, registry);
|
||||
|
||||
|
||||
// 2. Wrappers in Any.
|
||||
anyMessage = Any.pack(Int32Value.newBuilder().setValue(12345).build());
|
||||
assertEquals(
|
||||
@ -894,7 +906,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " \"value\": \"AQI=\"\n"
|
||||
+ "}", printer.print(anyMessage));
|
||||
assertRoundTripEquals(anyMessage, registry);
|
||||
|
||||
|
||||
// 3. Timestamp in Any.
|
||||
anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z"));
|
||||
assertEquals(
|
||||
@ -903,7 +915,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ " \"value\": \"1969-12-31T23:59:59Z\"\n"
|
||||
+ "}", printer.print(anyMessage));
|
||||
assertRoundTripEquals(anyMessage, registry);
|
||||
|
||||
|
||||
// 4. Duration in Any
|
||||
anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s"));
|
||||
assertEquals(
|
||||
@ -945,7 +957,7 @@ public class JsonFormatTest extends TestCase {
|
||||
+ "}", printer.print(anyMessage));
|
||||
assertRoundTripEquals(anyMessage, registry);
|
||||
}
|
||||
|
||||
|
||||
public void testParserMissingTypeUrl() throws Exception {
|
||||
try {
|
||||
Any.Builder builder = Any.newBuilder();
|
||||
@ -958,7 +970,7 @@ public class JsonFormatTest extends TestCase {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testParserUnexpectedTypeUrl() throws Exception {
|
||||
try {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -970,9 +982,9 @@ public class JsonFormatTest extends TestCase {
|
||||
fail("Exception is expected.");
|
||||
} catch (IOException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testParserRejectTrailingComma() throws Exception {
|
||||
try {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -1000,13 +1012,13 @@ public class JsonFormatTest extends TestCase {
|
||||
// // Expected.
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
public void testParserRejectInvalidBase64() throws Exception {
|
||||
assertRejects("optionalBytes", "!@#$");
|
||||
// We use standard BASE64 with paddings.
|
||||
assertRejects("optionalBytes", "AQI");
|
||||
}
|
||||
|
||||
|
||||
public void testParserRejectInvalidEnumValue() throws Exception {
|
||||
try {
|
||||
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
|
||||
@ -1017,7 +1029,7 @@ public class JsonFormatTest extends TestCase {
|
||||
fail("Exception is expected.");
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
// Expected.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testCustomJsonName() throws Exception {
|
||||
@ -1026,6 +1038,12 @@ public class JsonFormatTest extends TestCase {
|
||||
assertRoundTripEquals(message);
|
||||
}
|
||||
|
||||
public void testDefaultGsonDoesNotHtmlEscape() throws Exception {
|
||||
TestAllTypes message = TestAllTypes.newBuilder().setOptionalString("=").build();
|
||||
assertEquals(
|
||||
"{\n" + " \"optionalString\": \"=\"" + "\n}", JsonFormat.printer().print(message));
|
||||
}
|
||||
|
||||
public void testIncludingDefaultValueFields() throws Exception {
|
||||
TestAllTypes message = TestAllTypes.getDefaultInstance();
|
||||
assertEquals("{\n}", JsonFormat.printer().print(message));
|
||||
|
@ -28,6 +28,36 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// 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 json_test;
|
||||
@ -159,6 +189,7 @@ message TestFieldMask {
|
||||
message TestStruct {
|
||||
google.protobuf.Struct struct_value = 1;
|
||||
google.protobuf.Value value = 2;
|
||||
google.protobuf.ListValue list_value = 3;
|
||||
}
|
||||
|
||||
message TestAny {
|
||||
|
@ -41,11 +41,16 @@ goog.provide('jspb.BinaryMessage');
|
||||
goog.provide('jspb.BuilderFunction');
|
||||
goog.provide('jspb.ByteSource');
|
||||
goog.provide('jspb.ClonerFunction');
|
||||
goog.provide('jspb.ComparerFunction');
|
||||
goog.provide('jspb.ConstBinaryMessage');
|
||||
goog.provide('jspb.PrunerFunction');
|
||||
goog.provide('jspb.ReaderFunction');
|
||||
goog.provide('jspb.RecyclerFunction');
|
||||
goog.provide('jspb.RepeatedFieldType');
|
||||
goog.provide('jspb.ScalarFieldType');
|
||||
goog.provide('jspb.WriterFunction');
|
||||
|
||||
|
||||
goog.forwardDeclare('jspb.Message');
|
||||
goog.forwardDeclare('jsproto.BinaryExtension');
|
||||
|
||||
@ -78,12 +83,30 @@ jspb.BinaryMessage = function() {};
|
||||
jspb.ByteSource;
|
||||
|
||||
|
||||
/**
|
||||
* A scalar field in jspb can be a boolean, number, or string.
|
||||
* @typedef {boolean|number|string}
|
||||
*/
|
||||
jspb.ScalarFieldType;
|
||||
|
||||
|
||||
/**
|
||||
* A repeated field in jspb is an array of scalars, blobs, or messages.
|
||||
* @typedef {!Array<jspb.ScalarFieldType>|
|
||||
!Array<!Uint8Array>|
|
||||
!Array<!jspb.BinaryMessage>}
|
||||
*/
|
||||
jspb.RepeatedFieldType;
|
||||
|
||||
|
||||
/**
|
||||
* A field in jspb can be a scalar, a block of bytes, another proto, or an
|
||||
* array of any of the above.
|
||||
* @typedef {boolean|number|string|Uint8Array|
|
||||
jspb.BinaryMessage|jsproto.BinaryExtension|
|
||||
Array<jspb.AnyFieldType>}
|
||||
* @typedef {jspb.ScalarFieldType|
|
||||
jspb.RepeatedFieldType|
|
||||
!Uint8Array|
|
||||
!jspb.BinaryMessage|
|
||||
!jsproto.BinaryExtension}
|
||||
*/
|
||||
jspb.AnyFieldType;
|
||||
|
||||
@ -124,6 +147,23 @@ jspb.ReaderFunction;
|
||||
jspb.WriterFunction;
|
||||
|
||||
|
||||
/**
|
||||
* A pruner function removes default-valued fields and empty submessages from a
|
||||
* message and returns either the pruned message or null if the entire message
|
||||
* was pruned away.
|
||||
* @typedef {function(?jspb.BinaryMessage):?jspb.BinaryMessage}
|
||||
*/
|
||||
jspb.PrunerFunction;
|
||||
|
||||
|
||||
/**
|
||||
* A comparer function returns true if two protos are equal.
|
||||
* @typedef {!function(?jspb.ConstBinaryMessage,
|
||||
* ?jspb.ConstBinaryMessage):boolean}
|
||||
*/
|
||||
jspb.ComparerFunction;
|
||||
|
||||
|
||||
/**
|
||||
* Field type codes, taken from proto2/public/wire_format_lite.h.
|
||||
* @enum {number}
|
||||
|
@ -223,7 +223,7 @@ jspb.BinaryIterator.prototype.next = function() {
|
||||
jspb.BinaryDecoder = function(opt_bytes, opt_start, opt_length) {
|
||||
/**
|
||||
* Typed byte-wise view of the source buffer.
|
||||
* @private {Uint8Array}
|
||||
* @private {?Uint8Array}
|
||||
*/
|
||||
this.bytes_ = null;
|
||||
|
||||
@ -335,7 +335,7 @@ jspb.BinaryDecoder.prototype.clear = function() {
|
||||
|
||||
/**
|
||||
* Returns the raw buffer.
|
||||
* @return {Uint8Array} The raw buffer.
|
||||
* @return {?Uint8Array} The raw buffer.
|
||||
*/
|
||||
jspb.BinaryDecoder.prototype.getBuffer = function() {
|
||||
return this.bytes_;
|
||||
@ -631,6 +631,7 @@ jspb.BinaryDecoder.prototype.readUnsignedVarint32String = function() {
|
||||
return value.toString();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Reads a 32-bit signed variant and returns its value as a string.
|
||||
*
|
||||
@ -950,14 +951,15 @@ jspb.BinaryDecoder.prototype.readStringWithLength = function() {
|
||||
* Reads a block of raw bytes from the binary stream.
|
||||
*
|
||||
* @param {number} length The number of bytes to read.
|
||||
* @return {Uint8Array} The decoded block of bytes, or null if the length was
|
||||
* invalid.
|
||||
* @return {!Uint8Array} The decoded block of bytes, or an empty block if the
|
||||
* length was invalid.
|
||||
*/
|
||||
jspb.BinaryDecoder.prototype.readBytes = function(length) {
|
||||
if (length < 0 ||
|
||||
this.cursor_ + length > this.bytes_.length) {
|
||||
this.error_ = true;
|
||||
return null;
|
||||
goog.asserts.fail('Invalid byte length!');
|
||||
return new Uint8Array(0);
|
||||
}
|
||||
|
||||
var result = this.bytes_.subarray(this.cursor_, this.cursor_ + length);
|
||||
|
@ -44,11 +44,11 @@
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('jspb.BinaryConstants');
|
||||
goog.require('jspb.BinaryDecoder');
|
||||
goog.require('jspb.BinaryWriter');
|
||||
goog.require('jspb.BinaryEncoder');
|
||||
|
||||
|
||||
/**
|
||||
* Tests raw encoding and decoding of unsigned types.
|
||||
* Tests encoding and decoding of unsigned types.
|
||||
* @param {Function} readValue
|
||||
* @param {Function} writeValue
|
||||
* @param {number} epsilon
|
||||
@ -58,34 +58,38 @@ goog.require('jspb.BinaryWriter');
|
||||
*/
|
||||
function doTestUnsignedValue(readValue,
|
||||
writeValue, epsilon, upperLimit, filter) {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
var encoder = new jspb.BinaryEncoder();
|
||||
|
||||
// Encode zero and limits.
|
||||
writeValue.call(writer, filter(0));
|
||||
writeValue.call(writer, filter(epsilon));
|
||||
writeValue.call(writer, filter(upperLimit));
|
||||
writeValue.call(encoder, filter(0));
|
||||
writeValue.call(encoder, filter(epsilon));
|
||||
writeValue.call(encoder, filter(upperLimit));
|
||||
|
||||
// Encode positive values.
|
||||
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
|
||||
writeValue.call(writer, filter(cursor));
|
||||
writeValue.call(encoder, filter(cursor));
|
||||
}
|
||||
|
||||
var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
|
||||
var decoder = jspb.BinaryDecoder.alloc(encoder.end());
|
||||
|
||||
// Check zero and limits.
|
||||
assertEquals(filter(0), readValue.call(reader));
|
||||
assertEquals(filter(epsilon), readValue.call(reader));
|
||||
assertEquals(filter(upperLimit), readValue.call(reader));
|
||||
assertEquals(filter(0), readValue.call(decoder));
|
||||
assertEquals(filter(epsilon), readValue.call(decoder));
|
||||
assertEquals(filter(upperLimit), readValue.call(decoder));
|
||||
|
||||
// Check positive values.
|
||||
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
|
||||
if (filter(cursor) != readValue.call(reader)) throw 'fail!';
|
||||
if (filter(cursor) != readValue.call(decoder)) throw 'fail!';
|
||||
}
|
||||
|
||||
// Encoding values outside the valid range should assert.
|
||||
assertThrows(function() {writeValue.call(encoder, -1);});
|
||||
assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests raw encoding and decoding of signed types.
|
||||
* Tests encoding and decoding of signed types.
|
||||
* @param {Function} readValue
|
||||
* @param {Function} writeValue
|
||||
* @param {number} epsilon
|
||||
@ -96,44 +100,48 @@ function doTestUnsignedValue(readValue,
|
||||
*/
|
||||
function doTestSignedValue(readValue,
|
||||
writeValue, epsilon, lowerLimit, upperLimit, filter) {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
var encoder = new jspb.BinaryEncoder();
|
||||
|
||||
// Encode zero and limits.
|
||||
writeValue.call(writer, filter(lowerLimit));
|
||||
writeValue.call(writer, filter(-epsilon));
|
||||
writeValue.call(writer, filter(0));
|
||||
writeValue.call(writer, filter(epsilon));
|
||||
writeValue.call(writer, filter(upperLimit));
|
||||
writeValue.call(encoder, filter(lowerLimit));
|
||||
writeValue.call(encoder, filter(-epsilon));
|
||||
writeValue.call(encoder, filter(0));
|
||||
writeValue.call(encoder, filter(epsilon));
|
||||
writeValue.call(encoder, filter(upperLimit));
|
||||
|
||||
var inputValues = [];
|
||||
|
||||
// Encode negative values.
|
||||
for (var cursor = lowerLimit; cursor < -epsilon; cursor /= 1.1) {
|
||||
var val = filter(cursor);
|
||||
writeValue.call(writer, val);
|
||||
writeValue.call(encoder, val);
|
||||
inputValues.push(val);
|
||||
}
|
||||
|
||||
// Encode positive values.
|
||||
for (var cursor = epsilon; cursor < upperLimit; cursor *= 1.1) {
|
||||
var val = filter(cursor);
|
||||
writeValue.call(writer, val);
|
||||
writeValue.call(encoder, val);
|
||||
inputValues.push(val);
|
||||
}
|
||||
|
||||
var reader = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
|
||||
var decoder = jspb.BinaryDecoder.alloc(encoder.end());
|
||||
|
||||
// Check zero and limits.
|
||||
assertEquals(filter(lowerLimit), readValue.call(reader));
|
||||
assertEquals(filter(-epsilon), readValue.call(reader));
|
||||
assertEquals(filter(0), readValue.call(reader));
|
||||
assertEquals(filter(epsilon), readValue.call(reader));
|
||||
assertEquals(filter(upperLimit), readValue.call(reader));
|
||||
assertEquals(filter(lowerLimit), readValue.call(decoder));
|
||||
assertEquals(filter(-epsilon), readValue.call(decoder));
|
||||
assertEquals(filter(0), readValue.call(decoder));
|
||||
assertEquals(filter(epsilon), readValue.call(decoder));
|
||||
assertEquals(filter(upperLimit), readValue.call(decoder));
|
||||
|
||||
// Verify decoded values.
|
||||
for (var i = 0; i < inputValues.length; i++) {
|
||||
assertEquals(inputValues[i], readValue.call(reader));
|
||||
assertEquals(inputValues[i], readValue.call(decoder));
|
||||
}
|
||||
|
||||
// Encoding values outside the valid range should assert.
|
||||
assertThrows(function() {writeValue.call(encoder, lowerLimit * 1.1);});
|
||||
assertThrows(function() {writeValue.call(encoder, upperLimit * 1.1);});
|
||||
}
|
||||
|
||||
describe('binaryDecoderTest', function() {
|
||||
@ -169,7 +177,7 @@ describe('binaryDecoderTest', function() {
|
||||
* Tests reading 64-bit integers as hash strings.
|
||||
*/
|
||||
it('testHashStrings', function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
var encoder = new jspb.BinaryEncoder();
|
||||
|
||||
var hashA = String.fromCharCode(0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00);
|
||||
@ -180,17 +188,17 @@ describe('binaryDecoderTest', function() {
|
||||
var hashD = String.fromCharCode(0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF);
|
||||
|
||||
writer.rawWriteVarintHash64(hashA);
|
||||
writer.rawWriteVarintHash64(hashB);
|
||||
writer.rawWriteVarintHash64(hashC);
|
||||
writer.rawWriteVarintHash64(hashD);
|
||||
encoder.writeVarintHash64(hashA);
|
||||
encoder.writeVarintHash64(hashB);
|
||||
encoder.writeVarintHash64(hashC);
|
||||
encoder.writeVarintHash64(hashD);
|
||||
|
||||
writer.rawWriteFixedHash64(hashA);
|
||||
writer.rawWriteFixedHash64(hashB);
|
||||
writer.rawWriteFixedHash64(hashC);
|
||||
writer.rawWriteFixedHash64(hashD);
|
||||
encoder.writeFixedHash64(hashA);
|
||||
encoder.writeFixedHash64(hashB);
|
||||
encoder.writeFixedHash64(hashC);
|
||||
encoder.writeFixedHash64(hashD);
|
||||
|
||||
var decoder = jspb.BinaryDecoder.alloc(writer.getResultBuffer());
|
||||
var decoder = jspb.BinaryDecoder.alloc(encoder.end());
|
||||
|
||||
assertEquals(hashA, decoder.readVarintHash64());
|
||||
assertEquals(hashB, decoder.readVarintHash64());
|
||||
@ -214,8 +222,8 @@ describe('binaryDecoderTest', function() {
|
||||
assertThrows(function() {decoder.readUint64()});
|
||||
|
||||
// Overlong varints should trigger assertions.
|
||||
decoder.setBlock(
|
||||
[255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0]);
|
||||
decoder.setBlock([255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 0]);
|
||||
assertThrows(function() {decoder.readUnsignedVarint64()});
|
||||
decoder.reset();
|
||||
assertThrows(function() {decoder.readSignedVarint64()});
|
||||
@ -244,61 +252,61 @@ describe('binaryDecoderTest', function() {
|
||||
|
||||
|
||||
/**
|
||||
* Tests raw encoding and decoding of unsigned integers.
|
||||
* Tests encoding and decoding of unsigned integers.
|
||||
*/
|
||||
it('testRawUnsigned', function() {
|
||||
it('testUnsignedIntegers', function() {
|
||||
doTestUnsignedValue(
|
||||
jspb.BinaryDecoder.prototype.readUint8,
|
||||
jspb.BinaryWriter.prototype.rawWriteUint8,
|
||||
jspb.BinaryEncoder.prototype.writeUint8,
|
||||
1, 0xFF, Math.round);
|
||||
|
||||
doTestUnsignedValue(
|
||||
jspb.BinaryDecoder.prototype.readUint16,
|
||||
jspb.BinaryWriter.prototype.rawWriteUint16,
|
||||
jspb.BinaryEncoder.prototype.writeUint16,
|
||||
1, 0xFFFF, Math.round);
|
||||
|
||||
doTestUnsignedValue(
|
||||
jspb.BinaryDecoder.prototype.readUint32,
|
||||
jspb.BinaryWriter.prototype.rawWriteUint32,
|
||||
jspb.BinaryEncoder.prototype.writeUint32,
|
||||
1, 0xFFFFFFFF, Math.round);
|
||||
|
||||
doTestUnsignedValue(
|
||||
jspb.BinaryDecoder.prototype.readUint64,
|
||||
jspb.BinaryWriter.prototype.rawWriteUint64,
|
||||
jspb.BinaryEncoder.prototype.writeUint64,
|
||||
1, Math.pow(2, 64) - 1025, Math.round);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Tests raw encoding and decoding of signed integers.
|
||||
* Tests encoding and decoding of signed integers.
|
||||
*/
|
||||
it('testRawSigned', function() {
|
||||
it('testSignedIntegers', function() {
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readInt8,
|
||||
jspb.BinaryWriter.prototype.rawWriteInt8,
|
||||
jspb.BinaryEncoder.prototype.writeInt8,
|
||||
1, -0x80, 0x7F, Math.round);
|
||||
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readInt16,
|
||||
jspb.BinaryWriter.prototype.rawWriteInt16,
|
||||
jspb.BinaryEncoder.prototype.writeInt16,
|
||||
1, -0x8000, 0x7FFF, Math.round);
|
||||
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readInt32,
|
||||
jspb.BinaryWriter.prototype.rawWriteInt32,
|
||||
jspb.BinaryEncoder.prototype.writeInt32,
|
||||
1, -0x80000000, 0x7FFFFFFF, Math.round);
|
||||
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readInt64,
|
||||
jspb.BinaryWriter.prototype.rawWriteInt64,
|
||||
jspb.BinaryEncoder.prototype.writeInt64,
|
||||
1, -Math.pow(2, 63), Math.pow(2, 63) - 513, Math.round);
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Tests raw encoding and decoding of floats.
|
||||
* Tests encoding and decoding of floats.
|
||||
*/
|
||||
it('testRawFloats', function() {
|
||||
it('testFloats', function() {
|
||||
/**
|
||||
* @param {number} x
|
||||
* @return {number}
|
||||
@ -310,7 +318,7 @@ describe('binaryDecoderTest', function() {
|
||||
}
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readFloat,
|
||||
jspb.BinaryWriter.prototype.rawWriteFloat,
|
||||
jspb.BinaryEncoder.prototype.writeFloat,
|
||||
jspb.BinaryConstants.FLOAT32_EPS,
|
||||
-jspb.BinaryConstants.FLOAT32_MAX,
|
||||
jspb.BinaryConstants.FLOAT32_MAX,
|
||||
@ -318,7 +326,7 @@ describe('binaryDecoderTest', function() {
|
||||
|
||||
doTestSignedValue(
|
||||
jspb.BinaryDecoder.prototype.readDouble,
|
||||
jspb.BinaryWriter.prototype.rawWriteDouble,
|
||||
jspb.BinaryEncoder.prototype.writeDouble,
|
||||
jspb.BinaryConstants.FLOAT64_EPS * 10,
|
||||
-jspb.BinaryConstants.FLOAT64_MAX,
|
||||
jspb.BinaryConstants.FLOAT64_MAX,
|
||||
|
430
js/binary/encoder.js
Normal file
430
js/binary/encoder.js
Normal file
@ -0,0 +1,430 @@
|
||||
// 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.
|
||||
|
||||
/**
|
||||
* @fileoverview BinaryEncode defines methods for encoding Javascript values
|
||||
* into arrays of bytes compatible with the Protocol Buffer wire format.
|
||||
*
|
||||
* @author aappleby@google.com (Austin Appleby)
|
||||
*/
|
||||
|
||||
goog.provide('jspb.BinaryEncoder');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('jspb.BinaryConstants');
|
||||
goog.require('jspb.utils');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* BinaryEncoder implements encoders for all the wire types specified in
|
||||
* https://developers.google.com/protocol-buffers/docs/encoding.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
*/
|
||||
jspb.BinaryEncoder = function() {
|
||||
/** @private {!Array.<number>} */
|
||||
this.buffer_ = [];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number}
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.length = function() {
|
||||
return this.buffer_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Array.<number>}
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.end = function() {
|
||||
var buffer = this.buffer_;
|
||||
this.buffer_ = [];
|
||||
return buffer;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a 64-bit integer in 32:32 split representation into its wire-format
|
||||
* varint representation and stores it in the buffer.
|
||||
* @param {number} lowBits The low 32 bits of the int.
|
||||
* @param {number} highBits The high 32 bits of the int.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeSplitVarint64 = function(lowBits, highBits) {
|
||||
goog.asserts.assert(lowBits == Math.floor(lowBits));
|
||||
goog.asserts.assert(highBits == Math.floor(highBits));
|
||||
goog.asserts.assert((lowBits >= 0) &&
|
||||
(lowBits < jspb.BinaryConstants.TWO_TO_32));
|
||||
goog.asserts.assert((highBits >= 0) &&
|
||||
(highBits < jspb.BinaryConstants.TWO_TO_32));
|
||||
|
||||
// Break the binary representation into chunks of 7 bits, set the 8th bit
|
||||
// in each chunk if it's not the final chunk, and append to the result.
|
||||
while (highBits > 0 || lowBits > 127) {
|
||||
this.buffer_.push((lowBits & 0x7f) | 0x80);
|
||||
lowBits = ((lowBits >>> 7) | (highBits << 25)) >>> 0;
|
||||
highBits = highBits >>> 7;
|
||||
}
|
||||
this.buffer_.push(lowBits);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a 32-bit unsigned integer into its wire-format varint representation
|
||||
* and stores it in the buffer.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUnsignedVarint32 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_32));
|
||||
|
||||
while (value > 127) {
|
||||
this.buffer_.push((value & 0x7f) | 0x80);
|
||||
value = value >>> 7;
|
||||
}
|
||||
|
||||
this.buffer_.push(value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a 32-bit signed integer into its wire-format varint representation
|
||||
* and stores it in the buffer.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeSignedVarint32 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_31));
|
||||
|
||||
// Use the unsigned version if the value is not negative.
|
||||
if (value >= 0) {
|
||||
this.writeUnsignedVarint32(value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write nine bytes with a _signed_ right shift so we preserve the sign bit.
|
||||
for (var i = 0; i < 9; i++) {
|
||||
this.buffer_.push((value & 0x7f) | 0x80);
|
||||
value = value >> 7;
|
||||
}
|
||||
|
||||
// The above loop writes out 63 bits, so the last byte is always the sign bit
|
||||
// which is always set for negative numbers.
|
||||
this.buffer_.push(1);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a 64-bit unsigned integer into its wire-format varint representation
|
||||
* and stores it in the buffer. Integers that are not representable in 64 bits
|
||||
* will be truncated.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUnsignedVarint64 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_64));
|
||||
jspb.utils.splitInt64(value);
|
||||
this.writeSplitVarint64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a 64-bit signed integer into its wire-format varint representation
|
||||
* and stores it in the buffer. Integers that are not representable in 64 bits
|
||||
* will be truncated.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeSignedVarint64 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_63));
|
||||
jspb.utils.splitInt64(value);
|
||||
this.writeSplitVarint64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
|
||||
* representation and stores it in the buffer.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeZigzagVarint32 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_31));
|
||||
this.writeUnsignedVarint32(((value << 1) ^ (value >> 31)) >>> 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a JavaScript integer into its wire-format, zigzag-encoded varint
|
||||
* representation and stores it in the buffer. Integers not representable in 64
|
||||
* bits will be truncated.
|
||||
* @param {number} value The integer to convert.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeZigzagVarint64 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_63));
|
||||
jspb.utils.splitZigzag64(value);
|
||||
this.writeSplitVarint64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 8-bit unsigned integer to the buffer. Numbers outside the range
|
||||
* [0,2^8) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUint8 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) && (value < 256));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 16-bit unsigned integer to the buffer. Numbers outside the
|
||||
* range [0,2^16) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUint16 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) && (value < 65536));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
this.buffer_.push((value >>> 8) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 32-bit unsigned integer to the buffer. Numbers outside the
|
||||
* range [0,2^32) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUint32 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_32));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
this.buffer_.push((value >>> 8) & 0xFF);
|
||||
this.buffer_.push((value >>> 16) & 0xFF);
|
||||
this.buffer_.push((value >>> 24) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 64-bit unsigned integer to the buffer. Numbers outside the
|
||||
* range [0,2^64) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeUint64 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= 0) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_64));
|
||||
jspb.utils.splitUint64(value);
|
||||
this.writeUint32(jspb.utils.split64Low);
|
||||
this.writeUint32(jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 8-bit integer to the buffer. Numbers outside the range
|
||||
* [-2^7,2^7) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeInt8 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -128) && (value < 128));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 16-bit integer to the buffer. Numbers outside the range
|
||||
* [-2^15,2^15) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeInt16 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -32768) && (value < 32768));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
this.buffer_.push((value >>> 8) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 32-bit integer to the buffer. Numbers outside the range
|
||||
* [-2^31,2^31) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeInt32 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_31));
|
||||
this.buffer_.push((value >>> 0) & 0xFF);
|
||||
this.buffer_.push((value >>> 8) & 0xFF);
|
||||
this.buffer_.push((value >>> 16) & 0xFF);
|
||||
this.buffer_.push((value >>> 24) & 0xFF);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 64-bit integer to the buffer. Numbers outside the range
|
||||
* [-2^63,2^63) will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeInt64 = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_63) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_63));
|
||||
jspb.utils.splitInt64(value);
|
||||
this.writeUint32(jspb.utils.split64Low);
|
||||
this.writeUint32(jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a single-precision floating point value to the buffer. Numbers
|
||||
* requiring more than 32 bits of precision will be truncated.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeFloat = function(value) {
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.FLOAT32_MAX) &&
|
||||
(value <= jspb.BinaryConstants.FLOAT32_MAX));
|
||||
jspb.utils.splitFloat32(value);
|
||||
this.writeUint32(jspb.utils.split64Low);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a double-precision floating point value to the buffer. As this is
|
||||
* the native format used by JavaScript, no precision will be lost.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeDouble = function(value) {
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.FLOAT64_MAX) &&
|
||||
(value <= jspb.BinaryConstants.FLOAT64_MAX));
|
||||
jspb.utils.splitFloat64(value);
|
||||
this.writeUint32(jspb.utils.split64Low);
|
||||
this.writeUint32(jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a boolean value to the buffer as a varint.
|
||||
* @param {boolean} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeBool = function(value) {
|
||||
goog.asserts.assert(goog.isBoolean(value));
|
||||
this.buffer_.push(value ? 1 : 0);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes an enum value to the buffer as a varint.
|
||||
* @param {number} value The value to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeEnum = function(value) {
|
||||
goog.asserts.assert(value == Math.floor(value));
|
||||
goog.asserts.assert((value >= -jspb.BinaryConstants.TWO_TO_31) &&
|
||||
(value < jspb.BinaryConstants.TWO_TO_31));
|
||||
this.writeSignedVarint32(value);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes an arbitrary byte array to the buffer.
|
||||
* @param {!Uint8Array} bytes The array of bytes to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeBytes = function(bytes) {
|
||||
this.buffer_.push.apply(this.buffer_, bytes);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the
|
||||
* buffer as a varint.
|
||||
* @param {string} hash The hash to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeVarintHash64 = function(hash) {
|
||||
jspb.utils.splitHash64(hash);
|
||||
this.writeSplitVarint64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a 64-bit hash string (8 characters @ 8 bits of data each) to the
|
||||
* buffer as a fixed64.
|
||||
* @param {string} hash The hash to write.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeFixedHash64 = function(hash) {
|
||||
jspb.utils.splitHash64(hash);
|
||||
this.writeUint32(jspb.utils.split64Low);
|
||||
this.writeUint32(jspb.utils.split64High);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Writes a UTF16 Javascript string to the buffer encoded as UTF8.
|
||||
* TODO(aappleby): Add support for surrogate pairs, reject unpaired surrogates.
|
||||
* @param {string} value The string to write.
|
||||
* @return {number} The number of bytes used to encode the string.
|
||||
*/
|
||||
jspb.BinaryEncoder.prototype.writeString = function(value) {
|
||||
var oldLength = this.buffer_.length;
|
||||
|
||||
// UTF16 to UTF8 conversion loop swiped from goog.crypt.stringToUtf8ByteArray.
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
var c = value.charCodeAt(i);
|
||||
if (c < 128) {
|
||||
this.buffer_.push(c);
|
||||
} else if (c < 2048) {
|
||||
this.buffer_.push((c >> 6) | 192);
|
||||
this.buffer_.push((c & 63) | 128);
|
||||
} else {
|
||||
this.buffer_.push((c >> 12) | 224);
|
||||
this.buffer_.push(((c >> 6) & 63) | 128);
|
||||
this.buffer_.push((c & 63) | 128);
|
||||
}
|
||||
}
|
||||
|
||||
var length = this.buffer_.length - oldLength;
|
||||
return length;
|
||||
};
|
@ -30,7 +30,9 @@
|
||||
|
||||
// Test suite is written using Jasmine -- see http://jasmine.github.io/
|
||||
|
||||
goog.require('goog.crypt.base64');
|
||||
goog.require('goog.testing.asserts');
|
||||
goog.require('jspb.Message');
|
||||
|
||||
// CommonJS-LoadFromFile: ../testbinary_pb proto.jspb.test
|
||||
goog.require('proto.jspb.test.ExtendsWithMessage');
|
||||
@ -38,9 +40,61 @@ goog.require('proto.jspb.test.ForeignEnum');
|
||||
goog.require('proto.jspb.test.ForeignMessage');
|
||||
goog.require('proto.jspb.test.TestAllTypes');
|
||||
goog.require('proto.jspb.test.TestExtendable');
|
||||
goog.require('proto.jspb.test.extendOptionalBool');
|
||||
goog.require('proto.jspb.test.extendOptionalBytes');
|
||||
goog.require('proto.jspb.test.extendOptionalDouble');
|
||||
goog.require('proto.jspb.test.extendOptionalFixed32');
|
||||
goog.require('proto.jspb.test.extendOptionalFixed64');
|
||||
goog.require('proto.jspb.test.extendOptionalFloat');
|
||||
goog.require('proto.jspb.test.extendOptionalForeignEnum');
|
||||
goog.require('proto.jspb.test.extendOptionalInt32');
|
||||
goog.require('proto.jspb.test.extendOptionalInt64');
|
||||
goog.require('proto.jspb.test.extendOptionalSfixed32');
|
||||
goog.require('proto.jspb.test.extendOptionalSfixed64');
|
||||
goog.require('proto.jspb.test.extendOptionalSint32');
|
||||
goog.require('proto.jspb.test.extendOptionalSint64');
|
||||
goog.require('proto.jspb.test.extendOptionalString');
|
||||
goog.require('proto.jspb.test.extendOptionalUint32');
|
||||
goog.require('proto.jspb.test.extendOptionalUint64');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedBoolList');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedDoubleList');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedFixed32List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedFixed64List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedFloatList');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedForeignEnumList');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedInt32List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedInt64List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedSfixed32List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedSfixed64List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedSint32List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedSint64List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedUint32List');
|
||||
goog.require('proto.jspb.test.extendPackedRepeatedUint64List');
|
||||
goog.require('proto.jspb.test.extendRepeatedBoolList');
|
||||
goog.require('proto.jspb.test.extendRepeatedBytesList');
|
||||
goog.require('proto.jspb.test.extendRepeatedDoubleList');
|
||||
goog.require('proto.jspb.test.extendRepeatedFixed32List');
|
||||
goog.require('proto.jspb.test.extendRepeatedFixed64List');
|
||||
goog.require('proto.jspb.test.extendRepeatedFloatList');
|
||||
goog.require('proto.jspb.test.extendRepeatedForeignEnumList');
|
||||
goog.require('proto.jspb.test.extendRepeatedInt32List');
|
||||
goog.require('proto.jspb.test.extendRepeatedInt64List');
|
||||
goog.require('proto.jspb.test.extendRepeatedSfixed32List');
|
||||
goog.require('proto.jspb.test.extendRepeatedSfixed64List');
|
||||
goog.require('proto.jspb.test.extendRepeatedSint32List');
|
||||
goog.require('proto.jspb.test.extendRepeatedSint64List');
|
||||
goog.require('proto.jspb.test.extendRepeatedStringList');
|
||||
goog.require('proto.jspb.test.extendRepeatedUint32List');
|
||||
goog.require('proto.jspb.test.extendRepeatedUint64List');
|
||||
|
||||
|
||||
var suite = {};
|
||||
|
||||
var BYTES = new Uint8Array([1, 2, 8, 9]);
|
||||
|
||||
var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
|
||||
|
||||
|
||||
/**
|
||||
* Helper: fill all fields on a TestAllTypes message.
|
||||
* @param {proto.jspb.test.TestAllTypes} msg
|
||||
@ -62,7 +116,7 @@ function fillAllFields(msg) {
|
||||
msg.setOptionalDouble(-1.5);
|
||||
msg.setOptionalBool(true);
|
||||
msg.setOptionalString('hello world');
|
||||
msg.setOptionalBytes('bytes');
|
||||
msg.setOptionalBytes(BYTES);
|
||||
msg.setOptionalGroup(new proto.jspb.test.TestAllTypes.OptionalGroup());
|
||||
msg.getOptionalGroup().setA(100);
|
||||
var submsg = new proto.jspb.test.ForeignMessage();
|
||||
@ -71,6 +125,7 @@ function fillAllFields(msg) {
|
||||
msg.setOptionalForeignEnum(proto.jspb.test.ForeignEnum.FOREIGN_FOO);
|
||||
msg.setOneofString('oneof');
|
||||
|
||||
|
||||
msg.setRepeatedInt32List([-42]);
|
||||
msg.setRepeatedInt64List([-0x7fffffff00000000]);
|
||||
msg.setRepeatedUint32List([0x80000000]);
|
||||
@ -85,7 +140,7 @@ function fillAllFields(msg) {
|
||||
msg.setRepeatedDoubleList([-1.5]);
|
||||
msg.setRepeatedBoolList([true]);
|
||||
msg.setRepeatedStringList(['hello world']);
|
||||
msg.setRepeatedBytesList(['bytes']);
|
||||
msg.setRepeatedBytesList([BYTES, BYTES]);
|
||||
msg.setRepeatedGroupList([new proto.jspb.test.TestAllTypes.RepeatedGroup()]);
|
||||
msg.getRepeatedGroupList()[0].setA(100);
|
||||
submsg = new proto.jspb.test.ForeignMessage();
|
||||
@ -106,106 +161,115 @@ function fillAllFields(msg) {
|
||||
msg.setPackedRepeatedFloatList([1.5]);
|
||||
msg.setPackedRepeatedDoubleList([-1.5]);
|
||||
msg.setPackedRepeatedBoolList([true]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper: compare a bytes field to a string with codepoints 0--255.
|
||||
* Helper: compare a bytes field to an expected value
|
||||
* @param {Uint8Array|string} arr
|
||||
* @param {string} str
|
||||
* @param {Uint8Array} expected
|
||||
* @return {boolean}
|
||||
*/
|
||||
function bytesCompare(arr, str) {
|
||||
if (arr.length != str.length) {
|
||||
function bytesCompare(arr, expected) {
|
||||
if (goog.isString(arr)) {
|
||||
arr = goog.crypt.base64.decodeStringToUint8Array(arr);
|
||||
}
|
||||
if (arr.length != expected.length) {
|
||||
return false;
|
||||
}
|
||||
if (typeof arr == 'string') {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr.charCodeAt(i) != str.charCodeAt(i)) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != expected[i]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != str.charCodeAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper: verify contents of given TestAllTypes message as set by
|
||||
* fillAllFields().
|
||||
* @param {proto.jspb.test.TestAllTypes} msg
|
||||
* @param {proto.jspb.test.TestAllTypes} original
|
||||
* @param {proto.jspb.test.TestAllTypes} copy
|
||||
*/
|
||||
function checkAllFields(msg) {
|
||||
assertEquals(msg.getOptionalInt32(), -42);
|
||||
assertEquals(msg.getOptionalInt64(), -0x7fffffff00000000);
|
||||
assertEquals(msg.getOptionalUint32(), 0x80000000);
|
||||
assertEquals(msg.getOptionalUint64(), 0xf000000000000000);
|
||||
assertEquals(msg.getOptionalSint32(), -100);
|
||||
assertEquals(msg.getOptionalSint64(), -0x8000000000000000);
|
||||
assertEquals(msg.getOptionalFixed32(), 1234);
|
||||
assertEquals(msg.getOptionalFixed64(), 0x1234567800000000);
|
||||
assertEquals(msg.getOptionalSfixed32(), -1234);
|
||||
assertEquals(msg.getOptionalSfixed64(), -0x1234567800000000);
|
||||
assertEquals(msg.getOptionalFloat(), 1.5);
|
||||
assertEquals(msg.getOptionalDouble(), -1.5);
|
||||
assertEquals(msg.getOptionalBool(), true);
|
||||
assertEquals(msg.getOptionalString(), 'hello world');
|
||||
assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes'));
|
||||
assertEquals(msg.getOptionalGroup().getA(), 100);
|
||||
assertEquals(msg.getOptionalForeignMessage().getC(), 16);
|
||||
assertEquals(msg.getOptionalForeignEnum(),
|
||||
function checkAllFields(original, copy) {
|
||||
assertTrue(jspb.Message.equals(original, copy));
|
||||
|
||||
assertEquals(copy.getOptionalInt32(), -42);
|
||||
assertEquals(copy.getOptionalInt64(), -0x7fffffff00000000);
|
||||
assertEquals(copy.getOptionalUint32(), 0x80000000);
|
||||
assertEquals(copy.getOptionalUint64(), 0xf000000000000000);
|
||||
assertEquals(copy.getOptionalSint32(), -100);
|
||||
assertEquals(copy.getOptionalSint64(), -0x8000000000000000);
|
||||
assertEquals(copy.getOptionalFixed32(), 1234);
|
||||
assertEquals(copy.getOptionalFixed64(), 0x1234567800000000);
|
||||
assertEquals(copy.getOptionalSfixed32(), -1234);
|
||||
assertEquals(copy.getOptionalSfixed64(), -0x1234567800000000);
|
||||
assertEquals(copy.getOptionalFloat(), 1.5);
|
||||
assertEquals(copy.getOptionalDouble(), -1.5);
|
||||
assertEquals(copy.getOptionalBool(), true);
|
||||
assertEquals(copy.getOptionalString(), 'hello world');
|
||||
assertEquals(true, bytesCompare(copy.getOptionalBytes(), BYTES));
|
||||
assertEquals(true, bytesCompare(copy.getOptionalBytes_asU8(), BYTES));
|
||||
assertEquals(
|
||||
copy.getOptionalBytes_asB64(), goog.crypt.base64.encodeByteArray(BYTES));
|
||||
|
||||
assertEquals(copy.getOptionalGroup().getA(), 100);
|
||||
assertEquals(copy.getOptionalForeignMessage().getC(), 16);
|
||||
assertEquals(copy.getOptionalForeignEnum(),
|
||||
proto.jspb.test.ForeignEnum.FOREIGN_FOO);
|
||||
assertEquals(msg.getOneofString(), 'oneof');
|
||||
assertEquals(msg.getOneofFieldCase(),
|
||||
|
||||
|
||||
assertEquals(copy.getOneofString(), 'oneof');
|
||||
assertEquals(copy.getOneofFieldCase(),
|
||||
proto.jspb.test.TestAllTypes.OneofFieldCase.ONEOF_STRING);
|
||||
|
||||
assertElementsEquals(msg.getRepeatedInt32List(), [-42]);
|
||||
assertElementsEquals(msg.getRepeatedInt64List(), [-0x7fffffff00000000]);
|
||||
assertElementsEquals(msg.getRepeatedUint32List(), [0x80000000]);
|
||||
assertElementsEquals(msg.getRepeatedUint64List(), [0xf000000000000000]);
|
||||
assertElementsEquals(msg.getRepeatedSint32List(), [-100]);
|
||||
assertElementsEquals(msg.getRepeatedSint64List(), [-0x8000000000000000]);
|
||||
assertElementsEquals(msg.getRepeatedFixed32List(), [1234]);
|
||||
assertElementsEquals(msg.getRepeatedFixed64List(), [0x1234567800000000]);
|
||||
assertElementsEquals(msg.getRepeatedSfixed32List(), [-1234]);
|
||||
assertElementsEquals(msg.getRepeatedSfixed64List(), [-0x1234567800000000]);
|
||||
assertElementsEquals(msg.getRepeatedFloatList(), [1.5]);
|
||||
assertElementsEquals(msg.getRepeatedDoubleList(), [-1.5]);
|
||||
assertElementsEquals(msg.getRepeatedBoolList(), [true]);
|
||||
assertElementsEquals(msg.getRepeatedStringList(), ['hello world']);
|
||||
assertEquals(msg.getRepeatedBytesList().length, 1);
|
||||
assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes'));
|
||||
assertEquals(msg.getRepeatedGroupList().length, 1);
|
||||
assertEquals(msg.getRepeatedGroupList()[0].getA(), 100);
|
||||
assertEquals(msg.getRepeatedForeignMessageList().length, 1);
|
||||
assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
|
||||
assertElementsEquals(msg.getRepeatedForeignEnumList(),
|
||||
assertElementsEquals(copy.getRepeatedInt32List(), [-42]);
|
||||
assertElementsEquals(copy.getRepeatedInt64List(), [-0x7fffffff00000000]);
|
||||
assertElementsEquals(copy.getRepeatedUint32List(), [0x80000000]);
|
||||
assertElementsEquals(copy.getRepeatedUint64List(), [0xf000000000000000]);
|
||||
assertElementsEquals(copy.getRepeatedSint32List(), [-100]);
|
||||
assertElementsEquals(copy.getRepeatedSint64List(), [-0x8000000000000000]);
|
||||
assertElementsEquals(copy.getRepeatedFixed32List(), [1234]);
|
||||
assertElementsEquals(copy.getRepeatedFixed64List(), [0x1234567800000000]);
|
||||
assertElementsEquals(copy.getRepeatedSfixed32List(), [-1234]);
|
||||
assertElementsEquals(copy.getRepeatedSfixed64List(), [-0x1234567800000000]);
|
||||
assertElementsEquals(copy.getRepeatedFloatList(), [1.5]);
|
||||
assertElementsEquals(copy.getRepeatedDoubleList(), [-1.5]);
|
||||
assertElementsEquals(copy.getRepeatedBoolList(), [true]);
|
||||
assertElementsEquals(copy.getRepeatedStringList(), ['hello world']);
|
||||
assertEquals(copy.getRepeatedBytesList().length, 2);
|
||||
assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[0], BYTES));
|
||||
assertEquals(true, bytesCompare(copy.getRepeatedBytesList()[0], BYTES));
|
||||
assertEquals(true, bytesCompare(copy.getRepeatedBytesList_asU8()[1], BYTES));
|
||||
assertEquals(copy.getRepeatedBytesList_asB64()[0], BYTES_B64);
|
||||
assertEquals(copy.getRepeatedBytesList_asB64()[1], BYTES_B64);
|
||||
assertEquals(copy.getRepeatedGroupList().length, 1);
|
||||
assertEquals(copy.getRepeatedGroupList()[0].getA(), 100);
|
||||
assertEquals(copy.getRepeatedForeignMessageList().length, 1);
|
||||
assertEquals(copy.getRepeatedForeignMessageList()[0].getC(), 1000);
|
||||
assertElementsEquals(copy.getRepeatedForeignEnumList(),
|
||||
[proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
|
||||
|
||||
assertElementsEquals(msg.getPackedRepeatedInt32List(), [-42]);
|
||||
assertElementsEquals(msg.getPackedRepeatedInt64List(),
|
||||
assertElementsEquals(copy.getPackedRepeatedInt32List(), [-42]);
|
||||
assertElementsEquals(copy.getPackedRepeatedInt64List(),
|
||||
[-0x7fffffff00000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedUint32List(), [0x80000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedUint64List(), [0xf000000000000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedSint32List(), [-100]);
|
||||
assertElementsEquals(msg.getPackedRepeatedSint64List(),
|
||||
assertElementsEquals(copy.getPackedRepeatedUint32List(), [0x80000000]);
|
||||
assertElementsEquals(copy.getPackedRepeatedUint64List(),
|
||||
[0xf000000000000000]);
|
||||
assertElementsEquals(copy.getPackedRepeatedSint32List(), [-100]);
|
||||
assertElementsEquals(copy.getPackedRepeatedSint64List(),
|
||||
[-0x8000000000000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedFixed32List(), [1234]);
|
||||
assertElementsEquals(msg.getPackedRepeatedFixed64List(),
|
||||
assertElementsEquals(copy.getPackedRepeatedFixed32List(), [1234]);
|
||||
assertElementsEquals(copy.getPackedRepeatedFixed64List(),
|
||||
[0x1234567800000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedSfixed32List(), [-1234]);
|
||||
assertElementsEquals(msg.getPackedRepeatedSfixed64List(),
|
||||
assertElementsEquals(copy.getPackedRepeatedSfixed32List(), [-1234]);
|
||||
assertElementsEquals(copy.getPackedRepeatedSfixed64List(),
|
||||
[-0x1234567800000000]);
|
||||
assertElementsEquals(msg.getPackedRepeatedFloatList(), [1.5]);
|
||||
assertElementsEquals(msg.getPackedRepeatedDoubleList(), [-1.5]);
|
||||
assertElementsEquals(msg.getPackedRepeatedBoolList(), [true]);
|
||||
assertElementsEquals(copy.getPackedRepeatedFloatList(), [1.5]);
|
||||
assertElementsEquals(copy.getPackedRepeatedDoubleList(), [-1.5]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -242,14 +306,13 @@ function checkExtensions(msg) {
|
||||
msg.getExtension(proto.jspb.test.extendOptionalBool));
|
||||
assertEquals('hello world',
|
||||
msg.getExtension(proto.jspb.test.extendOptionalString));
|
||||
assertEquals(true,
|
||||
bytesCompare(msg.getExtension(proto.jspb.test.extendOptionalBytes),
|
||||
'bytes'));
|
||||
assertEquals(
|
||||
true, bytesCompare(
|
||||
msg.getExtension(proto.jspb.test.extendOptionalBytes), BYTES));
|
||||
assertEquals(16,
|
||||
msg.getExtension(
|
||||
proto.jspb.test.ExtendsWithMessage.optionalExtension).getFoo());
|
||||
assertEquals(proto.jspb.test.ForeignEnum.FOREIGN_FOO,
|
||||
msg.getExtension(proto.jspb.test.extendOptionalForeignEnum));
|
||||
|
||||
|
||||
assertElementsEquals(
|
||||
msg.getExtension(proto.jspb.test.extendRepeatedInt32List),
|
||||
@ -293,10 +356,10 @@ function checkExtensions(msg) {
|
||||
assertElementsEquals(
|
||||
msg.getExtension(proto.jspb.test.extendRepeatedStringList),
|
||||
['hello world']);
|
||||
assertEquals(true,
|
||||
assertEquals(
|
||||
true,
|
||||
bytesCompare(
|
||||
msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0],
|
||||
'bytes'));
|
||||
msg.getExtension(proto.jspb.test.extendRepeatedBytesList)[0], BYTES));
|
||||
assertEquals(1000,
|
||||
msg.getExtension(
|
||||
proto.jspb.test.ExtendsWithMessage.repeatedExtensionList)[0]
|
||||
@ -305,6 +368,7 @@ function checkExtensions(msg) {
|
||||
msg.getExtension(proto.jspb.test.extendRepeatedForeignEnumList),
|
||||
[proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
|
||||
|
||||
|
||||
assertElementsEquals(
|
||||
msg.getExtension(proto.jspb.test.extendPackedRepeatedInt32List),
|
||||
[-42]);
|
||||
@ -347,6 +411,7 @@ function checkExtensions(msg) {
|
||||
assertElementsEquals(
|
||||
msg.getExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList),
|
||||
[proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -360,9 +425,82 @@ describe('protoBinaryTest', function() {
|
||||
fillAllFields(msg);
|
||||
var encoded = msg.serializeBinary();
|
||||
var decoded = proto.jspb.test.TestAllTypes.deserializeBinary(encoded);
|
||||
checkAllFields(decoded);
|
||||
checkAllFields(msg, decoded);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that base64 string and Uint8Array are interchangeable in bytes fields.
|
||||
*/
|
||||
it('testBytesFieldsGettersInterop', function() {
|
||||
var msg = new proto.jspb.test.TestAllTypes();
|
||||
// Set from a base64 string and check all the getters work.
|
||||
msg.setOptionalBytes(BYTES_B64);
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
// Test binary serialize round trip doesn't break it.
|
||||
msg = proto.jspb.test.TestAllTypes.deserializeBinary(msg.serializeBinary());
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
msg = new proto.jspb.test.TestAllTypes();
|
||||
// Set from a Uint8Array and check all the getters work.
|
||||
msg.setOptionalBytes(BYTES);
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that bytes setters will receive result of any of the getters.
|
||||
*/
|
||||
it('testBytesFieldsSettersInterop', function() {
|
||||
var msg = new proto.jspb.test.TestAllTypes();
|
||||
msg.setOptionalBytes(BYTES);
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
msg.setOptionalBytes(msg.getOptionalBytes());
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
msg.setOptionalBytes(msg.getOptionalBytes_asB64());
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
msg.setOptionalBytes(msg.getOptionalBytes_asU8());
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that bytes setters will receive result of any of the getters.
|
||||
*/
|
||||
it('testRepeatedBytesGetters', function() {
|
||||
var msg = new proto.jspb.test.TestAllTypes();
|
||||
|
||||
function assertGetters() {
|
||||
assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[0]));
|
||||
assertTrue(goog.isString(msg.getRepeatedBytesList_asB64()[1]));
|
||||
assertTrue(msg.getRepeatedBytesList_asU8()[0] instanceof Uint8Array);
|
||||
assertTrue(msg.getRepeatedBytesList_asU8()[1] instanceof Uint8Array);
|
||||
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList()[0], BYTES));
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList()[1], BYTES));
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[0], BYTES));
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList_asB64()[1], BYTES));
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[0], BYTES));
|
||||
assertTrue(bytesCompare(msg.getRepeatedBytesList_asU8()[1], BYTES));
|
||||
}
|
||||
|
||||
msg.setRepeatedBytesList([BYTES, BYTES]);
|
||||
assertGetters();
|
||||
|
||||
msg.setRepeatedBytesList([BYTES_B64, BYTES_B64]);
|
||||
assertGetters();
|
||||
|
||||
msg.setRepeatedBytesList(null);
|
||||
assertEquals(0, msg.getRepeatedBytesList().length);
|
||||
assertEquals(0, msg.getRepeatedBytesList_asB64().length);
|
||||
assertEquals(0, msg.getRepeatedBytesList_asU8().length);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper: fill all extension values.
|
||||
@ -397,8 +535,7 @@ describe('protoBinaryTest', function() {
|
||||
proto.jspb.test.extendOptionalBool, true);
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendOptionalString, 'hello world');
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendOptionalBytes, 'bytes');
|
||||
msg.setExtension(proto.jspb.test.extendOptionalBytes, BYTES);
|
||||
var submsg = new proto.jspb.test.ExtendsWithMessage();
|
||||
submsg.setFoo(16);
|
||||
msg.setExtension(
|
||||
@ -407,6 +544,7 @@ describe('protoBinaryTest', function() {
|
||||
proto.jspb.test.extendOptionalForeignEnum,
|
||||
proto.jspb.test.ForeignEnum.FOREIGN_FOO);
|
||||
|
||||
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendRepeatedInt32List, [-42]);
|
||||
msg.setExtension(
|
||||
@ -435,8 +573,7 @@ describe('protoBinaryTest', function() {
|
||||
proto.jspb.test.extendRepeatedBoolList, [true]);
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendRepeatedStringList, ['hello world']);
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendRepeatedBytesList, ['bytes']);
|
||||
msg.setExtension(proto.jspb.test.extendRepeatedBytesList, [BYTES]);
|
||||
submsg = new proto.jspb.test.ExtendsWithMessage();
|
||||
submsg.setFoo(1000);
|
||||
msg.setExtension(
|
||||
@ -444,6 +581,7 @@ describe('protoBinaryTest', function() {
|
||||
msg.setExtension(proto.jspb.test.extendRepeatedForeignEnumList,
|
||||
[proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
|
||||
|
||||
|
||||
msg.setExtension(
|
||||
proto.jspb.test.extendPackedRepeatedInt32List, [-42]);
|
||||
msg.setExtension(
|
||||
@ -473,6 +611,7 @@ describe('protoBinaryTest', function() {
|
||||
proto.jspb.test.extendPackedRepeatedBoolList, [true]);
|
||||
msg.setExtension(proto.jspb.test.extendPackedRepeatedForeignEnumList,
|
||||
[proto.jspb.test.ForeignEnum.FOREIGN_FOO]);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -180,7 +180,7 @@ jspb.BinaryReader.prototype.getCursor = function() {
|
||||
|
||||
/**
|
||||
* Returns the raw buffer.
|
||||
* @return {Uint8Array} The raw buffer.
|
||||
* @return {?Uint8Array} The raw buffer.
|
||||
*/
|
||||
jspb.BinaryReader.prototype.getBuffer = function() {
|
||||
return this.decoder_.getBuffer();
|
||||
@ -592,8 +592,8 @@ jspb.BinaryReader.prototype.getFieldDecoder = function() {
|
||||
var start = this.decoder_.getCursor();
|
||||
var end = start + length;
|
||||
|
||||
var innerDecoder = jspb.BinaryDecoder.alloc(this.decoder_.getBuffer(),
|
||||
start, length);
|
||||
var innerDecoder =
|
||||
jspb.BinaryDecoder.alloc(this.decoder_.getBuffer(), start, length);
|
||||
this.decoder_.setCursor(end);
|
||||
return innerDecoder;
|
||||
};
|
||||
@ -869,7 +869,7 @@ jspb.BinaryReader.prototype.readString = function() {
|
||||
* Reads a length-prefixed block of bytes from the binary stream, or returns
|
||||
* null if the next field in the stream has an invalid length value.
|
||||
*
|
||||
* @return {Uint8Array} The block of bytes.
|
||||
* @return {!Uint8Array} The block of bytes.
|
||||
*/
|
||||
jspb.BinaryReader.prototype.readBytes = function() {
|
||||
goog.asserts.assert(
|
||||
|
@ -366,28 +366,28 @@ describe('binaryReaderTest', function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
|
||||
var testSignedData = [
|
||||
'2730538252207801776',
|
||||
'-2688470994844604560',
|
||||
'3398529779486536359',
|
||||
'3568577411627971000',
|
||||
'272477188847484900',
|
||||
'-6649058714086158188',
|
||||
'-7695254765712060806',
|
||||
'-4525541438037104029',
|
||||
'-4993706538836508568',
|
||||
'4990160321893729138'
|
||||
'2730538252207801776',
|
||||
'-2688470994844604560',
|
||||
'3398529779486536359',
|
||||
'3568577411627971000',
|
||||
'272477188847484900',
|
||||
'-6649058714086158188',
|
||||
'-7695254765712060806',
|
||||
'-4525541438037104029',
|
||||
'-4993706538836508568',
|
||||
'4990160321893729138'
|
||||
];
|
||||
var testUnsignedData = [
|
||||
'7822732630241694882',
|
||||
'6753602971916687352',
|
||||
'2399935075244442116',
|
||||
'8724292567325338867',
|
||||
'16948784802625696584',
|
||||
'4136275908516066934',
|
||||
'3575388346793700364',
|
||||
'5167142028379259461',
|
||||
'1557573948689737699',
|
||||
'17100725280812548567'
|
||||
'7822732630241694882',
|
||||
'6753602971916687352',
|
||||
'2399935075244442116',
|
||||
'8724292567325338867',
|
||||
'16948784802625696584',
|
||||
'4136275908516066934',
|
||||
'3575388346793700364',
|
||||
'5167142028379259461',
|
||||
'1557573948689737699',
|
||||
'17100725280812548567'
|
||||
];
|
||||
|
||||
for (var i = 0; i < testSignedData.length; i++) {
|
||||
@ -535,7 +535,7 @@ describe('binaryReaderTest', function() {
|
||||
*/
|
||||
it('testNesting', function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
|
||||
var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
|
||||
|
||||
writer.writeInt32(1, 100);
|
||||
|
||||
@ -626,31 +626,15 @@ describe('binaryReaderTest', function() {
|
||||
writer.writeBytes(4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
|
||||
writer.writeString(4, 'The quick brown fox jumps over the lazy dog');
|
||||
|
||||
// Write a group with a nested group inside. We use the internal
|
||||
// .rawWriteVarint() to ensure the tested wire data is what we want,
|
||||
// independently of any serialization logic.
|
||||
// Write a group with a nested group inside.
|
||||
writer.writeInt32(5, sentinel);
|
||||
// Start group, field 5.
|
||||
writer.rawWriteVarint(
|
||||
(5 << 3) + jspb.BinaryConstants.WireType.START_GROUP);
|
||||
// Varint, field 42.
|
||||
writer.rawWriteVarint(
|
||||
(42 << 3) + jspb.BinaryConstants.WireType.VARINT);
|
||||
// Varint data.
|
||||
writer.rawWriteVarint(42);
|
||||
// Start group, field 6.
|
||||
writer.rawWriteVarint(
|
||||
(6 << 3) + jspb.BinaryConstants.WireType.START_GROUP);
|
||||
// Varint, field 84.
|
||||
writer.rawWriteVarint(
|
||||
(84 << 3) + jspb.BinaryConstants.WireType.VARINT);
|
||||
writer.rawWriteVarint(42);
|
||||
// End group, field 6.
|
||||
writer.rawWriteVarint(
|
||||
(6 << 3) + jspb.BinaryConstants.WireType.END_GROUP);
|
||||
// End group, field 5.
|
||||
writer.rawWriteVarint(
|
||||
(5 << 3) + jspb.BinaryConstants.WireType.END_GROUP);
|
||||
var dummyMessage = /** @type {!jspb.BinaryMessage} */({});
|
||||
writer.writeGroup(5, dummyMessage, function() {
|
||||
writer.writeInt64(42, 42);
|
||||
writer.writeGroup(6, dummyMessage, function() {
|
||||
writer.writeInt64(84, 42);
|
||||
});
|
||||
});
|
||||
|
||||
// Write final sentinel.
|
||||
writer.writeInt32(6, sentinel);
|
||||
|
@ -838,63 +838,17 @@ jspb.utils.countDelimitedFields = function(buffer, start, end, field) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones a scalar field. Pulling this out to a helper method saves us a few
|
||||
* bytes of generated code.
|
||||
* @param {Array} array
|
||||
* @return {Array}
|
||||
*/
|
||||
jspb.utils.cloneRepeatedScalarField = function(array) {
|
||||
return array ? array.slice() : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones an array of messages using the provided cloner function.
|
||||
* @param {Array.<jspb.BinaryMessage>} messages
|
||||
* @param {jspb.ClonerFunction} cloner
|
||||
* @return {Array.<jspb.BinaryMessage>}
|
||||
*/
|
||||
jspb.utils.cloneRepeatedMessageField = function(messages, cloner) {
|
||||
if (messages === null) return null;
|
||||
var result = [];
|
||||
for (var i = 0; i < messages.length; i++) {
|
||||
result.push(cloner(messages[i]));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clones an array of byte blobs.
|
||||
* @param {Array.<Uint8Array>} blobs
|
||||
* @return {Array.<Uint8Array>}
|
||||
*/
|
||||
jspb.utils.cloneRepeatedBlobField = function(blobs) {
|
||||
if (blobs === null) return null;
|
||||
var result = [];
|
||||
for (var i = 0; i < blobs.length; i++) {
|
||||
result.push(new Uint8Array(blobs[i]));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* String-ify bytes for text format. Should be optimized away in non-debug.
|
||||
* The returned string uses \xXX escapes for all values and is itself quoted.
|
||||
* [1, 31] serializes to '"\x01\x1f"'.
|
||||
* @param {jspb.ByteSource} byteSource The bytes to serialize.
|
||||
* @param {boolean=} opt_stringIsRawBytes The string is interpreted as a series
|
||||
* of raw bytes rather than base64 data.
|
||||
* @return {string} Stringified bytes for text format.
|
||||
*/
|
||||
jspb.utils.debugBytesToTextFormat = function(byteSource,
|
||||
opt_stringIsRawBytes) {
|
||||
jspb.utils.debugBytesToTextFormat = function(byteSource) {
|
||||
var s = '"';
|
||||
if (byteSource) {
|
||||
var bytes =
|
||||
jspb.utils.byteSourceToUint8Array(byteSource, opt_stringIsRawBytes);
|
||||
var bytes = jspb.utils.byteSourceToUint8Array(byteSource);
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
s += '\\x';
|
||||
if (bytes[i] < 16) s += '0';
|
||||
@ -925,9 +879,8 @@ jspb.utils.debugScalarToTextFormat = function(scalar) {
|
||||
* exception.
|
||||
* @param {string} str
|
||||
* @return {!Uint8Array}
|
||||
* @private
|
||||
*/
|
||||
jspb.utils.stringToByteArray_ = function(str) {
|
||||
jspb.utils.stringToByteArray = function(str) {
|
||||
var arr = new Uint8Array(str.length);
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var codepoint = str.charCodeAt(i);
|
||||
@ -944,13 +897,10 @@ jspb.utils.stringToByteArray_ = function(str) {
|
||||
/**
|
||||
* Converts any type defined in jspb.ByteSource into a Uint8Array.
|
||||
* @param {!jspb.ByteSource} data
|
||||
* @param {boolean=} opt_stringIsRawBytes Interpret a string as a series of raw
|
||||
* bytes (encoded as codepoints 0--255 inclusive) rather than base64 data
|
||||
* (default behavior).
|
||||
* @return {!Uint8Array}
|
||||
* @suppress {invalidCasts}
|
||||
*/
|
||||
jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) {
|
||||
jspb.utils.byteSourceToUint8Array = function(data) {
|
||||
if (data.constructor === Uint8Array) {
|
||||
return /** @type {!Uint8Array} */(data);
|
||||
}
|
||||
@ -967,11 +917,7 @@ jspb.utils.byteSourceToUint8Array = function(data, opt_stringIsRawBytes) {
|
||||
|
||||
if (data.constructor === String) {
|
||||
data = /** @type {string} */(data);
|
||||
if (opt_stringIsRawBytes) {
|
||||
return jspb.utils.stringToByteArray_(data);
|
||||
} else {
|
||||
return goog.crypt.base64.decodeStringToUint8Array(data);
|
||||
}
|
||||
return goog.crypt.base64.decodeStringToUint8Array(data);
|
||||
}
|
||||
|
||||
goog.asserts.fail('Type not convertible to Uint8Array.');
|
||||
|
@ -310,7 +310,7 @@ describe('binaryUtilsTest', function() {
|
||||
// NaN.
|
||||
jspb.utils.splitFloat32(NaN);
|
||||
if (!isNaN(jspb.utils.joinFloat32(jspb.utils.split64Low,
|
||||
jspb.utils.split64High))) {
|
||||
jspb.utils.split64High))) {
|
||||
throw 'fail!';
|
||||
}
|
||||
|
||||
@ -324,7 +324,7 @@ describe('binaryUtilsTest', function() {
|
||||
if (opt_bits != jspb.utils.split64Low) throw 'fail!';
|
||||
}
|
||||
if (truncate(x) != jspb.utils.joinFloat32(jspb.utils.split64Low,
|
||||
jspb.utils.split64High)) {
|
||||
jspb.utils.split64High)) {
|
||||
throw 'fail!';
|
||||
}
|
||||
}
|
||||
@ -376,7 +376,7 @@ describe('binaryUtilsTest', function() {
|
||||
// NaN.
|
||||
jspb.utils.splitFloat64(NaN);
|
||||
if (!isNaN(jspb.utils.joinFloat64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High))) {
|
||||
jspb.utils.split64High))) {
|
||||
throw 'fail!';
|
||||
}
|
||||
|
||||
@ -394,7 +394,7 @@ describe('binaryUtilsTest', function() {
|
||||
if (opt_lowBits != jspb.utils.split64Low) throw 'fail!';
|
||||
}
|
||||
if (x != jspb.utils.joinFloat64(jspb.utils.split64Low,
|
||||
jspb.utils.split64High)) {
|
||||
jspb.utils.split64High)) {
|
||||
throw 'fail!';
|
||||
}
|
||||
}
|
||||
@ -439,16 +439,20 @@ describe('binaryUtilsTest', function() {
|
||||
* Tests counting packed varints.
|
||||
*/
|
||||
it('testCountVarints', function() {
|
||||
var writer = new jspb.BinaryWriter();
|
||||
|
||||
var count = 0;
|
||||
var values = [];
|
||||
for (var i = 1; i < 1000000000; i *= 1.1) {
|
||||
writer.rawWriteVarint(Math.floor(i));
|
||||
count++;
|
||||
values.push(Math.floor(i));
|
||||
}
|
||||
|
||||
var writer = new jspb.BinaryWriter();
|
||||
writer.writePackedUint64(1, values);
|
||||
|
||||
var buffer = new Uint8Array(writer.getResultBuffer());
|
||||
assertEquals(count, jspb.utils.countVarints(buffer, 0, buffer.length));
|
||||
|
||||
// We should have two more varints than we started with - one for the field
|
||||
// tag, one for the packed length.
|
||||
assertEquals(values.length + 2,
|
||||
jspb.utils.countVarints(buffer, 0, buffer.length));
|
||||
});
|
||||
|
||||
|
||||
@ -625,8 +629,5 @@ describe('binaryUtilsTest', function() {
|
||||
|
||||
// Converting base64-encoded strings into Uint8Arrays should work.
|
||||
check(convert(sourceBase64));
|
||||
|
||||
// Converting binary-data strings into Uint8Arrays should work.
|
||||
check(convert(sourceString, /* opt_stringIsRawBytes = */ true));
|
||||
});
|
||||
});
|
||||
|
1205
js/binary/writer.js
1205
js/binary/writer.js
File diff suppressed because it is too large
Load Diff
@ -68,7 +68,7 @@ jspb.debug.dump = function(message) {
|
||||
* Recursively introspects a message and the values its getters return to
|
||||
* make a best effort in creating a human readable representation of the
|
||||
* message.
|
||||
* @param {*} thing A jspb.Message, Array or primitive type to dump.
|
||||
* @param {?} thing A jspb.Message, Array or primitive type to dump.
|
||||
* @return {*}
|
||||
* @private
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@ goog.require('proto.jspb.test.IsExtension');
|
||||
goog.require('proto.jspb.test.Simple1');
|
||||
|
||||
|
||||
|
||||
describe('debugTest', function() {
|
||||
it('testSimple1', function() {
|
||||
if (COMPILED) {
|
||||
|
284
js/message.js
284
js/message.js
@ -39,8 +39,8 @@ goog.provide('jspb.Message');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.crypt.base64');
|
||||
goog.require('goog.json');
|
||||
goog.require('goog.object');
|
||||
|
||||
// Not needed in compilation units that have no protos with xids.
|
||||
goog.forwardDeclare('xid.String');
|
||||
@ -119,6 +119,14 @@ jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Does this field represent a sub Message?
|
||||
*/
|
||||
jspb.ExtensionFieldInfo.prototype.isMessageType = function() {
|
||||
return !!this.ctor;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base class for all JsPb messages.
|
||||
* @constructor
|
||||
@ -165,6 +173,14 @@ goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED);
|
||||
// TODO(b/19419436) Turn this on by default.
|
||||
|
||||
|
||||
/**
|
||||
* Does this browser support Uint8Aray typed arrays?
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function');
|
||||
|
||||
|
||||
/**
|
||||
* The internal data array.
|
||||
* @type {!Array}
|
||||
@ -210,6 +226,14 @@ jspb.Message.prototype.pivot_;
|
||||
jspb.Message.prototype.messageId_;
|
||||
|
||||
|
||||
/**
|
||||
* Repeated float or double fields which have been converted to include only
|
||||
* numbers and not strings holding "NaN", "Infinity" and "-Infinity".
|
||||
* @private {!Object<number,boolean>|undefined}
|
||||
*/
|
||||
jspb.Message.prototype.convertedFloatingPointFields_;
|
||||
|
||||
|
||||
/**
|
||||
* The xid of this proto type (The same for all instances of a proto). Provides
|
||||
* a way to identify a proto by stable obfuscated name.
|
||||
@ -284,6 +308,8 @@ jspb.Message.initialize = function(
|
||||
msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
|
||||
msg.array = data;
|
||||
jspb.Message.materializeExtensionObject_(msg, suggestedPivot);
|
||||
msg.convertedFloatingPointFields_ = {};
|
||||
|
||||
if (repeatedFields) {
|
||||
for (var i = 0; i < repeatedFields.length; i++) {
|
||||
var fieldNumber = repeatedFields[i];
|
||||
@ -419,8 +445,9 @@ jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) {
|
||||
* @param {!jspb.Message} proto The proto whose extensions to convert.
|
||||
* @param {!Object} obj The Soy object to add converted extension data to.
|
||||
* @param {!Object} extensions The proto class' registered extensions.
|
||||
* @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
|
||||
* class' getExtension function. Passed for effective dead code removal.
|
||||
* @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn
|
||||
* The proto class' getExtension function. Passed for effective dead code
|
||||
* removal.
|
||||
* @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
||||
* for transitional soy proto support: http://goto/soy-param-migration
|
||||
*/
|
||||
@ -472,7 +499,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
|
||||
}
|
||||
var value = getExtensionFn.call(proto, fieldInfo);
|
||||
if (value) {
|
||||
if (fieldInfo.ctor) { // is this a message type?
|
||||
if (fieldInfo.isMessageType()) {
|
||||
// If the message type of the extension was generated without binary
|
||||
// support, there may not be a binary message serializer function, and
|
||||
// we can't know when we codegen the extending message that the extended
|
||||
@ -517,8 +544,7 @@ jspb.Message.readBinaryExtension = function(msg, reader, extensions,
|
||||
}
|
||||
|
||||
var value;
|
||||
if (fieldInfo.ctor) {
|
||||
// Message type.
|
||||
if (fieldInfo.isMessageType()) {
|
||||
value = new fieldInfo.ctor();
|
||||
fieldInfo.binaryReaderFn.call(
|
||||
reader, value, fieldInfo.binaryMessageDeserializeFn);
|
||||
@ -566,6 +592,130 @@ jspb.Message.getField = function(msg, fieldNumber) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of an optional float or double field.
|
||||
* @param {!jspb.Message} msg A jspb proto.
|
||||
* @param {number} fieldNumber The field number.
|
||||
* @return {?number|undefined} The field's value.
|
||||
* @protected
|
||||
*/
|
||||
jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) {
|
||||
var value = jspb.Message.getField(msg, fieldNumber);
|
||||
// Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers.
|
||||
return value == null ? value : +value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of a repeated float or double field.
|
||||
* @param {!jspb.Message} msg A jspb proto.
|
||||
* @param {number} fieldNumber The field number.
|
||||
* @return {!Array<number>} The field's value.
|
||||
* @protected
|
||||
*/
|
||||
jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
|
||||
var values = jspb.Message.getField(msg, fieldNumber);
|
||||
if (!msg.convertedFloatingPointFields_) {
|
||||
msg.convertedFloatingPointFields_ = {};
|
||||
}
|
||||
if (!msg.convertedFloatingPointFields_[fieldNumber]) {
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
// Converts "NaN", "Infinity" and "-Infinity" to their corresponding
|
||||
// numbers.
|
||||
values[i] = +values[i];
|
||||
}
|
||||
msg.convertedFloatingPointFields_[fieldNumber] = true;
|
||||
}
|
||||
return /** @type {!Array<number>} */ (values);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Coerce a 'bytes' field to a base 64 string.
|
||||
* @param {string|Uint8Array|null} value
|
||||
* @return {?string} The field's coerced value.
|
||||
*/
|
||||
jspb.Message.bytesAsB64 = function(value) {
|
||||
if (value == null || goog.isString(value)) {
|
||||
return value;
|
||||
}
|
||||
if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) {
|
||||
return goog.crypt.base64.encodeByteArray(value);
|
||||
}
|
||||
goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value));
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Coerce a 'bytes' field to a Uint8Array byte buffer.
|
||||
* Note that Uint8Array is not supported on IE versions before 10 nor on Opera
|
||||
* Mini. @see http://caniuse.com/Uint8Array
|
||||
* @param {string|Uint8Array|null} value
|
||||
* @return {?Uint8Array} The field's coerced value.
|
||||
*/
|
||||
jspb.Message.bytesAsU8 = function(value) {
|
||||
if (value == null || value instanceof Uint8Array) {
|
||||
return value;
|
||||
}
|
||||
if (goog.isString(value)) {
|
||||
return goog.crypt.base64.decodeStringToUint8Array(value);
|
||||
}
|
||||
goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value));
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Coerce a repeated 'bytes' field to an array of base 64 strings.
|
||||
* Note: the returned array should be treated as immutable.
|
||||
* @param {!Array<string>|!Array<!Uint8Array>} value
|
||||
* @return {!Array<string?>} The field's coerced value.
|
||||
*/
|
||||
jspb.Message.bytesListAsB64 = function(value) {
|
||||
jspb.Message.assertConsistentTypes_(value);
|
||||
if (!value.length || goog.isString(value[0])) {
|
||||
return /** @type {!Array<string>} */ (value);
|
||||
}
|
||||
return goog.array.map(value, jspb.Message.bytesAsB64);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers.
|
||||
* Note: the returned array should be treated as immutable.
|
||||
* Note that Uint8Array is not supported on IE versions before 10 nor on Opera
|
||||
* Mini. @see http://caniuse.com/Uint8Array
|
||||
* @param {!Array<string>|!Array<!Uint8Array>} value
|
||||
* @return {!Array<Uint8Array?>} The field's coerced value.
|
||||
*/
|
||||
jspb.Message.bytesListAsU8 = function(value) {
|
||||
jspb.Message.assertConsistentTypes_(value);
|
||||
if (!value.length || value[0] instanceof Uint8Array) {
|
||||
return /** @type {!Array<!Uint8Array>} */ (value);
|
||||
}
|
||||
return goog.array.map(value, jspb.Message.bytesAsU8);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that all elements of an array are of the same type.
|
||||
* @param {Array?} array The array to test.
|
||||
* @private
|
||||
*/
|
||||
jspb.Message.assertConsistentTypes_ = function(array) {
|
||||
if (goog.DEBUG && array && array.length > 1) {
|
||||
var expected = goog.typeOf(array[0]);
|
||||
goog.array.forEach(array, function(e) {
|
||||
if (goog.typeOf(e) != expected) {
|
||||
goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' +
|
||||
'Got ' + goog.typeOf(e) + ' expected ' + expected);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the value of a non-extension primitive field, with proto3 (non-nullable
|
||||
* primitives) semantics. Returns `defaultValue` if the field is not otherwise
|
||||
@ -841,7 +991,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
||||
}
|
||||
var fieldNumber = fieldInfo.fieldIndex;
|
||||
if (fieldInfo.isRepeated) {
|
||||
if (fieldInfo.ctor) {
|
||||
if (fieldInfo.isMessageType()) {
|
||||
if (!this.wrappers_[fieldNumber]) {
|
||||
this.wrappers_[fieldNumber] =
|
||||
goog.array.map(this.extensionObject_[fieldNumber] || [],
|
||||
@ -854,7 +1004,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
||||
return this.extensionObject_[fieldNumber];
|
||||
}
|
||||
} else {
|
||||
if (fieldInfo.ctor) {
|
||||
if (fieldInfo.isMessageType()) {
|
||||
if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) {
|
||||
this.wrappers_[fieldNumber] = new fieldInfo.ctor(
|
||||
/** @type {Array|undefined} */ (
|
||||
@ -871,7 +1021,8 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
||||
/**
|
||||
* Sets the value of the extension field in the extended object.
|
||||
* @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set.
|
||||
* @param {jspb.Message|string|number|boolean|Array} value The value to set.
|
||||
* @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value
|
||||
* to set.
|
||||
*/
|
||||
jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
||||
if (!this.wrappers_) {
|
||||
@ -881,7 +1032,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
||||
var fieldNumber = fieldInfo.fieldIndex;
|
||||
if (fieldInfo.isRepeated) {
|
||||
value = value || [];
|
||||
if (fieldInfo.ctor) {
|
||||
if (fieldInfo.isMessageType()) {
|
||||
this.wrappers_[fieldNumber] = value;
|
||||
this.extensionObject_[fieldNumber] = goog.array.map(
|
||||
/** @type {Array<jspb.Message>} */ (value), function(msg) {
|
||||
@ -891,7 +1042,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
||||
this.extensionObject_[fieldNumber] = value;
|
||||
}
|
||||
} else {
|
||||
if (fieldInfo.ctor) {
|
||||
if (fieldInfo.isMessageType()) {
|
||||
this.wrappers_[fieldNumber] = value;
|
||||
this.extensionObject_[fieldNumber] = value ? value.toArray() : value;
|
||||
} else {
|
||||
@ -957,6 +1108,33 @@ jspb.Message.equals = function(m1, m2) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compares two message extension fields recursively.
|
||||
* @param {!Object} extension1 The first field.
|
||||
* @param {!Object} extension2 The second field.
|
||||
* @return {boolean} true if the extensions are null/undefined, or otherwise
|
||||
* equal.
|
||||
*/
|
||||
jspb.Message.compareExtensions = function(extension1, extension2) {
|
||||
extension1 = extension1 || {};
|
||||
extension2 = extension2 || {};
|
||||
|
||||
var keys = {};
|
||||
for (var name in extension1) {
|
||||
keys[name] = 0;
|
||||
}
|
||||
for (var name in extension2) {
|
||||
keys[name] = 0;
|
||||
}
|
||||
for (name in keys) {
|
||||
if (!jspb.Message.compareFields(extension1[name], extension2[name])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compares two message fields recursively.
|
||||
* @param {*} field1 The first field.
|
||||
@ -964,43 +1142,77 @@ jspb.Message.equals = function(m1, m2) {
|
||||
* @return {boolean} true if the fields are null/undefined, or otherwise equal.
|
||||
*/
|
||||
jspb.Message.compareFields = function(field1, field2) {
|
||||
if (goog.isObject(field1) && goog.isObject(field2)) {
|
||||
var keys = {}, name, extensionObject1, extensionObject2;
|
||||
for (name in field1) {
|
||||
field1.hasOwnProperty(name) && (keys[name] = 0);
|
||||
// If the fields are trivially equal, they're equal.
|
||||
if (field1 == field2) return true;
|
||||
|
||||
// If the fields aren't trivially equal and one of them isn't an object,
|
||||
// they can't possibly be equal.
|
||||
if (!goog.isObject(field1) || !goog.isObject(field2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We have two objects. If they're different types, they're not equal.
|
||||
field1 = /** @type {!Object} */(field1);
|
||||
field2 = /** @type {!Object} */(field2);
|
||||
if (field1.constructor != field2.constructor) return false;
|
||||
|
||||
// If both are Uint8Arrays, compare them element-by-element.
|
||||
if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) {
|
||||
var bytes1 = /** @type {!Uint8Array} */(field1);
|
||||
var bytes2 = /** @type {!Uint8Array} */(field2);
|
||||
if (bytes1.length != bytes2.length) return false;
|
||||
for (var i = 0; i < bytes1.length; i++) {
|
||||
if (bytes1[i] != bytes2[i]) return false;
|
||||
}
|
||||
for (name in field2) {
|
||||
field2.hasOwnProperty(name) && (keys[name] = 0);
|
||||
}
|
||||
for (name in keys) {
|
||||
var val1 = field1[name], val2 = field2[name];
|
||||
if (goog.isObject(val1) && !goog.isArray(val1)) {
|
||||
if (extensionObject1 !== undefined) {
|
||||
throw new Error('invalid jspb state');
|
||||
}
|
||||
extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// If they're both Arrays, compare them element by element except for the
|
||||
// optional extension objects at the end, which we compare separately.
|
||||
if (field1.constructor === Array) {
|
||||
var extension1 = undefined;
|
||||
var extension2 = undefined;
|
||||
|
||||
var length = Math.max(field1.length, field2.length);
|
||||
for (var i = 0; i < length; i++) {
|
||||
var val1 = field1[i];
|
||||
var val2 = field2[i];
|
||||
|
||||
if (val1 && (val1.constructor == Object)) {
|
||||
goog.asserts.assert(extension1 === undefined);
|
||||
goog.asserts.assert(i === field1.length - 1);
|
||||
extension1 = val1;
|
||||
val1 = undefined;
|
||||
}
|
||||
if (goog.isObject(val2) && !goog.isArray(val2)) {
|
||||
if (extensionObject2 !== undefined) {
|
||||
throw new Error('invalid jspb state');
|
||||
}
|
||||
extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2;
|
||||
|
||||
if (val2 && (val2.constructor == Object)) {
|
||||
goog.asserts.assert(extension2 === undefined);
|
||||
goog.asserts.assert(i === field2.length - 1);
|
||||
extension2 = val2;
|
||||
val2 = undefined;
|
||||
}
|
||||
|
||||
if (!jspb.Message.compareFields(val1, val2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (extensionObject1 || extensionObject2) {
|
||||
return jspb.Message.compareFields(extensionObject1, extensionObject2);
|
||||
|
||||
if (extension1 || extension2) {
|
||||
extension1 = extension1 || {};
|
||||
extension2 = extension2 || {};
|
||||
return jspb.Message.compareExtensions(extension1, extension2);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
// Primitive fields, null and undefined compare as equal.
|
||||
// This also forces booleans and 0/1 to compare as equal to ensure
|
||||
// compatibility with the jspb serializer.
|
||||
return field1 == field2;
|
||||
|
||||
// If they're both plain Objects (i.e. extensions), compare them as
|
||||
// extensions.
|
||||
if (field1.constructor === Object) {
|
||||
return jspb.Message.compareExtensions(field1, field2);
|
||||
}
|
||||
|
||||
throw new Error('Invalid type in JSPB array');
|
||||
};
|
||||
|
||||
|
||||
|
@ -53,6 +53,8 @@ goog.require('proto.jspb.test.Complex');
|
||||
goog.require('proto.jspb.test.DefaultValues');
|
||||
goog.require('proto.jspb.test.Empty');
|
||||
goog.require('proto.jspb.test.EnumContainer');
|
||||
goog.require('proto.jspb.test.floatingMsgField');
|
||||
goog.require('proto.jspb.test.FloatingPointFields');
|
||||
goog.require('proto.jspb.test.floatingStrField');
|
||||
goog.require('proto.jspb.test.HasExtensions');
|
||||
goog.require('proto.jspb.test.IndirectExtension');
|
||||
@ -60,7 +62,6 @@ goog.require('proto.jspb.test.IsExtension');
|
||||
goog.require('proto.jspb.test.OptionalFields');
|
||||
goog.require('proto.jspb.test.OuterEnum');
|
||||
goog.require('proto.jspb.test.OuterMessage.Complex');
|
||||
goog.require('proto.jspb.test.simple1');
|
||||
goog.require('proto.jspb.test.Simple1');
|
||||
goog.require('proto.jspb.test.Simple2');
|
||||
goog.require('proto.jspb.test.SpecialCases');
|
||||
@ -74,7 +75,7 @@ goog.require('proto.jspb.test.TestReservedNamesExtension');
|
||||
// CommonJS-LoadFromFile: test2_pb proto.jspb.test
|
||||
goog.require('proto.jspb.test.ExtensionMessage');
|
||||
goog.require('proto.jspb.test.TestExtensionsMessage');
|
||||
goog.require('proto.jspb.test.floatingMsgField');
|
||||
|
||||
|
||||
|
||||
|
||||
@ -98,12 +99,6 @@ describe('Message test suite', function() {
|
||||
assertEquals('some_bytes', data.getBytesField());
|
||||
});
|
||||
|
||||
it('testNestedMessage', function() {
|
||||
var msg = new proto.jspb.test.OuterMessage.Complex();
|
||||
msg.setInnerComplexField(5);
|
||||
assertObjectEquals({innerComplexField: 5}, msg.toObject());
|
||||
});
|
||||
|
||||
it('testComplexConversion', function() {
|
||||
var data1 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
|
||||
var data2 = ['a',,, [, 11], [[, 22], [, 33]],, ['s1', 's2'],, 1];
|
||||
@ -160,6 +155,13 @@ describe('Message test suite', function() {
|
||||
|
||||
});
|
||||
|
||||
it('testNestedComplexMessage', function() {
|
||||
// Instantiate the message and set a unique field, just to ensure that we
|
||||
// are not getting jspb.test.Complex instead.
|
||||
var msg = new proto.jspb.test.OuterMessage.Complex();
|
||||
msg.setInnerComplexField(5);
|
||||
});
|
||||
|
||||
it('testSpecialCases', function() {
|
||||
// Note: Some property names are reserved in JavaScript.
|
||||
// These names are converted to the Js property named pb_<reserved_name>.
|
||||
@ -544,12 +546,6 @@ describe('Message test suite', function() {
|
||||
extendable.setExtension(proto.jspb.test.IndirectExtension.str, null);
|
||||
assertNull(extendable.getExtension(proto.jspb.test.IndirectExtension.str));
|
||||
|
||||
// These assertions will only work properly in uncompiled mode.
|
||||
// Extension fields defined on proto2 Descriptor messages are filtered out.
|
||||
|
||||
// TODO(haberman): codegen changes to properly ignore descriptor.proto
|
||||
// extensions need to be merged from google3.
|
||||
// assertUndefined(proto.jspb.test.IsExtension['simpleOption']);
|
||||
|
||||
// Extension fields with jspb.ignore = true are ignored.
|
||||
assertUndefined(proto.jspb.test.IndirectExtension['ignored']);
|
||||
@ -907,7 +903,7 @@ describe('Message test suite', function() {
|
||||
message.getDefaultOneofACase());
|
||||
|
||||
message =
|
||||
new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567,890));
|
||||
new proto.jspb.test.TestMessageWithOneof(new Array(9).concat(567, 890));
|
||||
assertEquals(1234, message.getAone());
|
||||
assertEquals(890, message.getAtwo());
|
||||
assertEquals(
|
||||
@ -936,7 +932,7 @@ describe('Message test suite', function() {
|
||||
message.getDefaultOneofBCase());
|
||||
|
||||
message = new proto.jspb.test.TestMessageWithOneof(
|
||||
new Array(11).concat(567,890));
|
||||
new Array(11).concat(567, 890));
|
||||
assertUndefined(message.getBone());
|
||||
assertEquals(890, message.getBtwo());
|
||||
assertEquals(
|
||||
@ -989,4 +985,26 @@ describe('Message test suite', function() {
|
||||
assertEquals('y', array[4]);
|
||||
});
|
||||
|
||||
it('testFloatingPointFieldsSupportNan', function() {
|
||||
var assertNan = function(x) {
|
||||
assertTrue('Expected ' + x + ' (' + goog.typeOf(x) + ') to be NaN.',
|
||||
goog.isNumber(x) && isNaN(x));
|
||||
};
|
||||
|
||||
var message = new proto.jspb.test.FloatingPointFields([
|
||||
'NaN', 'NaN', ['NaN', 'NaN'], 'NaN',
|
||||
'NaN', 'NaN', ['NaN', 'NaN'], 'NaN'
|
||||
]);
|
||||
assertNan(message.getOptionalFloatField());
|
||||
assertNan(message.getRequiredFloatField());
|
||||
assertNan(message.getRepeatedFloatFieldList()[0]);
|
||||
assertNan(message.getRepeatedFloatFieldList()[1]);
|
||||
assertNan(message.getDefaultFloatField());
|
||||
assertNan(message.getOptionalDoubleField());
|
||||
assertNan(message.getRequiredDoubleField());
|
||||
assertNan(message.getRepeatedDoubleFieldList()[0]);
|
||||
assertNan(message.getRepeatedDoubleFieldList()[1]);
|
||||
assertNan(message.getDefaultDoubleField());
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -28,6 +28,7 @@
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
goog.require('goog.crypt.base64');
|
||||
goog.require('goog.testing.asserts');
|
||||
|
||||
// CommonJS-LoadFromFile: testbinary_pb proto.jspb.test
|
||||
@ -37,31 +38,30 @@ goog.require('proto.jspb.test.ForeignMessage');
|
||||
goog.require('proto.jspb.test.Proto3Enum');
|
||||
goog.require('proto.jspb.test.TestProto3');
|
||||
|
||||
|
||||
var BYTES = new Uint8Array([1, 2, 8, 9]);
|
||||
var BYTES_B64 = goog.crypt.base64.encodeByteArray(BYTES);
|
||||
|
||||
|
||||
/**
|
||||
* Helper: compare a bytes field to a string with codepoints 0--255.
|
||||
* Helper: compare a bytes field to an expected value
|
||||
* @param {Uint8Array|string} arr
|
||||
* @param {string} str
|
||||
* @param {Uint8Array} expected
|
||||
* @return {boolean}
|
||||
*/
|
||||
function bytesCompare(arr, str) {
|
||||
if (arr.length != str.length) {
|
||||
function bytesCompare(arr, expected) {
|
||||
if (goog.isString(arr)) {
|
||||
arr = goog.crypt.base64.decodeStringToUint8Array(arr);
|
||||
}
|
||||
if (arr.length != expected.length) {
|
||||
return false;
|
||||
}
|
||||
if (typeof arr == 'string') {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr.charCodeAt(i) != str.charCodeAt(i)) {
|
||||
return false;
|
||||
}
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != expected[i]) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] != str.charCodeAt(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -86,13 +86,17 @@ describe('proto3Test', function() {
|
||||
assertEquals(msg.getOptionalDouble(), 0);
|
||||
assertEquals(msg.getOptionalString(), '');
|
||||
|
||||
// If/when we change bytes fields to return Uint8Array, we'll want to switch
|
||||
// to this assertion instead:
|
||||
//assertEquals(msg.getOptionalBytes() instanceof Uint8Array, true);
|
||||
// TODO(b/26173701): when we change bytes fields default getter to return
|
||||
// Uint8Array, we'll want to switch this assertion to match the u8 case.
|
||||
assertEquals(typeof msg.getOptionalBytes(), 'string');
|
||||
|
||||
assertEquals(msg.getOptionalBytes_asU8() instanceof Uint8Array, true);
|
||||
assertEquals(typeof msg.getOptionalBytes_asB64(), 'string');
|
||||
assertEquals(msg.getOptionalBytes().length, 0);
|
||||
assertEquals(msg.getOptionalForeignEnum(), proto.jspb.test.Proto3Enum.PROTO3_FOO);
|
||||
assertEquals(msg.getOptionalBytes_asU8().length, 0);
|
||||
assertEquals(msg.getOptionalBytes_asB64(), '');
|
||||
|
||||
assertEquals(msg.getOptionalForeignEnum(),
|
||||
proto.jspb.test.Proto3Enum.PROTO3_FOO);
|
||||
assertEquals(msg.getOptionalForeignMessage(), undefined);
|
||||
assertEquals(msg.getOptionalForeignMessage(), undefined);
|
||||
|
||||
@ -136,7 +140,7 @@ describe('proto3Test', function() {
|
||||
msg.setOptionalDouble(-1.5);
|
||||
msg.setOptionalBool(true);
|
||||
msg.setOptionalString('hello world');
|
||||
msg.setOptionalBytes('bytes');
|
||||
msg.setOptionalBytes(BYTES);
|
||||
var submsg = new proto.jspb.test.ForeignMessage();
|
||||
submsg.setC(16);
|
||||
msg.setOptionalForeignMessage(submsg);
|
||||
@ -156,7 +160,7 @@ describe('proto3Test', function() {
|
||||
msg.setRepeatedDoubleList([-1.5]);
|
||||
msg.setRepeatedBoolList([true]);
|
||||
msg.setRepeatedStringList(['hello world']);
|
||||
msg.setRepeatedBytesList(['bytes']);
|
||||
msg.setRepeatedBytesList([BYTES]);
|
||||
submsg = new proto.jspb.test.ForeignMessage();
|
||||
submsg.setC(1000);
|
||||
msg.setRepeatedForeignMessageList([submsg]);
|
||||
@ -181,7 +185,7 @@ describe('proto3Test', function() {
|
||||
assertEquals(msg.getOptionalDouble(), -1.5);
|
||||
assertEquals(msg.getOptionalBool(), true);
|
||||
assertEquals(msg.getOptionalString(), 'hello world');
|
||||
assertEquals(true, bytesCompare(msg.getOptionalBytes(), 'bytes'));
|
||||
assertEquals(true, bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
assertEquals(msg.getOptionalForeignMessage().getC(), 16);
|
||||
assertEquals(msg.getOptionalForeignEnum(),
|
||||
proto.jspb.test.Proto3Enum.PROTO3_BAR);
|
||||
@ -201,7 +205,7 @@ describe('proto3Test', function() {
|
||||
assertElementsEquals(msg.getRepeatedBoolList(), [true]);
|
||||
assertElementsEquals(msg.getRepeatedStringList(), ['hello world']);
|
||||
assertEquals(msg.getRepeatedBytesList().length, 1);
|
||||
assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], 'bytes'));
|
||||
assertEquals(true, bytesCompare(msg.getRepeatedBytesList()[0], BYTES));
|
||||
assertEquals(msg.getRepeatedForeignMessageList().length, 1);
|
||||
assertEquals(msg.getRepeatedForeignMessageList()[0].getC(), 1000);
|
||||
assertElementsEquals(msg.getRepeatedForeignEnumList(),
|
||||
@ -242,11 +246,12 @@ describe('proto3Test', function() {
|
||||
assertEquals(msg.getOneofString(), 'hello');
|
||||
assertEquals(msg.getOneofBytes(), undefined);
|
||||
|
||||
msg.setOneofBytes('\u00FF\u00FF');
|
||||
msg.setOneofBytes(goog.crypt.base64.encodeString('\u00FF\u00FF'));
|
||||
assertEquals(msg.getOneofUint32(), undefined);
|
||||
assertEquals(msg.getOneofForeignMessage(), undefined);
|
||||
assertEquals(msg.getOneofString(), undefined);
|
||||
assertEquals(msg.getOneofBytes(), '\u00FF\u00FF');
|
||||
assertEquals(msg.getOneofBytes_asB64(),
|
||||
goog.crypt.base64.encodeString('\u00FF\u00FF'));
|
||||
});
|
||||
|
||||
|
||||
@ -267,7 +272,7 @@ describe('proto3Test', function() {
|
||||
msg.setOptionalBool(false);
|
||||
msg.setOptionalString('hello world');
|
||||
msg.setOptionalString('');
|
||||
msg.setOptionalBytes('\u00FF\u00FF');
|
||||
msg.setOptionalBytes(goog.crypt.base64.encodeString('\u00FF\u00FF'));
|
||||
msg.setOptionalBytes('');
|
||||
msg.setOptionalForeignMessage(new proto.jspb.test.ForeignMessage());
|
||||
msg.setOptionalForeignMessage(null);
|
||||
@ -280,4 +285,30 @@ describe('proto3Test', function() {
|
||||
var serialized = msg.serializeBinary();
|
||||
assertEquals(0, serialized.length);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that base64 string and Uint8Array are interchangeable in bytes fields.
|
||||
*/
|
||||
it('testBytesFieldsInterop', function() {
|
||||
var msg = new proto.jspb.test.TestProto3();
|
||||
// Set as a base64 string and check all the getters work.
|
||||
msg.setOptionalBytes(BYTES_B64);
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
// Test binary serialize round trip doesn't break it.
|
||||
msg = proto.jspb.test.TestProto3.deserializeBinary(msg.serializeBinary());
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
msg = new proto.jspb.test.TestProto3();
|
||||
// Set as a Uint8Array and check all the getters work.
|
||||
msg.setOptionalBytes(BYTES);
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asU8(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes_asB64(), BYTES));
|
||||
assertTrue(bytesCompare(msg.getOptionalBytes(), BYTES));
|
||||
|
||||
});
|
||||
});
|
||||
|
@ -145,6 +145,17 @@ message DefaultValues {
|
||||
optional bytes bytes_field = 8 [default="moo"]; // Base64 encoding is "bW9v"
|
||||
}
|
||||
|
||||
message FloatingPointFields {
|
||||
optional float optional_float_field = 1;
|
||||
required float required_float_field = 2;
|
||||
repeated float repeated_float_field = 3;
|
||||
optional float default_float_field = 4 [default = 2.0];
|
||||
optional double optional_double_field = 5;
|
||||
required double required_double_field = 6;
|
||||
repeated double repeated_double_field = 7;
|
||||
optional double default_double_field = 8 [default = 2.0];
|
||||
}
|
||||
|
||||
message TestClone {
|
||||
optional string str = 1;
|
||||
optional Simple1 simple1 = 3;
|
||||
@ -216,3 +227,4 @@ message TestMessageWithOneof {
|
||||
int32 btwo = 13 [default = 1234];
|
||||
}
|
||||
}
|
||||
|
||||
|
10
php/ext/google/protobuf/config.m4
Normal file
10
php/ext/google/protobuf/config.m4
Normal file
@ -0,0 +1,10 @@
|
||||
dnl lines starting with "dnl" are comments
|
||||
|
||||
PHP_ARG_ENABLE(protobuf, whether to enable Protobuf extension, [ --enable-protobuf Enable Protobuf extension])
|
||||
|
||||
if test "$PHP_PROTOBUF" != "no"; then
|
||||
|
||||
dnl this defines the extension
|
||||
PHP_NEW_EXTENSION(protobuf, upb.c protobuf.c def.c message.c storage.c, $ext_shared)
|
||||
|
||||
fi
|
381
php/ext/google/protobuf/def.c
Normal file
381
php/ext/google/protobuf/def.c
Normal file
@ -0,0 +1,381 @@
|
||||
#include "protobuf.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Common Utilities
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void check_upb_status(const upb_status* status, const char* msg) {
|
||||
if (!upb_ok(status)) {
|
||||
zend_error("%s: %s\n", msg, upb_status_errmsg(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static upb_def *check_notfrozen(const upb_def *def) {
|
||||
if (upb_def_isfrozen(def)) {
|
||||
zend_error(E_ERROR,
|
||||
"Attempt to modify a frozen descriptor. Once descriptors are "
|
||||
"added to the descriptor pool, they may not be modified.");
|
||||
}
|
||||
return (upb_def *)def;
|
||||
}
|
||||
|
||||
static upb_msgdef *check_msgdef_notfrozen(const upb_msgdef *def) {
|
||||
return upb_downcast_msgdef_mutable(check_notfrozen((const upb_def *)def));
|
||||
}
|
||||
|
||||
static upb_fielddef *check_fielddef_notfrozen(const upb_fielddef *def) {
|
||||
return upb_downcast_fielddef_mutable(check_notfrozen((const upb_def *)def));
|
||||
}
|
||||
|
||||
#define PROTOBUF_WRAP_INTERN(wrapper, intern, intern_dtor) \
|
||||
Z_TYPE_P(wrapper) = IS_OBJECT; \
|
||||
Z_OBJVAL_P(wrapper) \
|
||||
.handle = zend_objects_store_put( \
|
||||
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
|
||||
intern_dtor, NULL TSRMLS_CC); \
|
||||
Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
|
||||
|
||||
#define PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \
|
||||
intern) \
|
||||
Z_TYPE_P(wrapper) = IS_OBJECT; \
|
||||
class_name *intern = ALLOC(class_name); \
|
||||
memset(intern, 0, sizeof(class_name)); \
|
||||
class_name_lower##_init_c_instance(intern TSRMLS_CC); \
|
||||
Z_OBJVAL_P(wrapper) \
|
||||
.handle = zend_objects_store_put(intern, NULL, class_name_lower##_free, \
|
||||
NULL TSRMLS_CC); \
|
||||
Z_OBJVAL_P(wrapper).handlers = zend_get_std_object_handlers();
|
||||
|
||||
#define PROTOBUF_CREATE_ZEND_WRAPPER(class_name, class_name_lower, wrapper, \
|
||||
intern) \
|
||||
MAKE_STD_ZVAL(wrapper); \
|
||||
PROTOBUF_SETUP_ZEND_WRAPPER(class_name, class_name_lower, wrapper, intern);
|
||||
|
||||
#define DEFINE_CLASS(name, name_lower, string_name) \
|
||||
zend_class_entry *name_lower##_type; \
|
||||
void name_lower##_init(TSRMLS_D) { \
|
||||
zend_class_entry class_type; \
|
||||
INIT_CLASS_ENTRY(class_type, string_name, name_lower##_methods); \
|
||||
name_lower##_type = zend_register_internal_class(&class_type TSRMLS_CC); \
|
||||
name_lower##_type->create_object = name_lower##_create; \
|
||||
} \
|
||||
name *php_to_##name_lower(zval *val TSRMLS_DC) { \
|
||||
return (name *)zend_object_store_get_object(val TSRMLS_CC); \
|
||||
} \
|
||||
void name_lower##_free(void *object TSRMLS_DC) { \
|
||||
name *intern = (name *)object; \
|
||||
name_lower##_free_c(intern TSRMLS_CC); \
|
||||
efree(object); \
|
||||
} \
|
||||
zend_object_value name_lower##_create(zend_class_entry *ce TSRMLS_DC) { \
|
||||
zend_object_value return_value; \
|
||||
name *intern = (name *)emalloc(sizeof(name)); \
|
||||
memset(intern, 0, sizeof(name)); \
|
||||
name_lower##_init_c_instance(intern TSRMLS_CC); \
|
||||
return_value.handle = zend_objects_store_put( \
|
||||
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
|
||||
name_lower##_free, NULL TSRMLS_CC); \
|
||||
return_value.handlers = zend_get_std_object_handlers(); \
|
||||
return return_value; \
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// DescriptorPool
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zend_function_entry descriptor_pool_methods[] = {
|
||||
PHP_ME(DescriptorPool, addMessage, NULL, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(DescriptorPool, finalize, NULL, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS(DescriptorPool, descriptor_pool,
|
||||
"Google\\Protobuf\\DescriptorPool");
|
||||
|
||||
DescriptorPool *generated_pool; // The actual generated pool
|
||||
|
||||
ZEND_FUNCTION(get_generated_pool) {
|
||||
if (PROTOBUF_G(generated_pool) == NULL) {
|
||||
MAKE_STD_ZVAL(PROTOBUF_G(generated_pool));
|
||||
Z_TYPE_P(PROTOBUF_G(generated_pool)) = IS_OBJECT;
|
||||
generated_pool = ALLOC(DescriptorPool);
|
||||
descriptor_pool_init_c_instance(generated_pool TSRMLS_CC);
|
||||
Z_OBJ_HANDLE_P(PROTOBUF_G(generated_pool)) = zend_objects_store_put(
|
||||
generated_pool, NULL,
|
||||
(zend_objects_free_object_storage_t)descriptor_pool_free, NULL TSRMLS_CC);
|
||||
Z_OBJ_HT_P(PROTOBUF_G(generated_pool)) = zend_get_std_object_handlers();
|
||||
}
|
||||
RETURN_ZVAL(PROTOBUF_G(generated_pool), 1, 0);
|
||||
}
|
||||
|
||||
void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC) {
|
||||
zend_object_std_init(&pool->std, descriptor_pool_type TSRMLS_CC);
|
||||
pool->symtab = upb_symtab_new(&pool->symtab);
|
||||
|
||||
ALLOC_HASHTABLE(pool->pending_list);
|
||||
zend_hash_init(pool->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
|
||||
}
|
||||
|
||||
void descriptor_pool_free_c(DescriptorPool *pool TSRMLS_DC) {
|
||||
upb_symtab_unref(pool->symtab, &pool->symtab);
|
||||
zend_hash_destroy(pool->pending_list);
|
||||
FREE_HASHTABLE(pool->pending_list);
|
||||
}
|
||||
|
||||
PHP_METHOD(DescriptorPool, addMessage) {
|
||||
char *name = NULL;
|
||||
int str_len;
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &str_len) ==
|
||||
FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
zval* retval = NULL;
|
||||
PROTOBUF_CREATE_ZEND_WRAPPER(MessageBuilderContext, message_builder_context,
|
||||
retval, context);
|
||||
|
||||
MAKE_STD_ZVAL(context->pool);
|
||||
ZVAL_ZVAL(context->pool, getThis(), 1, 0);
|
||||
|
||||
Descriptor *desc = php_to_descriptor(context->descriptor TSRMLS_CC);
|
||||
Descriptor_name_set(desc, name);
|
||||
|
||||
RETURN_ZVAL(retval, 0, 1);
|
||||
}
|
||||
|
||||
static void validate_msgdef(const upb_msgdef* msgdef) {
|
||||
// Verify that no required fields exist. proto3 does not support these.
|
||||
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) {
|
||||
zend_error(E_ERROR, "Required fields are unsupported in proto3.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PHP_METHOD(DescriptorPool, finalize) {
|
||||
DescriptorPool *self = php_to_descriptor_pool(getThis() TSRMLS_CC);
|
||||
Bucket *temp;
|
||||
int i, num;
|
||||
|
||||
num = zend_hash_num_elements(self->pending_list);
|
||||
upb_def **defs = emalloc(sizeof(upb_def *) * num);
|
||||
|
||||
for (i = 0, temp = self->pending_list->pListHead; temp != NULL;
|
||||
temp = temp->pListNext) {
|
||||
zval *def_php = *(zval **)temp->pData;
|
||||
Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
|
||||
defs[i] = (upb_def *)desc->msgdef;
|
||||
validate_msgdef((const upb_msgdef *)defs[i++]);
|
||||
}
|
||||
|
||||
CHECK_UPB(upb_symtab_add(self->symtab, (upb_def **)defs, num, NULL, &status),
|
||||
"Unable to add defs to DescriptorPool");
|
||||
|
||||
for (temp = self->pending_list->pListHead; temp != NULL;
|
||||
temp = temp->pListNext) {
|
||||
// zval *def_php = *(zval **)temp->pData;
|
||||
// Descriptor* desc = php_to_descriptor(def_php TSRMLS_CC);
|
||||
build_class_from_descriptor((zval *)temp->pDataPtr TSRMLS_CC);
|
||||
}
|
||||
|
||||
FREE(defs);
|
||||
zend_hash_destroy(self->pending_list);
|
||||
zend_hash_init(self->pending_list, 1, NULL, ZVAL_PTR_DTOR, 0);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Descriptor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zend_function_entry descriptor_methods[] = {
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS(Descriptor, descriptor, "Google\\Protobuf\\Descriptor");
|
||||
|
||||
void descriptor_free_c(Descriptor *self TSRMLS_DC) {
|
||||
upb_msg_field_iter iter;
|
||||
upb_msg_field_begin(&iter, self->msgdef);
|
||||
while (!upb_msg_field_done(&iter)) {
|
||||
upb_fielddef *fielddef = upb_msg_iter_field(&iter);
|
||||
upb_fielddef_unref(fielddef, &fielddef);
|
||||
upb_msg_field_next(&iter);
|
||||
}
|
||||
upb_msgdef_unref(self->msgdef, &self->msgdef);
|
||||
if (self->layout) {
|
||||
free_layout(self->layout);
|
||||
}
|
||||
}
|
||||
|
||||
static void descriptor_add_field(Descriptor *desc,
|
||||
const upb_fielddef *fielddef) {
|
||||
upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
|
||||
upb_fielddef *mut_field_def = check_fielddef_notfrozen(fielddef);
|
||||
CHECK_UPB(upb_msgdef_addfield(mut_def, mut_field_def, NULL, &status),
|
||||
"Adding field to Descriptor failed");
|
||||
// add_def_obj(fielddef, obj);
|
||||
}
|
||||
|
||||
void descriptor_init_c_instance(Descriptor* desc TSRMLS_DC) {
|
||||
zend_object_std_init(&desc->std, descriptor_type TSRMLS_CC);
|
||||
desc->msgdef = upb_msgdef_new(&desc->msgdef);
|
||||
desc->layout = NULL;
|
||||
// MAKE_STD_ZVAL(intern->klass);
|
||||
// ZVAL_NULL(intern->klass);
|
||||
desc->pb_serialize_handlers = NULL;
|
||||
}
|
||||
|
||||
void Descriptor_name_set(Descriptor *desc, const char *name) {
|
||||
upb_msgdef *mut_def = check_msgdef_notfrozen(desc->msgdef);
|
||||
CHECK_UPB(upb_msgdef_setfullname(mut_def, name, &status),
|
||||
"Error setting Descriptor name");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// FieldDescriptor
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static void field_descriptor_name_set(const upb_fielddef* fielddef,
|
||||
const char *name) {
|
||||
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
|
||||
CHECK_UPB(upb_fielddef_setname(mut_def, name, &status),
|
||||
"Error setting FieldDescriptor name");
|
||||
}
|
||||
|
||||
static void field_descriptor_label_set(const upb_fielddef* fielddef,
|
||||
upb_label_t upb_label) {
|
||||
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
|
||||
upb_fielddef_setlabel(mut_def, upb_label);
|
||||
}
|
||||
|
||||
upb_fieldtype_t string_to_descriptortype(const char *type) {
|
||||
#define CONVERT(upb, str) \
|
||||
if (!strcmp(type, str)) { \
|
||||
return UPB_DESCRIPTOR_TYPE_##upb; \
|
||||
}
|
||||
|
||||
CONVERT(FLOAT, "float");
|
||||
CONVERT(DOUBLE, "double");
|
||||
CONVERT(BOOL, "bool");
|
||||
CONVERT(STRING, "string");
|
||||
CONVERT(BYTES, "bytes");
|
||||
CONVERT(MESSAGE, "message");
|
||||
CONVERT(GROUP, "group");
|
||||
CONVERT(ENUM, "enum");
|
||||
CONVERT(INT32, "int32");
|
||||
CONVERT(INT64, "int64");
|
||||
CONVERT(UINT32, "uint32");
|
||||
CONVERT(UINT64, "uint64");
|
||||
CONVERT(SINT32, "sint32");
|
||||
CONVERT(SINT64, "sint64");
|
||||
CONVERT(FIXED32, "fixed32");
|
||||
CONVERT(FIXED64, "fixed64");
|
||||
CONVERT(SFIXED32, "sfixed32");
|
||||
CONVERT(SFIXED64, "sfixed64");
|
||||
|
||||
#undef CONVERT
|
||||
|
||||
zend_error(E_ERROR, "Unknown field type.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void field_descriptor_type_set(const upb_fielddef* fielddef,
|
||||
const char *type) {
|
||||
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
|
||||
upb_fielddef_setdescriptortype(mut_def, string_to_descriptortype(type));
|
||||
}
|
||||
|
||||
static void field_descriptor_number_set(const upb_fielddef* fielddef,
|
||||
int number) {
|
||||
upb_fielddef *mut_def = check_fielddef_notfrozen(fielddef);
|
||||
CHECK_UPB(upb_fielddef_setnumber(mut_def, number, &status),
|
||||
"Error setting field number");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// MessageBuilderContext
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zend_function_entry message_builder_context_methods[] = {
|
||||
PHP_ME(MessageBuilderContext, finalizeToPool, NULL, ZEND_ACC_PUBLIC)
|
||||
PHP_ME(MessageBuilderContext, optional, NULL, ZEND_ACC_PUBLIC)
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
DEFINE_CLASS(MessageBuilderContext, message_builder_context,
|
||||
"Google\\Protobuf\\Internal\\MessageBuilderContext");
|
||||
|
||||
void message_builder_context_free_c(MessageBuilderContext *context TSRMLS_DC) {
|
||||
zval_ptr_dtor(&context->descriptor);
|
||||
zval_ptr_dtor(&context->pool);
|
||||
}
|
||||
|
||||
void message_builder_context_init_c_instance(
|
||||
MessageBuilderContext *context TSRMLS_DC) {
|
||||
zend_object_std_init(&context->std, message_builder_context_type TSRMLS_CC);
|
||||
PROTOBUF_CREATE_ZEND_WRAPPER(Descriptor, descriptor, context->descriptor,
|
||||
desc);
|
||||
}
|
||||
|
||||
static void msgdef_add_field(Descriptor *desc, upb_label_t upb_label,
|
||||
const char *name, const char *type, int number,
|
||||
const char *type_class) {
|
||||
upb_fielddef *fielddef = upb_fielddef_new(&fielddef);
|
||||
upb_fielddef_setpacked(fielddef, false);
|
||||
|
||||
field_descriptor_label_set(fielddef, upb_label);
|
||||
field_descriptor_name_set(fielddef, name);
|
||||
field_descriptor_type_set(fielddef, type);
|
||||
field_descriptor_number_set(fielddef, number);
|
||||
|
||||
// // if (type_class != Qnil) {
|
||||
// // if (TYPE(type_class) != T_STRING) {
|
||||
// // rb_raise(rb_eArgError, "Expected string for type class");
|
||||
// // }
|
||||
// // // Make it an absolute type name by prepending a dot.
|
||||
// // type_class = rb_str_append(rb_str_new2("."), type_class);
|
||||
// // rb_funcall(fielddef, rb_intern("submsg_name="), 1, type_class);
|
||||
// // }
|
||||
descriptor_add_field(desc, fielddef);
|
||||
}
|
||||
|
||||
PHP_METHOD(MessageBuilderContext, optional) {
|
||||
MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
|
||||
Descriptor *desc = php_to_descriptor(self->descriptor TSRMLS_CC);
|
||||
// VALUE name, type, number, type_class;
|
||||
const char *name, *type, *type_class;
|
||||
int number, name_str_len, type_str_len, type_class_str_len;
|
||||
if (ZEND_NUM_ARGS() == 3) {
|
||||
if (zend_parse_parameters(3 TSRMLS_CC, "ssl", &name,
|
||||
&name_str_len, &type, &type_str_len, &number) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (zend_parse_parameters(4 TSRMLS_CC, "ssls", &name,
|
||||
&name_str_len, &type, &type_str_len, &number, &type_class,
|
||||
&type_class_str_len) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
msgdef_add_field(desc, UPB_LABEL_OPTIONAL, name, type, number, type_class);
|
||||
|
||||
zval_copy_ctor(getThis());
|
||||
RETURN_ZVAL(getThis(), 1, 0);
|
||||
}
|
||||
|
||||
PHP_METHOD(MessageBuilderContext, finalizeToPool) {
|
||||
MessageBuilderContext *self = php_to_message_builder_context(getThis() TSRMLS_CC);
|
||||
DescriptorPool *pool = php_to_descriptor_pool(self->pool TSRMLS_CC);
|
||||
Descriptor* desc = php_to_descriptor(self->descriptor TSRMLS_CC);
|
||||
|
||||
Z_ADDREF_P(self->descriptor);
|
||||
zend_hash_next_index_insert(pool->pending_list, &self->descriptor,
|
||||
sizeof(zval *), NULL);
|
||||
RETURN_ZVAL(self->pool, 1, 0);
|
||||
}
|
273
php/ext/google/protobuf/message.c
Normal file
273
php/ext/google/protobuf/message.c
Normal file
@ -0,0 +1,273 @@
|
||||
// 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 <php.h>
|
||||
|
||||
#include "protobuf.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Class/module creation from msgdefs and enumdefs, respectively.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void* message_data(void* msg) {
|
||||
return ((uint8_t *)msg) + sizeof(MessageHeader);
|
||||
}
|
||||
|
||||
void message_set_property(zval* object, zval* field_name, zval* value,
|
||||
const zend_literal* key TSRMLS_DC) {
|
||||
const upb_fielddef* field;
|
||||
|
||||
MessageHeader* self = zend_object_store_get_object(object TSRMLS_CC);
|
||||
|
||||
CHECK_TYPE(field_name, IS_STRING);
|
||||
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(field_name));
|
||||
if (field == NULL) {
|
||||
zend_error(E_ERROR, "Unknown field: %s", Z_STRVAL_P(field_name));
|
||||
}
|
||||
layout_set(self->descriptor->layout, message_data(self), field, value);
|
||||
}
|
||||
|
||||
zval* message_get_property(zval* object, zval* member, int type,
|
||||
const zend_literal* key TSRMLS_DC) {
|
||||
MessageHeader* self =
|
||||
(MessageHeader*)zend_object_store_get_object(object TSRMLS_CC);
|
||||
CHECK_TYPE(member, IS_STRING);
|
||||
|
||||
const upb_fielddef* field;
|
||||
field = upb_msgdef_ntofz(self->descriptor->msgdef, Z_STRVAL_P(member));
|
||||
if (field == NULL) {
|
||||
return EG(uninitialized_zval_ptr);
|
||||
}
|
||||
zval* retval = layout_get(self->descriptor->layout, message_data(self), field TSRMLS_CC);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static zend_function_entry message_methods[] = {
|
||||
PHP_ME(Message, encode, NULL, ZEND_ACC_PUBLIC)
|
||||
{NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
/* stringsink *****************************************************************/
|
||||
|
||||
// This should probably be factored into a common upb component.
|
||||
|
||||
typedef struct {
|
||||
upb_byteshandler handler;
|
||||
upb_bytessink sink;
|
||||
char *ptr;
|
||||
size_t len, size;
|
||||
} stringsink;
|
||||
|
||||
static void *stringsink_start(void *_sink, const void *hd, size_t size_hint) {
|
||||
stringsink *sink = _sink;
|
||||
sink->len = 0;
|
||||
return sink;
|
||||
}
|
||||
|
||||
static size_t stringsink_string(void *_sink, const void *hd, const char *ptr,
|
||||
size_t len, const upb_bufhandle *handle) {
|
||||
stringsink *sink = _sink;
|
||||
size_t new_size = sink->size;
|
||||
|
||||
UPB_UNUSED(hd);
|
||||
UPB_UNUSED(handle);
|
||||
|
||||
while (sink->len + len > new_size) {
|
||||
new_size *= 2;
|
||||
}
|
||||
|
||||
if (new_size != sink->size) {
|
||||
sink->ptr = realloc(sink->ptr, new_size);
|
||||
sink->size = new_size;
|
||||
}
|
||||
|
||||
memcpy(sink->ptr + sink->len, ptr, len);
|
||||
sink->len += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void stringsink_init(stringsink *sink) {
|
||||
upb_byteshandler_init(&sink->handler);
|
||||
upb_byteshandler_setstartstr(&sink->handler, stringsink_start, NULL);
|
||||
upb_byteshandler_setstring(&sink->handler, stringsink_string, NULL);
|
||||
|
||||
upb_bytessink_reset(&sink->sink, &sink->handler, sink);
|
||||
|
||||
sink->size = 32;
|
||||
sink->ptr = malloc(sink->size);
|
||||
sink->len = 0;
|
||||
}
|
||||
|
||||
void stringsink_uninit(stringsink *sink) { free(sink->ptr); }
|
||||
|
||||
// Stack-allocated context during an encode/decode operation. Contains the upb
|
||||
// environment and its stack-based allocator, an initial buffer for allocations
|
||||
// to avoid malloc() when possible, and a template for PHP exception messages
|
||||
// if any error occurs.
|
||||
#define STACK_ENV_STACKBYTES 4096
|
||||
typedef struct {
|
||||
upb_env env;
|
||||
upb_seededalloc alloc;
|
||||
const char *php_error_template;
|
||||
char allocbuf[STACK_ENV_STACKBYTES];
|
||||
} stackenv;
|
||||
|
||||
static void stackenv_init(stackenv* se, const char* errmsg);
|
||||
static void stackenv_uninit(stackenv* se);
|
||||
|
||||
// Callback invoked by upb if any error occurs during parsing or serialization.
|
||||
static bool env_error_func(void* ud, const upb_status* status) {
|
||||
stackenv* se = ud;
|
||||
// Free the env -- rb_raise will longjmp up the stack past the encode/decode
|
||||
// function so it would not otherwise have been freed.
|
||||
stackenv_uninit(se);
|
||||
|
||||
// TODO(teboring): have a way to verify that this is actually a parse error,
|
||||
// instead of just throwing "parse error" unconditionally.
|
||||
zend_error(E_ERROR, se->php_error_template);
|
||||
// Never reached.
|
||||
return false;
|
||||
}
|
||||
|
||||
static void stackenv_init(stackenv* se, const char* errmsg) {
|
||||
se->php_error_template = errmsg;
|
||||
upb_env_init(&se->env);
|
||||
upb_seededalloc_init(&se->alloc, &se->allocbuf, STACK_ENV_STACKBYTES);
|
||||
upb_env_setallocfunc(&se->env, upb_seededalloc_getallocfunc(&se->alloc),
|
||||
&se->alloc);
|
||||
upb_env_seterrorfunc(&se->env, env_error_func, se);
|
||||
}
|
||||
|
||||
static void stackenv_uninit(stackenv* se) {
|
||||
upb_env_uninit(&se->env);
|
||||
upb_seededalloc_uninit(&se->alloc);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Message
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static const upb_handlers* msgdef_pb_serialize_handlers(Descriptor* desc) {
|
||||
if (desc->pb_serialize_handlers == NULL) {
|
||||
desc->pb_serialize_handlers =
|
||||
upb_pb_encoder_newhandlers(desc->msgdef, &desc->pb_serialize_handlers);
|
||||
}
|
||||
return desc->pb_serialize_handlers;
|
||||
}
|
||||
|
||||
PHP_METHOD(Message, encode) {
|
||||
Descriptor* desc = (Descriptor*)zend_object_store_get_object(
|
||||
CE_STATIC_MEMBERS(Z_OBJCE_P(getThis()))[0] TSRMLS_CC);
|
||||
|
||||
stringsink sink;
|
||||
stringsink_init(&sink);
|
||||
|
||||
{
|
||||
const upb_handlers* serialize_handlers = msgdef_pb_serialize_handlers(desc);
|
||||
|
||||
stackenv se;
|
||||
upb_pb_encoder* encoder;
|
||||
|
||||
stackenv_init(&se, "Error occurred during encoding: %s");
|
||||
encoder = upb_pb_encoder_create(&se.env, serialize_handlers, &sink.sink);
|
||||
|
||||
putmsg(getThis(), desc, upb_pb_encoder_input(encoder), 0);
|
||||
|
||||
RETVAL_STRINGL(sink.ptr, sink.len, 1);
|
||||
|
||||
stackenv_uninit(&se);
|
||||
stringsink_uninit(&sink);
|
||||
}
|
||||
}
|
||||
|
||||
void message_free(void * object TSRMLS_DC) {
|
||||
FREE(object);
|
||||
}
|
||||
|
||||
zend_object_value message_create(zend_class_entry* ce TSRMLS_DC) {
|
||||
zend_object_value return_value;
|
||||
|
||||
zval* php_descriptor = get_def_obj(ce);
|
||||
|
||||
Descriptor* desc = zend_object_store_get_object(php_descriptor TSRMLS_CC);
|
||||
MessageHeader* msg = (MessageHeader*)ALLOC_N(
|
||||
uint8_t, sizeof(MessageHeader) + desc->layout->size);
|
||||
memset(message_data(msg), 0, desc->layout->size);
|
||||
|
||||
// We wrap first so that everything in the message object is GC-rooted in case
|
||||
// a collection happens during object creation in layout_init().
|
||||
msg->descriptor = desc;
|
||||
|
||||
layout_init(desc->layout, message_data(msg));
|
||||
zend_object_std_init(&msg->std, ce TSRMLS_CC);
|
||||
|
||||
return_value.handle = zend_objects_store_put(
|
||||
msg, (zend_objects_store_dtor_t)zend_objects_destroy_object,
|
||||
message_free, NULL TSRMLS_CC);
|
||||
|
||||
return_value.handlers = PROTOBUF_G(message_handlers);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
const zend_class_entry* build_class_from_descriptor(
|
||||
zval* php_descriptor TSRMLS_DC) {
|
||||
Descriptor* desc = php_to_descriptor(php_descriptor);
|
||||
if (desc->layout == NULL) {
|
||||
MessageLayout* layout = create_layout(desc->msgdef);
|
||||
desc->layout = layout;
|
||||
}
|
||||
// TODO(teboring): Add it back.
|
||||
// if (desc->fill_method == NULL) {
|
||||
// desc->fill_method = new_fillmsg_decodermethod(desc, &desc->fill_method);
|
||||
// }
|
||||
|
||||
const char* name = upb_msgdef_fullname(desc->msgdef);
|
||||
if (name == NULL) {
|
||||
zend_error(E_ERROR, "Descriptor does not have assigned name.");
|
||||
}
|
||||
|
||||
zend_class_entry class_entry;
|
||||
INIT_CLASS_ENTRY_EX(class_entry, name, strlen(name), message_methods);
|
||||
zend_class_entry* registered_ce =
|
||||
zend_register_internal_class(&class_entry TSRMLS_CC);
|
||||
|
||||
add_def_obj(registered_ce, php_descriptor);
|
||||
|
||||
if (PROTOBUF_G(message_handlers) == NULL) {
|
||||
PROTOBUF_G(message_handlers) = ALLOC(zend_object_handlers);
|
||||
memcpy(PROTOBUF_G(message_handlers), zend_get_std_object_handlers(),
|
||||
sizeof(zend_object_handlers));
|
||||
PROTOBUF_G(message_handlers)->write_property = message_set_property;
|
||||
PROTOBUF_G(message_handlers)->read_property = message_get_property;
|
||||
}
|
||||
|
||||
registered_ce->create_object = message_create;
|
||||
}
|
89
php/ext/google/protobuf/protobuf.c
Normal file
89
php/ext/google/protobuf/protobuf.c
Normal file
@ -0,0 +1,89 @@
|
||||
#include "protobuf.h"
|
||||
|
||||
#include <zend_hash.h>
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(protobuf)
|
||||
static PHP_GINIT_FUNCTION(protobuf);
|
||||
static PHP_GSHUTDOWN_FUNCTION(protobuf);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
|
||||
// instances.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
void add_def_obj(const void* def, zval* value) {
|
||||
uint nIndex = (ulong)def & PROTOBUF_G(upb_def_to_php_obj_map).nTableMask;
|
||||
|
||||
zval* pDest = NULL;
|
||||
Z_ADDREF_P(value);
|
||||
zend_hash_index_update(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
|
||||
&value, sizeof(zval*), &pDest);
|
||||
}
|
||||
|
||||
zval* get_def_obj(const void* def) {
|
||||
zval** value;
|
||||
if (zend_hash_index_find(&PROTOBUF_G(upb_def_to_php_obj_map), (zend_ulong)def,
|
||||
&value) == FAILURE) {
|
||||
zend_error(E_ERROR, "PHP object not found for given definition.\n");
|
||||
return NULL;
|
||||
}
|
||||
return *value;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Utilities.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// define the function(s) we want to add
|
||||
zend_function_entry protobuf_functions[] = {
|
||||
ZEND_FE(get_generated_pool, NULL)
|
||||
ZEND_FE_END
|
||||
};
|
||||
|
||||
// "protobuf_functions" refers to the struct defined above
|
||||
// we'll be filling in more of this later: you can use this to specify
|
||||
// globals, php.ini info, startup and teardown functions, etc.
|
||||
zend_module_entry protobuf_module_entry = {
|
||||
STANDARD_MODULE_HEADER,
|
||||
PHP_PROTOBUF_EXTNAME, // extension name
|
||||
protobuf_functions, // function list
|
||||
PHP_MINIT(protobuf), // process startup
|
||||
NULL, // process shutdown
|
||||
NULL, // request startup
|
||||
NULL, // request shutdown
|
||||
NULL, // extension info
|
||||
PHP_PROTOBUF_VERSION, // extension version
|
||||
PHP_MODULE_GLOBALS(protobuf), // globals descriptor
|
||||
PHP_GINIT(protobuf), // globals ctor
|
||||
PHP_GSHUTDOWN(protobuf), // globals dtor
|
||||
NULL, // post deactivate
|
||||
STANDARD_MODULE_PROPERTIES_EX
|
||||
};
|
||||
|
||||
// install module
|
||||
ZEND_GET_MODULE(protobuf)
|
||||
|
||||
// global variables
|
||||
static PHP_GINIT_FUNCTION(protobuf) {
|
||||
protobuf_globals->generated_pool = NULL;
|
||||
generated_pool = NULL;
|
||||
protobuf_globals->message_handlers = NULL;
|
||||
zend_hash_init(&protobuf_globals->upb_def_to_php_obj_map, 16, NULL,
|
||||
ZVAL_PTR_DTOR, 0);
|
||||
}
|
||||
|
||||
static PHP_GSHUTDOWN_FUNCTION(protobuf) {
|
||||
if (protobuf_globals->generated_pool != NULL) {
|
||||
FREE_ZVAL(protobuf_globals->generated_pool);
|
||||
}
|
||||
if (protobuf_globals->message_handlers != NULL) {
|
||||
FREE(protobuf_globals->message_handlers);
|
||||
}
|
||||
zend_hash_destroy(&protobuf_globals->upb_def_to_php_obj_map);
|
||||
}
|
||||
|
||||
PHP_MINIT_FUNCTION(protobuf) {
|
||||
descriptor_pool_init(TSRMLS_C);
|
||||
descriptor_init(TSRMLS_C);
|
||||
message_builder_context_init(TSRMLS_C);
|
||||
}
|
281
php/ext/google/protobuf/protobuf.h
Normal file
281
php/ext/google/protobuf/protobuf.h
Normal file
@ -0,0 +1,281 @@
|
||||
#ifndef __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
|
||||
#define __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
|
||||
|
||||
#include <php.h>
|
||||
|
||||
#include "upb.h"
|
||||
|
||||
#define PHP_PROTOBUF_EXTNAME "protobuf"
|
||||
#define PHP_PROTOBUF_VERSION "0.01"
|
||||
|
||||
// Forward decls.
|
||||
struct DescriptorPool;
|
||||
struct Descriptor;
|
||||
struct FieldDescriptor;
|
||||
struct EnumDescriptor;
|
||||
struct MessageLayout;
|
||||
struct MessageField;
|
||||
struct MessageHeader;
|
||||
struct MessageBuilderContext;
|
||||
struct EnumBuilderContext;
|
||||
|
||||
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;
|
||||
|
||||
extern zend_class_entry* builder_type;
|
||||
extern zend_class_entry* descriptor_type;
|
||||
extern zend_class_entry* message_builder_context_type;
|
||||
|
||||
extern DescriptorPool* generated_pool; // The actual generated pool
|
||||
|
||||
ZEND_BEGIN_MODULE_GLOBALS(protobuf)
|
||||
zval* generated_pool;
|
||||
zend_object_handlers* message_handlers;
|
||||
HashTable upb_def_to_php_obj_map;
|
||||
ZEND_END_MODULE_GLOBALS(protobuf)
|
||||
|
||||
ZEND_DECLARE_MODULE_GLOBALS(protobuf)
|
||||
|
||||
#ifdef ZTS
|
||||
#define PROTOBUF_G(v) TSRMG(protobuf_globals_id, zend_protobuf_globals*, v)
|
||||
#else
|
||||
#define PROTOBUF_G(v) (protobuf_globals.v)
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP functions and global variables.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
PHP_MINIT_FUNCTION(protobuf);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP class structure.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct DescriptorPool {
|
||||
zend_object std;
|
||||
upb_symtab* symtab;
|
||||
HashTable* pending_list;
|
||||
};
|
||||
|
||||
struct Descriptor {
|
||||
zend_object std;
|
||||
const upb_msgdef* msgdef;
|
||||
MessageLayout* layout;
|
||||
// zval* klass; // begins as NULL
|
||||
// const upb_handlers* fill_handlers;
|
||||
// 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.
|
||||
// zval_array typeclass_references;
|
||||
};
|
||||
|
||||
struct FieldDescriptor {
|
||||
zend_object std;
|
||||
const upb_fielddef* fielddef;
|
||||
};
|
||||
|
||||
struct OneofDescriptor {
|
||||
zend_object std;
|
||||
const upb_oneofdef* oneofdef;
|
||||
};
|
||||
|
||||
struct EnumDescriptor {
|
||||
zend_object std;
|
||||
const upb_enumdef* enumdef;
|
||||
// zval* module; // begins as NULL
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Native slot storage abstraction.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define NATIVE_SLOT_MAX_SIZE sizeof(uint64_t)
|
||||
|
||||
size_t native_slot_size(upb_fieldtype_t type);
|
||||
|
||||
#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);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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;
|
||||
MessageField* fields;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
void layout_init(MessageLayout* layout, void* storage);
|
||||
zval* layout_get(MessageLayout* layout, const void* storage,
|
||||
const upb_fielddef* field TSRMLS_DC);
|
||||
MessageLayout* create_layout(const upb_msgdef* msgdef);
|
||||
void free_layout(MessageLayout* layout);
|
||||
zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
|
||||
const void* memory TSRMLS_DC);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Message class creation.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
struct MessageHeader {
|
||||
zend_object std;
|
||||
Descriptor* descriptor; // kept alive by self.class.descriptor reference.
|
||||
// Data comes after this.
|
||||
};
|
||||
|
||||
struct MessageBuilderContext {
|
||||
zend_object std;
|
||||
zval* descriptor;
|
||||
zval* pool;
|
||||
};
|
||||
|
||||
struct OneofBuilderContext {
|
||||
zend_object std;
|
||||
// VALUE descriptor;
|
||||
// VALUE builder;
|
||||
};
|
||||
|
||||
struct EnumBuilderContext {
|
||||
zend_object std;
|
||||
// VALUE enumdesc;
|
||||
};
|
||||
|
||||
// Forward-declare all of the PHP method implementations.
|
||||
|
||||
DescriptorPool* php_to_descriptor_pool(zval* value TSRMLS_DC);
|
||||
zend_object_value descriptor_pool_create(zend_class_entry *ce TSRMLS_DC);
|
||||
void descriptor_pool_free_c(DescriptorPool* object TSRMLS_DC);
|
||||
void descriptor_pool_free(void* object TSRMLS_DC);
|
||||
void descriptor_pool_init_c_instance(DescriptorPool* pool TSRMLS_DC);
|
||||
PHP_METHOD(DescriptorPool, addMessage);
|
||||
PHP_METHOD(DescriptorPool, finalize);
|
||||
|
||||
Descriptor* php_to_descriptor(zval* value TSRMLS_DC);
|
||||
zend_object_value descriptor_create(zend_class_entry *ce TSRMLS_DC);
|
||||
void descriptor_init_c_instance(Descriptor* intern TSRMLS_DC);
|
||||
void descriptor_free_c(Descriptor* object TSRMLS_DC);
|
||||
void descriptor_free(void* object TSRMLS_DC);
|
||||
void descriptor_name_set(Descriptor *desc, const char *name);
|
||||
|
||||
MessageBuilderContext* php_to_message_builder_context(zval* value TSRMLS_DC);
|
||||
zend_object_value message_builder_context_create(
|
||||
zend_class_entry* ce TSRMLS_DC);
|
||||
void message_builder_context_init_c_instance(
|
||||
MessageBuilderContext* intern TSRMLS_DC);
|
||||
void message_builder_context_free_c(MessageBuilderContext* object TSRMLS_DC);
|
||||
void message_builder_context_free(void* object TSRMLS_DC);
|
||||
PHP_METHOD(MessageBuilderContext, optional);
|
||||
PHP_METHOD(MessageBuilderContext, finalizeToPool);
|
||||
|
||||
PHP_METHOD(Message, encode);
|
||||
const zend_class_entry* build_class_from_descriptor(
|
||||
zval* php_descriptor TSRMLS_DC);
|
||||
|
||||
PHP_FUNCTION(get_generated_pool);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Global map from upb {msg,enum}defs to wrapper Descriptor/EnumDescriptor
|
||||
// instances.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void add_def_obj(const void* def, zval* value);
|
||||
zval* get_def_obj(const void* def);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Utilities.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// PHP Array utils.
|
||||
#define Z_ARRVAL_SIZE_P(zval_p) zend_hash_num_elements(Z_ARRVAL_P(zval_p))
|
||||
#define Z_ARRVAL_BEGIN_P(zval_p) Z_ARRVAL_P(zval_p)->pListHead
|
||||
#define Z_BUCKET_NEXT_PP(bucket_pp) *bucket_pp = (*bucket_pp)->pListNext
|
||||
|
||||
#define DEFINE_PHP_OBJECT(class_name, class_name_lower, name) \
|
||||
do { \
|
||||
zval* name; \
|
||||
MAKE_STD_ZVAL(name); \
|
||||
object_init_ex(name, class_name_lower##_type); \
|
||||
} while (0)
|
||||
|
||||
#define DEFINE_PHP_WRAPPER(class_name, class_name_lower, name, intern) \
|
||||
zval* name; \
|
||||
MAKE_STD_ZVAL(name); \
|
||||
object_init_ex(name, class_name_lower##_type); \
|
||||
Z_OBJVAL_P(name) \
|
||||
.handle = zend_objects_store_put( \
|
||||
intern, (zend_objects_store_dtor_t)zend_objects_destroy_object, \
|
||||
class_name_lower##_free, NULL TSRMLS_CC);
|
||||
|
||||
#define DEFINE_PHP_ZVAL(name) \
|
||||
do { \
|
||||
zval* name; \
|
||||
MAKE_STD_ZVAL(name); \
|
||||
} while (0)
|
||||
|
||||
#define DEFINE_PHP_STRING(name, value) \
|
||||
do { \
|
||||
zval* name; \
|
||||
MAKE_STD_ZVAL(name); \
|
||||
ZVAL_STRING(name, value, 1); \
|
||||
} while (0)
|
||||
|
||||
// Upb Utilities
|
||||
|
||||
void check_upb_status(const upb_status* status, const char* msg);
|
||||
|
||||
#define CHECK_UPB(code, msg) \
|
||||
do { \
|
||||
upb_status status = UPB_STATUS_INIT; \
|
||||
code; \
|
||||
check_upb_status(&status, msg); \
|
||||
} while (0)
|
||||
|
||||
// Memory management
|
||||
|
||||
#define ALLOC(class_name) (class_name*) emalloc(sizeof(class_name))
|
||||
#define ALLOC_N(class_name, n) (class_name*) emalloc(sizeof(class_name) * n)
|
||||
#define FREE(object) efree(object)
|
||||
|
||||
// Type Checking
|
||||
#define CHECK_TYPE(field, type) \
|
||||
if (Z_TYPE_P(field) != type) { \
|
||||
zend_error(E_ERROR, "Unexpected type"); \
|
||||
}
|
||||
|
||||
#endif // __GOOGLE_PROTOBUF_PHP_PROTOBUF_H__
|
539
php/ext/google/protobuf/storage.c
Normal file
539
php/ext/google/protobuf/storage.c
Normal file
@ -0,0 +1,539 @@
|
||||
#include <stdint.h>
|
||||
#include <protobuf.h>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PHP <-> native slot management.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
static zval* int32_to_zval(int32_t value) {
|
||||
zval* tmp;
|
||||
MAKE_STD_ZVAL(tmp);
|
||||
ZVAL_LONG(tmp, value);
|
||||
php_printf("int32 to zval\n");
|
||||
// ZVAL_LONG(tmp, 1);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
#define DEREF(memory, type) *(type*)(memory)
|
||||
|
||||
size_t native_slot_size(upb_fieldtype_t type) {
|
||||
switch (type) {
|
||||
case UPB_TYPE_FLOAT: return 4;
|
||||
case UPB_TYPE_DOUBLE: return 8;
|
||||
case UPB_TYPE_BOOL: return 1;
|
||||
case UPB_TYPE_STRING: return sizeof(zval*);
|
||||
case UPB_TYPE_BYTES: return sizeof(zval*);
|
||||
case UPB_TYPE_MESSAGE: return sizeof(zval*);
|
||||
case UPB_TYPE_ENUM: return 4;
|
||||
case UPB_TYPE_INT32: return 4;
|
||||
case UPB_TYPE_INT64: return 8;
|
||||
case UPB_TYPE_UINT32: return 4;
|
||||
case UPB_TYPE_UINT64: return 8;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool is_php_num(zval* value) {
|
||||
// Is numerial string also valid?
|
||||
return (Z_TYPE_P(value) == IS_LONG ||
|
||||
Z_TYPE_P(value) == IS_DOUBLE);
|
||||
}
|
||||
|
||||
void native_slot_check_int_range_precision(upb_fieldtype_t type, zval* val) {
|
||||
// TODO(teboring): Add it back.
|
||||
// if (!is_php_num(val)) {
|
||||
// zend_error(E_ERROR, "Expected number type for integral field.");
|
||||
// }
|
||||
|
||||
// if (Z_TYPE_P(val) == IS_DOUBLE) {
|
||||
// double dbl_val = NUM2DBL(val);
|
||||
// if (floor(dbl_val) != dbl_val) {
|
||||
// zend_error(E_ERROR,
|
||||
// "Non-integral floating point value assigned to integer field.");
|
||||
// }
|
||||
// }
|
||||
// if (type == UPB_TYPE_UINT32 || type == UPB_TYPE_UINT64) {
|
||||
// if (NUM2DBL(val) < 0) {
|
||||
// zend_error(E_ERROR,
|
||||
// "Assigning negative value to unsigned integer field.");
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
zval* native_slot_get(upb_fieldtype_t type, /*VALUE type_class,*/
|
||||
const void* memory TSRMLS_DC) {
|
||||
zval* retval = NULL;
|
||||
switch (type) {
|
||||
// TODO(teboring): Add it back.
|
||||
// case UPB_TYPE_FLOAT:
|
||||
// return DBL2NUM(DEREF(memory, float));
|
||||
// case UPB_TYPE_DOUBLE:
|
||||
// return DBL2NUM(DEREF(memory, double));
|
||||
// case UPB_TYPE_BOOL:
|
||||
// return DEREF(memory, int8_t) ? Qtrue : Qfalse;
|
||||
// case UPB_TYPE_STRING:
|
||||
// case UPB_TYPE_BYTES:
|
||||
// case UPB_TYPE_MESSAGE:
|
||||
// return DEREF(memory, VALUE);
|
||||
// case UPB_TYPE_ENUM: {
|
||||
// int32_t val = DEREF(memory, int32_t);
|
||||
// VALUE symbol = enum_lookup(type_class, INT2NUM(val));
|
||||
// if (symbol == Qnil) {
|
||||
// return INT2NUM(val);
|
||||
// } else {
|
||||
// return symbol;
|
||||
// }
|
||||
// }
|
||||
case UPB_TYPE_INT32:
|
||||
return int32_to_zval(DEREF(memory, int32_t));
|
||||
// TODO(teboring): Add it back.
|
||||
// case UPB_TYPE_INT64:
|
||||
// return LL2NUM(DEREF(memory, int64_t));
|
||||
// case UPB_TYPE_UINT32:
|
||||
// return UINT2NUM(DEREF(memory, uint32_t));
|
||||
// case UPB_TYPE_UINT64:
|
||||
// return ULL2NUM(DEREF(memory, uint64_t));
|
||||
default:
|
||||
return EG(uninitialized_zval_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void native_slot_init(upb_fieldtype_t type, void* memory) {
|
||||
switch (type) {
|
||||
case UPB_TYPE_FLOAT:
|
||||
DEREF(memory, float) = 0.0;
|
||||
break;
|
||||
case UPB_TYPE_DOUBLE:
|
||||
DEREF(memory, double) = 0.0;
|
||||
break;
|
||||
case UPB_TYPE_BOOL:
|
||||
DEREF(memory, int8_t) = 0;
|
||||
break;
|
||||
// TODO(teboring): Add it back.
|
||||
// case UPB_TYPE_STRING:
|
||||
// case UPB_TYPE_BYTES:
|
||||
// DEREF(memory, VALUE) = php_str_new2("");
|
||||
// php_enc_associate(DEREF(memory, VALUE), (type == UPB_TYPE_BYTES)
|
||||
// ? kRubyString8bitEncoding
|
||||
// : kRubyStringUtf8Encoding);
|
||||
// break;
|
||||
// case UPB_TYPE_MESSAGE:
|
||||
// DEREF(memory, VALUE) = Qnil;
|
||||
// break;
|
||||
case UPB_TYPE_ENUM:
|
||||
case UPB_TYPE_INT32:
|
||||
DEREF(memory, int32_t) = 0;
|
||||
break;
|
||||
case UPB_TYPE_INT64:
|
||||
DEREF(memory, int64_t) = 0;
|
||||
break;
|
||||
case UPB_TYPE_UINT32:
|
||||
DEREF(memory, uint32_t) = 0;
|
||||
break;
|
||||
case UPB_TYPE_UINT64:
|
||||
DEREF(memory, uint64_t) = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void native_slot_set(upb_fieldtype_t type, /*VALUE type_class,*/ void* memory,
|
||||
zval* 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, zval* value,
|
||||
uint32_t* case_memory,
|
||||
uint32_t case_number) {
|
||||
switch (type) {
|
||||
case UPB_TYPE_FLOAT:
|
||||
if (!Z_TYPE_P(value) == IS_LONG) {
|
||||
zend_error(E_ERROR, "Expected number type for float field.");
|
||||
}
|
||||
DEREF(memory, float) = Z_DVAL_P(value);
|
||||
break;
|
||||
case UPB_TYPE_DOUBLE:
|
||||
// TODO(teboring): Add it back.
|
||||
// if (!is_php_num(value)) {
|
||||
// zend_error(E_ERROR, "Expected number type for double field.");
|
||||
// }
|
||||
// DEREF(memory, double) = Z_DVAL_P(value);
|
||||
break;
|
||||
case UPB_TYPE_BOOL: {
|
||||
int8_t val = -1;
|
||||
if (zval_is_true(value)) {
|
||||
val = 1;
|
||||
} else {
|
||||
val = 0;
|
||||
}
|
||||
// TODO(teboring): Add it back.
|
||||
// else if (value == Qfalse) {
|
||||
// val = 0;
|
||||
// }
|
||||
// else {
|
||||
// php_raise(php_eTypeError, "Invalid argument for boolean field.");
|
||||
// }
|
||||
DEREF(memory, int8_t) = val;
|
||||
break;
|
||||
}
|
||||
case UPB_TYPE_STRING:
|
||||
case UPB_TYPE_BYTES: {
|
||||
// TODO(teboring): Add it back.
|
||||
// if (Z_TYPE_P(value) != IS_STRING) {
|
||||
// zend_error(E_ERROR, "Invalid argument for string field.");
|
||||
// }
|
||||
// native_slot_validate_string_encoding(type, value);
|
||||
// DEREF(memory, zval*) = value;
|
||||
break;
|
||||
}
|
||||
case UPB_TYPE_MESSAGE: {
|
||||
// TODO(teboring): Add it back.
|
||||
// if (CLASS_OF(value) == CLASS_OF(Qnil)) {
|
||||
// value = Qnil;
|
||||
// } else if (CLASS_OF(value) != type_class) {
|
||||
// php_raise(php_eTypeError,
|
||||
// "Invalid type %s to assign to submessage field.",
|
||||
// php_class2name(CLASS_OF(value)));
|
||||
// }
|
||||
// DEREF(memory, VALUE) = value;
|
||||
break;
|
||||
}
|
||||
case UPB_TYPE_ENUM: {
|
||||
// TODO(teboring): Add it back.
|
||||
// int32_t int_val = 0;
|
||||
// if (!is_php_num(value) && TYPE(value) != T_SYMBOL) {
|
||||
// php_raise(php_eTypeError,
|
||||
// "Expected number or symbol type for enum field.");
|
||||
// }
|
||||
// if (TYPE(value) == T_SYMBOL) {
|
||||
// // Ensure that the given symbol exists in the enum module.
|
||||
// VALUE lookup = php_funcall(type_class, php_intern("resolve"), 1, value);
|
||||
// if (lookup == Qnil) {
|
||||
// php_raise(php_eRangeError, "Unknown symbol value for enum field.");
|
||||
// } else {
|
||||
// int_val = NUM2INT(lookup);
|
||||
// }
|
||||
// } else {
|
||||
// native_slot_check_int_range_precision(UPB_TYPE_INT32, value);
|
||||
// int_val = NUM2INT(value);
|
||||
// }
|
||||
// DEREF(memory, int32_t) = int_val;
|
||||
// break;
|
||||
}
|
||||
case UPB_TYPE_INT32:
|
||||
case UPB_TYPE_INT64:
|
||||
case UPB_TYPE_UINT32:
|
||||
case UPB_TYPE_UINT64:
|
||||
native_slot_check_int_range_precision(type, value);
|
||||
switch (type) {
|
||||
case UPB_TYPE_INT32:
|
||||
php_printf("Setting INT32 field\n");
|
||||
DEREF(memory, int32_t) = Z_LVAL_P(value);
|
||||
break;
|
||||
case UPB_TYPE_INT64:
|
||||
// TODO(teboring): Add it back.
|
||||
// DEREF(memory, int64_t) = NUM2LL(value);
|
||||
break;
|
||||
case UPB_TYPE_UINT32:
|
||||
// TODO(teboring): Add it back.
|
||||
// DEREF(memory, uint32_t) = NUM2UINT(value);
|
||||
break;
|
||||
case UPB_TYPE_UINT64:
|
||||
// TODO(teboring): Add it back.
|
||||
// DEREF(memory, uint64_t) = NUM2ULL(value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (case_memory != NULL) {
|
||||
*case_memory = case_number;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Map field utilities.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const upb_msgdef* tryget_map_entry_msgdef(const upb_fielddef* field) {
|
||||
const upb_msgdef* subdef;
|
||||
if (upb_fielddef_label(field) != UPB_LABEL_REPEATED ||
|
||||
upb_fielddef_type(field) != UPB_TYPE_MESSAGE) {
|
||||
return NULL;
|
||||
}
|
||||
subdef = upb_fielddef_msgsubdef(field);
|
||||
return upb_msgdef_mapentry(subdef) ? subdef : NULL;
|
||||
}
|
||||
|
||||
const upb_msgdef* map_entry_msgdef(const upb_fielddef* field) {
|
||||
const upb_msgdef* subdef = tryget_map_entry_msgdef(field);
|
||||
assert(subdef);
|
||||
return subdef;
|
||||
}
|
||||
|
||||
bool is_map_field(const upb_fielddef* field) {
|
||||
return tryget_map_entry_msgdef(field) != NULL;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 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);
|
||||
upb_msg_field_iter it;
|
||||
upb_msg_oneof_iter oit;
|
||||
size_t off = 0;
|
||||
|
||||
layout->fields = ALLOC_N(MessageField, nfields);
|
||||
|
||||
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;
|
||||
|
||||
if (upb_fielddef_containingoneof(field)) {
|
||||
// Oneofs are handled separately below.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Allocate |field_size| bytes for this field in the layout.
|
||||
field_size = 0;
|
||||
if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
|
||||
field_size = sizeof(zval*);
|
||||
} 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.
|
||||
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);
|
||||
upb_oneof_iter fit;
|
||||
|
||||
// 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.
|
||||
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);
|
||||
upb_oneof_iter fit;
|
||||
|
||||
size_t field_size = sizeof(uint32_t);
|
||||
// Align the offset .
|
||||
off = (off + field_size - 1) & ~(field_size - 1);
|
||||
// Assign all fields in the oneof this same offset.
|
||||
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;
|
||||
}
|
||||
|
||||
layout->size = off;
|
||||
|
||||
layout->msgdef = msgdef;
|
||||
upb_msgdef_ref(layout->msgdef, &layout->msgdef);
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
void free_layout(MessageLayout* layout) {
|
||||
FREE(layout->fields);
|
||||
upb_msgdef_unref(layout->msgdef, &layout->msgdef);
|
||||
FREE(layout);
|
||||
}
|
||||
|
||||
// TODO(teboring): Add it back.
|
||||
// VALUE field_type_class(const upb_fielddef* field) {
|
||||
// VALUE type_class = Qnil;
|
||||
// if (upb_fielddef_type(field) == UPB_TYPE_MESSAGE) {
|
||||
// VALUE submsgdesc = get_def_obj(upb_fielddef_subdef(field));
|
||||
// type_class = Descriptor_msgclass(submsgdesc);
|
||||
// } else if (upb_fielddef_type(field) == UPB_TYPE_ENUM) {
|
||||
// VALUE subenumdesc = get_def_obj(upb_fielddef_subdef(field));
|
||||
// type_class = EnumDescriptor_enummodule(subenumdesc);
|
||||
// }
|
||||
// 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);
|
||||
}
|
||||
|
||||
void layout_set(MessageLayout* layout, void* storage, const upb_fielddef* field,
|
||||
zval* val) {
|
||||
void* memory = slot_memory(layout, storage, field);
|
||||
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
|
||||
|
||||
if (upb_fielddef_containingoneof(field)) {
|
||||
if (Z_TYPE_P(val) == IS_NULL) {
|
||||
// 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)) {
|
||||
// TODO(teboring): Add it back.
|
||||
// check_map_field_type(val, field);
|
||||
// DEREF(memory, zval*) = val;
|
||||
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
|
||||
// TODO(teboring): Add it back.
|
||||
// check_repeated_field_type(val, field);
|
||||
// DEREF(memory, zval*) = val;
|
||||
} else {
|
||||
native_slot_set(upb_fielddef_type(field), /*field_type_class(field),*/ memory,
|
||||
val);
|
||||
}
|
||||
}
|
||||
|
||||
void layout_init(MessageLayout* layout, void* storage) {
|
||||
upb_msg_field_iter it;
|
||||
for (upb_msg_field_begin(&it, layout->msgdef); !upb_msg_field_done(&it);
|
||||
upb_msg_field_next(&it)) {
|
||||
const upb_fielddef* field = upb_msg_iter_field(&it);
|
||||
void* memory = slot_memory(layout, storage, field);
|
||||
uint32_t* oneof_case = slot_oneof_case(layout, storage, field);
|
||||
|
||||
if (upb_fielddef_containingoneof(field)) {
|
||||
// TODO(teboring): Add it back.
|
||||
// memset(memory, 0, NATIVE_SLOT_MAX_SIZE);
|
||||
// *oneof_case = ONEOF_CASE_NONE;
|
||||
} else if (is_map_field(field)) {
|
||||
// TODO(teboring): Add it back.
|
||||
// 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_php(upb_fielddef_type(key_field)),
|
||||
// fieldtype_to_php(upb_fielddef_type(value_field)), type_class,
|
||||
// };
|
||||
// map = php_class_new_instance(3, args, cMap);
|
||||
// } else {
|
||||
// VALUE args[2] = {
|
||||
// fieldtype_to_php(upb_fielddef_type(key_field)),
|
||||
// fieldtype_to_php(upb_fielddef_type(value_field)),
|
||||
// };
|
||||
// map = php_class_new_instance(2, args, cMap);
|
||||
// }
|
||||
|
||||
// DEREF(memory, VALUE) = map;
|
||||
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
|
||||
// TODO(teboring): Add it back.
|
||||
// VALUE ary = Qnil;
|
||||
|
||||
// VALUE type_class = field_type_class(field);
|
||||
|
||||
// if (type_class != Qnil) {
|
||||
// VALUE args[2] = {
|
||||
// fieldtype_to_php(upb_fielddef_type(field)), type_class,
|
||||
// };
|
||||
// ary = php_class_new_instance(2, args, cRepeatedField);
|
||||
// } else {
|
||||
// VALUE args[1] = {fieldtype_to_php(upb_fielddef_type(field))};
|
||||
// ary = php_class_new_instance(1, args, cRepeatedField);
|
||||
// }
|
||||
|
||||
// DEREF(memory, VALUE) = ary;
|
||||
} else {
|
||||
native_slot_init(upb_fielddef_type(field), memory);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zval* layout_get(MessageLayout* layout, const void* storage,
|
||||
const upb_fielddef* field TSRMLS_DC) {
|
||||
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 NULL;
|
||||
// TODO(teboring): Add it back.
|
||||
// return Qnil;
|
||||
}
|
||||
return NULL;
|
||||
// TODO(teboring): Add it back.
|
||||
// return native_slot_get(upb_fielddef_type(field), field_type_class(field),
|
||||
// memory);
|
||||
} else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) {
|
||||
return NULL;
|
||||
// TODO(teboring): Add it back.
|
||||
// return *((VALUE*)memory);
|
||||
} else {
|
||||
return native_slot_get(
|
||||
upb_fielddef_type(field), /*field_type_class(field), */
|
||||
memory TSRMLS_CC);
|
||||
}
|
||||
}
|
15
php/ext/google/protobuf/test.php
Normal file
15
php/ext/google/protobuf/test.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace Google\Protobuf;
|
||||
|
||||
$pool = get_generated_pool();
|
||||
$pool->addMessage("TestMessage")
|
||||
->optional("optional_int32_a", "int32", 1)
|
||||
->optional("optional_int32_b", "int32", 2)
|
||||
->finalizeToPool()
|
||||
->finalize();
|
||||
|
||||
$test_message = new \TestMessage();
|
||||
|
||||
?>
|
11990
php/ext/google/protobuf/upb.c
Normal file
11990
php/ext/google/protobuf/upb.c
Normal file
File diff suppressed because it is too large
Load Diff
8217
php/ext/google/protobuf/upb.h
Normal file
8217
php/ext/google/protobuf/upb.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -35,9 +35,10 @@
|
||||
__author__ = 'matthewtoia@google.com (Matt Toia)'
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
import unittest2 as unittest #PY26
|
||||
except ImportError:
|
||||
import unittest
|
||||
|
||||
from google.protobuf import descriptor_pb2
|
||||
from google.protobuf.internal import factory_test2_pb2
|
||||
from google.protobuf import descriptor_database
|
||||
|
@ -35,11 +35,13 @@
|
||||
__author__ = 'matthewtoia@google.com (Matt Toia)'
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
import unittest2 as unittest #PY26
|
||||
except ImportError:
|
||||
import unittest
|
||||
|
||||
from google.protobuf import unittest_import_pb2
|
||||
from google.protobuf import unittest_import_public_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
@ -49,7 +51,6 @@ from google.protobuf.internal import descriptor_pool_test1_pb2
|
||||
from google.protobuf.internal import descriptor_pool_test2_pb2
|
||||
from google.protobuf.internal import factory_test1_pb2
|
||||
from google.protobuf.internal import factory_test2_pb2
|
||||
from google.protobuf.internal import test_util
|
||||
from google.protobuf import descriptor
|
||||
from google.protobuf import descriptor_database
|
||||
from google.protobuf import descriptor_pool
|
||||
@ -350,7 +351,7 @@ class DescriptorPoolTest(unittest.TestCase):
|
||||
|
||||
|
||||
@unittest.skipIf(api_implementation.Type() != 'cpp',
|
||||
'explicit tests of the C++ implementation')
|
||||
'explicit tests of the C++ implementation')
|
||||
class CppDescriptorPoolTest(DescriptorPoolTest):
|
||||
# TODO(amauryfa): remove when descriptor_pool.DescriptorPool() creates true
|
||||
# C++ descriptor pool object for C++ implementation.
|
||||
@ -555,7 +556,7 @@ class AddDescriptorTest(unittest.TestCase):
|
||||
prefix + 'protobuf_unittest.TestAllTypes.NestedMessage').name)
|
||||
|
||||
@unittest.skipIf(api_implementation.Type() == 'cpp',
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
def testMessage(self):
|
||||
self._TestMessage('')
|
||||
self._TestMessage('.')
|
||||
@ -591,13 +592,13 @@ class AddDescriptorTest(unittest.TestCase):
|
||||
prefix + 'protobuf_unittest.TestAllTypes.NestedEnum').name)
|
||||
|
||||
@unittest.skipIf(api_implementation.Type() == 'cpp',
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
def testEnum(self):
|
||||
self._TestEnum('')
|
||||
self._TestEnum('.')
|
||||
|
||||
@unittest.skipIf(api_implementation.Type() == 'cpp',
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
'With the cpp implementation, Add() must be called first')
|
||||
def testFile(self):
|
||||
pool = descriptor_pool.DescriptorPool()
|
||||
pool.AddFileDescriptor(unittest_pb2.DESCRIPTOR)
|
||||
|
@ -37,9 +37,10 @@ __author__ = 'robinson@google.com (Will Robinson)'
|
||||
import sys
|
||||
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
import unittest2 as unittest #PY26
|
||||
except ImportError:
|
||||
import unittest
|
||||
|
||||
from google.protobuf import unittest_custom_options_pb2
|
||||
from google.protobuf import unittest_import_pb2
|
||||
from google.protobuf import unittest_pb2
|
||||
@ -596,7 +597,7 @@ class DescriptorCopyToProtoTest(unittest.TestCase):
|
||||
"""
|
||||
|
||||
self._InternalTestCopyToProto(
|
||||
unittest_pb2._FOREIGNENUM,
|
||||
unittest_pb2.ForeignEnum.DESCRIPTOR,
|
||||
descriptor_pb2.EnumDescriptorProto,
|
||||
TEST_FOREIGN_ENUM_ASCII)
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user