Merge branch 'master' into internal-merge

Temporarily merges google internal changes. The javascript tests are
still failing and require additional investigation.
This commit is contained in:
Jisi Liu 2016-04-01 17:17:59 -07:00
commit 7630a83767
270 changed files with 34767 additions and 5146 deletions

View File

@ -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 \

View File

@ -118,6 +118,8 @@ if (MSVC)
# Build with multiple processes
add_definitions(/MP)
add_definitions(/wd4244 /wd4267 /wd4018 /wd4355 /wd4800 /wd4251 /wd4996 /wd4146 /wd4305)
# Allow big object
add_definitions(/bigobj)
string(REPLACE "/" "\\" PROTOBUF_SOURCE_WIN32_PATH ${protobuf_SOURCE_DIR})
string(REPLACE "/" "\\" PROTOBUF_BINARY_WIN32_PATH ${protobuf_BINARY_DIR})
configure_file(extract_includes.bat.in extract_includes.bat)

View File

@ -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 " +

View 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();
}
}

View File

@ -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

View File

@ -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,7 +50,12 @@ do
echo "Round $PROCESS_ROUND"
CORE_PROTO_IS_CORRECT=1
make $@ protoc &&
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

View 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));
}
}

View 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;
}

View File

@ -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;
@ -59,6 +88,54 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*/
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
* is safe because of the Java Memory Model's "no out-of-thin-air values"
@ -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);

View File

@ -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;

View File

@ -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; }

View File

@ -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;

View File

@ -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());

View File

@ -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;
@ -1094,16 +1114,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
* {@link GeneratedExtension}.
@ -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();
}
@ -1273,4 +1244,214 @@ public abstract class GeneratedMessageLite<
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;
}
}

View File

@ -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>}.
*

View File

@ -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;

View File

@ -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;
@ -231,6 +272,46 @@ public class LazyFieldLite {
}
}
/**
* 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) {
try {
@ -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;
}
}
}

View File

@ -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;
/**

View File

@ -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;
}
}

View File

@ -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

View File

@ -30,7 +30,6 @@
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
/**

View File

@ -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() {

View File

@ -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

View File

@ -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;
@ -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);
}
/**

View File

@ -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("\"", "\\\"");
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -62,15 +62,6 @@ public final class UnknownFieldSetLite {
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;
}
}
}

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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;
}
}

View File

@ -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"));
}
}

View File

@ -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;
/**

View File

@ -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

View File

@ -36,6 +36,7 @@ import junit.framework.TestCase;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
/**
* Test field deprecation
*

View File

@ -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")

View File

@ -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;
/**

View 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);
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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, leaf),
RopeByteString.newInstanceForTest(ByteString.EMPTY, sub),
RopeByteString.newInstanceForTest(lit, ByteString.EMPTY),
RopeByteString.newInstanceForTest(leaf, ByteString.EMPTY),
RopeByteString.newInstanceForTest(sub, ByteString.EMPTY),
RopeByteString.newInstanceForTest(sub, lit)
};
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);
}
}

View File

@ -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 +
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;
@ -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};
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()
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()
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();
}
}

View File

@ -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}.
*

View File

@ -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}.
*

View File

@ -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();
}
}

View File

@ -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;
@ -129,32 +130,6 @@ public class LiteTest extends TestCase {
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());
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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) {

View File

@ -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,7 +81,9 @@ public class ParseExceptionsTest {
@Test public void message_parseFrom_InputStream() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(in);
}
@ -55,7 +92,9 @@ public class ParseExceptionsTest {
@Test public void message_parseFrom_InputStreamAndExtensionRegistry() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(in, ExtensionRegistry.newInstance());
}
@ -64,7 +103,9 @@ public class ParseExceptionsTest {
@Test public void message_parseFrom_CodedInputStream() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in));
}
@ -73,17 +114,21 @@ public class ParseExceptionsTest {
@Test public void message_parseFrom_CodedInputStreamAndExtensionRegistry() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseFrom(CodedInputStream.newInstance(in),
ExtensionRegistry.newInstance());
return DescriptorProto.parseFrom(
CodedInputStream.newInstance(in), ExtensionRegistry.newInstance());
}
});
}
@Test public void message_parseDelimitedFrom_InputStream() {
setupDelimited();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseDelimitedFrom(in);
}
@ -92,7 +137,9 @@ public class ParseExceptionsTest {
@Test public void message_parseDelimitedFrom_InputStreamAndExtensionRegistry() {
setupDelimited();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.parseDelimitedFrom(in, ExtensionRegistry.newInstance());
}
@ -101,7 +148,9 @@ public class ParseExceptionsTest {
@Test public void messageBuilder_mergeFrom_InputStream() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.newBuilder().mergeFrom(in).build();
}
@ -110,16 +159,22 @@ public class ParseExceptionsTest {
@Test public void messageBuilder_mergeFrom_InputStreamAndExtensionRegistry() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.newBuilder().mergeFrom(in, ExtensionRegistry.newInstance()).build();
return DescriptorProto.newBuilder()
.mergeFrom(in, ExtensionRegistry.newInstance())
.build();
}
});
}
@Test public void messageBuilder_mergeFrom_CodedInputStream() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.newBuilder().mergeFrom(CodedInputStream.newInstance(in)).build();
}
@ -128,17 +183,22 @@ public class ParseExceptionsTest {
@Test public void messageBuilder_mergeFrom_CodedInputStreamAndExtensionRegistry() {
setup();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
return DescriptorProto.newBuilder()
.mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance()).build();
.mergeFrom(CodedInputStream.newInstance(in), ExtensionRegistry.newInstance())
.build();
}
});
}
@Test public void messageBuilder_mergeDelimitedFrom_InputStream() {
setupDelimited();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
builder.mergeDelimitedFrom(in);
@ -149,7 +209,9 @@ public class ParseExceptionsTest {
@Test public void messageBuilder_mergeDelimitedFrom_InputStreamAndExtensionRegistry() {
setupDelimited();
verifyExceptions(new ParseTester() {
verifyExceptions(
new ParseTester() {
@Override
public DescriptorProto parse(InputStream in) throws IOException {
DescriptorProto.Builder builder = DescriptorProto.newBuilder();
builder.mergeDelimitedFrom(in, ExtensionRegistry.newInstance());

View File

@ -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;

View File

@ -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.
*

View File

@ -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;

View File

@ -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());
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}

View File

@ -48,46 +48,6 @@ import java.io.IOException;
*/
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();

View File

@ -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.

View File

@ -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;
}
}

View File

@ -64,7 +64,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -60,8 +60,7 @@ 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 Logger logger = Logger.getLogger(FieldMaskTree.class.getName());
private static final String FIELD_PATH_SEPARATOR_REGEX = "\\.";
@ -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 "
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.getFieldBuilder(field).mergeFrom(
(Message) source.getField(field));
destination.setField(field, source.getField(field));
}
} else {
if (source.hasField(field)) {
destination.getFieldBuilder(field).mergeFrom((Message) source.getField(field));
}
}
} else {
destination.setField(field, source.getField(field));

View File

@ -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(
@ -1065,6 +1066,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 {
Descriptor type = builder.getDescriptorForType();
@ -1236,9 +1256,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);

View File

@ -63,17 +63,14 @@ public class FieldMaskTreeTest extends TestCase {
}
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,13 +93,15 @@ public class FieldMaskTreeTest extends TestCase {
}
public void testMerge() throws Exception {
TestAllTypes value = TestAllTypes.newBuilder()
TestAllTypes value =
TestAllTypes.newBuilder()
.setOptionalInt32(1234)
.setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678))
.addRepeatedInt32(4321)
.addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765))
.build();
NestedTestAllTypes source = NestedTestAllTypes.newBuilder()
NestedTestAllTypes source =
NestedTestAllTypes.newBuilder()
.setPayload(value)
.setChild(NestedTestAllTypes.newBuilder().setPayload(value))
.build();
@ -121,87 +120,90 @@ public class FieldMaskTreeTest extends TestCase {
// 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")
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));
@ -209,21 +211,31 @@ public class FieldMaskTreeTest extends TestCase {
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());
}
}

View File

@ -220,7 +220,8 @@ public class JsonFormatTest extends TestCase {
+ " \"1\": \"FOO\",\n"
+ " \"2\": 12345\n"
+ " }\n"
+ "}", toJsonString(mapMessage));
+ "}",
toJsonString(mapMessage));
assertRoundTripEquals(mapMessage);
}
@ -787,6 +788,17 @@ 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 {
@ -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));

View File

@ -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 {

View File

@ -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}

View File

@ -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);

View File

@ -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
View 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;
};

View File

@ -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)) {
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;
}
}
/**
* 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]);
}

View File

@ -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(

View File

@ -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);

View File

@ -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,12 +917,8 @@ 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);
}
}
goog.asserts.fail('Type not convertible to Uint8Array.');
return /** @type {!Uint8Array} */(new Uint8Array(0));

View File

@ -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));
});
});

File diff suppressed because it is too large Load Diff

View File

@ -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
*/

View File

@ -41,6 +41,7 @@ goog.require('proto.jspb.test.IsExtension');
goog.require('proto.jspb.test.Simple1');
describe('debugTest', function() {
it('testSimple1', function() {
if (COMPILED) {

View File

@ -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;
}
for (name in field2) {
field2.hasOwnProperty(name) && (keys[name] = 0);
// 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 keys) {
var val1 = field1[name], val2 = field2[name];
if (goog.isObject(val1) && !goog.isArray(val1)) {
if (extensionObject1 !== undefined) {
throw new Error('invalid jspb state');
return true;
}
extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1;
// 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');
};

View File

@ -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']);
@ -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());
});
});

View File

@ -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)) {
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;
}
}
@ -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));
});
});

View File

@ -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];
}
}

View 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

View 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);
}

View 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;
}

View 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);
}

View 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__

View 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);
}
}

View 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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

View File

@ -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