Temporarily merges google internal changes. The javascript tests are still failing and require additional investigation.pull/1381/head
commit
7630a83767
270 changed files with 34422 additions and 4801 deletions
@ -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(); |
||||
} |
||||
} |
@ -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)); |
||||
} |
||||
} |
@ -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; |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
||||
} |
@ -0,0 +1,137 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf; |
||||
|
||||
/** |
||||
* Provide text format escaping support for proto2 instances. |
||||
*/ |
||||
final class TextFormatEscaper { |
||||
private TextFormatEscaper() {} |
||||
|
||||
private interface ByteSequence { |
||||
int size(); |
||||
byte byteAt(int offset); |
||||
} |
||||
|
||||
/** |
||||
* Escapes bytes in the format used in protocol buffer text format, which |
||||
* is the same as the format used for C string literals. All bytes |
||||
* that are not printable 7-bit ASCII characters are escaped, as well as |
||||
* backslash, single-quote, and double-quote characters. Characters for |
||||
* which no defined short-hand escape sequence is defined will be escaped |
||||
* using 3-digit octal sequences. |
||||
*/ |
||||
static String escapeBytes(final ByteSequence input) { |
||||
final StringBuilder builder = new StringBuilder(input.size()); |
||||
for (int i = 0; i < input.size(); i++) { |
||||
final byte b = input.byteAt(i); |
||||
switch (b) { |
||||
// Java does not recognize \a or \v, apparently.
|
||||
case 0x07: builder.append("\\a"); break; |
||||
case '\b': builder.append("\\b"); break; |
||||
case '\f': builder.append("\\f"); break; |
||||
case '\n': builder.append("\\n"); break; |
||||
case '\r': builder.append("\\r"); break; |
||||
case '\t': builder.append("\\t"); break; |
||||
case 0x0b: builder.append("\\v"); break; |
||||
case '\\': builder.append("\\\\"); break; |
||||
case '\'': builder.append("\\\'"); break; |
||||
case '"' : builder.append("\\\""); break; |
||||
default: |
||||
// Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
|
||||
// printable. Other byte values must be escaped.
|
||||
if (b >= 0x20 && b <= 0x7e) { |
||||
builder.append((char) b); |
||||
} else { |
||||
builder.append('\\'); |
||||
builder.append((char) ('0' + ((b >>> 6) & 3))); |
||||
builder.append((char) ('0' + ((b >>> 3) & 7))); |
||||
builder.append((char) ('0' + (b & 7))); |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
return builder.toString(); |
||||
} |
||||
|
||||
/** |
||||
* Escapes bytes in the format used in protocol buffer text format, which |
||||
* is the same as the format used for C string literals. All bytes |
||||
* that are not printable 7-bit ASCII characters are escaped, as well as |
||||
* backslash, single-quote, and double-quote characters. Characters for |
||||
* which no defined short-hand escape sequence is defined will be escaped |
||||
* using 3-digit octal sequences. |
||||
*/ |
||||
static String escapeBytes(final ByteString input) { |
||||
return escapeBytes(new ByteSequence() { |
||||
@Override |
||||
public int size() { |
||||
return input.size(); |
||||
} |
||||
@Override |
||||
public byte byteAt(int offset) { |
||||
return input.byteAt(offset); |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Like {@link #escapeBytes(ByteString)}, but used for byte array. |
||||
*/ |
||||
static String escapeBytes(final byte[] input) { |
||||
return escapeBytes(new ByteSequence() { |
||||
@Override |
||||
public int size() { |
||||
return input.length; |
||||
} |
||||
@Override |
||||
public byte byteAt(int offset) { |
||||
return input[offset]; |
||||
} |
||||
}); |
||||
} |
||||
|
||||
/** |
||||
* Like {@link #escapeBytes(ByteString)}, but escapes a text string. |
||||
* Non-ASCII characters are first encoded as UTF-8, then each byte is escaped |
||||
* individually as a 3-digit octal escape. Yes, it's weird. |
||||
*/ |
||||
static String escapeText(final String input) { |
||||
return escapeBytes(ByteString.copyFromUtf8(input)); |
||||
} |
||||
|
||||
/** |
||||
* Escape double quotes and backslashes in a String for unicode output of a message. |
||||
*/ |
||||
static String escapeDoubleQuotesAndBackslashes(final String input) { |
||||
return input.replace("\\", "\\\\").replace("\"", "\\\""); |
||||
} |
||||
} |
@ -0,0 +1,225 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf; |
||||
|
||||
import com.google.protobuf.Descriptors.FieldDescriptor; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Map.Entry; |
||||
|
||||
|
||||
/** |
||||
* Data structure which is populated with the locations of each field value parsed from the text. |
||||
* |
||||
* <p>The locations of primary fields values are retrieved by {@code getLocation} or |
||||
* {@code getLocations}. The locations of sub message values are within nested |
||||
* {@code TextFormatParseInfoTree}s and are retrieve by {@getNestedTree} or {code @getNestedTrees}. |
||||
* |
||||
* <p>The {@code TextFormatParseInfoTree} is created by a Builder. |
||||
*/ |
||||
public class TextFormatParseInfoTree { |
||||
|
||||
// Defines a mapping between each field's descriptor to the list of locations where
|
||||
// its value(s) were was encountered.
|
||||
private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField; |
||||
|
||||
// Defines a mapping between a field's descriptor to a list of TextFormatParseInfoTrees for
|
||||
// sub message location information.
|
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree>> subtreesFromField; |
||||
|
||||
/** |
||||
* Construct a {@code TextFormatParseInfoTree}. |
||||
* |
||||
* @param locationsFromField a map of fields to location in the source code |
||||
* @param subtreeBuildersFromField a map of fields to parse tree location information builders |
||||
*/ |
||||
private TextFormatParseInfoTree( |
||||
Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField, |
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree.Builder>> subtreeBuildersFromField) { |
||||
|
||||
// The maps are unmodifiable. The values in the maps are unmodifiable.
|
||||
Map<FieldDescriptor, List<TextFormatParseLocation>> locs = |
||||
new HashMap<FieldDescriptor, List<TextFormatParseLocation>>(); |
||||
for (Entry<FieldDescriptor, List<TextFormatParseLocation>> kv : locationsFromField.entrySet()) { |
||||
locs.put(kv.getKey(), Collections.unmodifiableList(kv.getValue())); |
||||
} |
||||
this.locationsFromField = Collections.unmodifiableMap(locs); |
||||
|
||||
Map<FieldDescriptor, List<TextFormatParseInfoTree>> subs = |
||||
new HashMap<FieldDescriptor, List<TextFormatParseInfoTree>>(); |
||||
for (Entry<FieldDescriptor, List<Builder>> kv : subtreeBuildersFromField.entrySet()) { |
||||
List<TextFormatParseInfoTree> submessagesOfField = new ArrayList<TextFormatParseInfoTree>(); |
||||
for (Builder subBuilder : kv.getValue()) { |
||||
submessagesOfField.add(subBuilder.build()); |
||||
} |
||||
subs.put(kv.getKey(), Collections.unmodifiableList(submessagesOfField)); |
||||
} |
||||
this.subtreesFromField = Collections.unmodifiableMap(subs); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve all the locations of a field. |
||||
* |
||||
* @param fieldDescriptor the the @{link FieldDescriptor} of the desired field |
||||
* @return a list of the locations of values of the field. If there are not values |
||||
* or the field doesn't exist, an empty list is returned. |
||||
*/ |
||||
public List<TextFormatParseLocation> getLocations(final FieldDescriptor fieldDescriptor) { |
||||
List<TextFormatParseLocation> result = locationsFromField.get(fieldDescriptor); |
||||
return (result == null) ? Collections.<TextFormatParseLocation>emptyList() : result; |
||||
} |
||||
|
||||
/** |
||||
* Get the location in the source of a field's value. |
||||
* |
||||
* <p>Returns the {@link TextFormatParseLocation} for index-th value of the field in the parsed |
||||
* text. |
||||
* |
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired field |
||||
* @param index the index of the value. |
||||
* @return the {@link TextFormatParseLocation} of the value |
||||
* @throws IllegalArgumentException index is out of range |
||||
*/ |
||||
public TextFormatParseLocation getLocation(final FieldDescriptor fieldDescriptor, int index) { |
||||
return getFromList(getLocations(fieldDescriptor), index, fieldDescriptor); |
||||
} |
||||
|
||||
/** |
||||
* Retrieve a list of all the location information trees for a sub message field. |
||||
* |
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired field |
||||
* @return A list of {@link TextFormatParseInfoTree} |
||||
*/ |
||||
public List<TextFormatParseInfoTree> getNestedTrees(final FieldDescriptor fieldDescriptor) { |
||||
List<TextFormatParseInfoTree> result = subtreesFromField.get(fieldDescriptor); |
||||
return result == null ? Collections.<TextFormatParseInfoTree>emptyList() : result; |
||||
} |
||||
|
||||
/** |
||||
* Returns the parse info tree for the given field, which must be a message type. |
||||
* |
||||
* @param fieldDescriptor the @{link FieldDescriptor} of the desired sub message |
||||
* @param index the index of message value. |
||||
* @return the {@code ParseInfoTree} of the message value. {@code null} is returned if the field |
||||
* doesn't exist or the index is out of range. |
||||
* @throws IllegalArgumentException if index is out of range |
||||
*/ |
||||
public TextFormatParseInfoTree getNestedTree(final FieldDescriptor fieldDescriptor, int index) { |
||||
return getFromList(getNestedTrees(fieldDescriptor), index, fieldDescriptor); |
||||
} |
||||
|
||||
/** |
||||
* Create a builder for a {@code ParseInfoTree}. |
||||
* |
||||
* @return the builder |
||||
*/ |
||||
public static Builder builder() { |
||||
return new Builder(); |
||||
} |
||||
|
||||
private static <T> T getFromList(List<T> list, int index, FieldDescriptor fieldDescriptor) { |
||||
if (index >= list.size() || index < 0) { |
||||
throw new IllegalArgumentException(String.format("Illegal index field: %s, index %d", |
||||
fieldDescriptor == null ? "<null>" : fieldDescriptor.getName(), index)); |
||||
} |
||||
return list.get(index); |
||||
} |
||||
|
||||
/** |
||||
* Builder for a {@link TextFormatParseInfoTree}. |
||||
*/ |
||||
public static class Builder { |
||||
|
||||
private Map<FieldDescriptor, List<TextFormatParseLocation>> locationsFromField; |
||||
|
||||
// Defines a mapping between a field's descriptor to a list of ParseInfoTrees builders for
|
||||
// sub message location information.
|
||||
private Map<FieldDescriptor, List<Builder>> subtreeBuildersFromField; |
||||
|
||||
/** |
||||
* Create a root level {@ParseInfoTree} builder. |
||||
*/ |
||||
private Builder() { |
||||
locationsFromField = new HashMap<FieldDescriptor, List<TextFormatParseLocation>>(); |
||||
subtreeBuildersFromField = new HashMap<FieldDescriptor, List<Builder>>(); |
||||
} |
||||
|
||||
/** |
||||
* Record the starting location of a single value for a field. |
||||
* |
||||
* @param fieldDescriptor the field |
||||
* @param location source code location information |
||||
*/ |
||||
public Builder setLocation( |
||||
final FieldDescriptor fieldDescriptor, TextFormatParseLocation location) { |
||||
List<TextFormatParseLocation> fieldLocations = locationsFromField.get(fieldDescriptor); |
||||
if (fieldLocations == null) { |
||||
fieldLocations = new ArrayList<TextFormatParseLocation>(); |
||||
locationsFromField.put(fieldDescriptor, fieldLocations); |
||||
} |
||||
fieldLocations.add(location); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Set for a sub message. |
||||
* |
||||
* <p>A new builder is created for a sub message. The builder that is returned is a new builder. |
||||
* The return is <emph>not</emph> the invoked {@code builder.getBuilderForSubMessageField}. |
||||
* |
||||
* @param fieldDescriptor the field whose value is the submessage |
||||
* @return a new Builder for the sub message |
||||
*/ |
||||
public Builder getBuilderForSubMessageField(final FieldDescriptor fieldDescriptor) { |
||||
List<Builder> submessageBuilders = subtreeBuildersFromField.get(fieldDescriptor); |
||||
if (submessageBuilders == null) { |
||||
submessageBuilders = new ArrayList<Builder>(); |
||||
subtreeBuildersFromField.put(fieldDescriptor, submessageBuilders); |
||||
} |
||||
Builder subtreeBuilder = new Builder(); |
||||
submessageBuilders.add(subtreeBuilder); |
||||
return subtreeBuilder; |
||||
} |
||||
|
||||
/** |
||||
* Build the {@code TextFormatParseInfoTree}. |
||||
* |
||||
* @return the {@code TextFormatParseInfoTree} |
||||
*/ |
||||
public TextFormatParseInfoTree build() { |
||||
return new TextFormatParseInfoTree(locationsFromField, subtreeBuildersFromField); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,104 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
/** |
||||
* A location in the source code. |
||||
* |
||||
* <p>A location is the starting line number and starting column number. |
||||
*/ |
||||
public final class TextFormatParseLocation { |
||||
|
||||
/** |
||||
* The empty location. |
||||
*/ |
||||
public static final TextFormatParseLocation EMPTY = new TextFormatParseLocation(-1, -1); |
||||
|
||||
/** |
||||
* Create a location. |
||||
* |
||||
* @param line the starting line number |
||||
* @param column the starting column number |
||||
* @return a {@code ParseLocation} |
||||
*/ |
||||
static TextFormatParseLocation create(int line, int column) { |
||||
if (line == -1 && column == -1) { |
||||
return EMPTY; |
||||
} |
||||
if (line < 0 || column < 0) { |
||||
throw new IllegalArgumentException( |
||||
String.format("line and column values must be >= 0: line %d, column: %d", line, column)); |
||||
} |
||||
return new TextFormatParseLocation(line, column); |
||||
} |
||||
|
||||
private final int line; |
||||
private final int column; |
||||
|
||||
private TextFormatParseLocation(int line, int column) { |
||||
this.line = line; |
||||
this.column = column; |
||||
} |
||||
|
||||
public int getLine() { |
||||
return line; |
||||
} |
||||
|
||||
public int getColumn() { |
||||
return column; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return String.format("ParseLocation{line=%d, column=%d}", line, column); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (o == this) { |
||||
return true; |
||||
} |
||||
if (!(o instanceof TextFormatParseLocation)) { |
||||
return false; |
||||
} |
||||
TextFormatParseLocation that = (TextFormatParseLocation) o; |
||||
return (this.line == that.getLine()) |
||||
&& (this.column == that.getColumn()); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
int[] values = {line, column}; |
||||
return Arrays.hashCode(values); |
||||
} |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,182 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf; |
||||
|
||||
import com.google.protobuf.Descriptors.Descriptor; |
||||
import com.google.protobuf.Descriptors.FieldDescriptor; |
||||
import protobuf_unittest.UnittestProto.TestAllTypes; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
/** |
||||
* Test @{link TextFormatParseInfoTree}. |
||||
*/ |
||||
public class TextFormatParseInfoTreeTest extends TestCase { |
||||
|
||||
private static final Descriptor DESCRIPTOR = TestAllTypes.getDescriptor(); |
||||
private static final FieldDescriptor OPTIONAL_INT32 = |
||||
DESCRIPTOR.findFieldByName("optional_int32"); |
||||
private static final FieldDescriptor OPTIONAL_BOOLEAN = |
||||
DESCRIPTOR.findFieldByName("optional_boolean"); |
||||
private static final FieldDescriptor REPEATED_INT32 = |
||||
DESCRIPTOR.findFieldByName("repeated_int32"); |
||||
private static final FieldDescriptor OPTIONAL_NESTED_MESSAGE = |
||||
DESCRIPTOR.findFieldByName("optional_nested_message"); |
||||
private static final FieldDescriptor REPEATED_NESTED_MESSAGE = |
||||
DESCRIPTOR.findFieldByName("repeated_nested_message"); |
||||
private static final FieldDescriptor FIELD_BB = |
||||
TestAllTypes.NestedMessage.getDescriptor().findFieldByName("bb"); |
||||
|
||||
private static final TextFormatParseLocation LOC0 = TextFormatParseLocation.create(1, 2); |
||||
private static final TextFormatParseLocation LOC1 = TextFormatParseLocation.create(2, 3); |
||||
|
||||
private TextFormatParseInfoTree.Builder rootBuilder; |
||||
|
||||
@Override |
||||
public void setUp() { |
||||
rootBuilder = TextFormatParseInfoTree.builder(); |
||||
} |
||||
|
||||
public void testBuildEmptyParseTree() { |
||||
TextFormatParseInfoTree tree = rootBuilder.build(); |
||||
assertTrue(tree.getLocations(null).isEmpty()); |
||||
} |
||||
|
||||
public void testGetLocationReturnsSingleLocation() { |
||||
rootBuilder.setLocation(OPTIONAL_INT32, LOC0); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertEquals(LOC0, root.getLocation(OPTIONAL_INT32, 0)); |
||||
assertEquals(1, root.getLocations(OPTIONAL_INT32).size()); |
||||
} |
||||
|
||||
public void testGetLocationsReturnsNoParseLocationsForUnknownField() { |
||||
assertTrue(rootBuilder.build().getLocations(OPTIONAL_INT32).isEmpty()); |
||||
rootBuilder.setLocation(OPTIONAL_BOOLEAN, LOC0); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertTrue(root.getLocations(OPTIONAL_INT32).isEmpty()); |
||||
assertEquals(LOC0, root.getLocations(OPTIONAL_BOOLEAN).get(0)); |
||||
} |
||||
|
||||
public void testGetLocationThrowsIllegalArgumentExceptionForUnknownField() { |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
try { |
||||
root.getNestedTree(OPTIONAL_INT32, 0); |
||||
fail("Did not detect unknown field"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
} |
||||
|
||||
public void testGetLocationThrowsIllegalArgumentExceptionForInvalidIndex() { |
||||
TextFormatParseInfoTree root = rootBuilder.setLocation(OPTIONAL_INT32, LOC0).build(); |
||||
try { |
||||
root.getLocation(OPTIONAL_INT32, 1); |
||||
fail("Invalid index not detected"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
try { |
||||
root.getLocation(OPTIONAL_INT32, -1); |
||||
fail("Negative index not detected"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
} |
||||
|
||||
public void testGetLocationsReturnsMultipleLocations() { |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0); |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC1); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertEquals(LOC0, root.getLocation(REPEATED_INT32, 0)); |
||||
assertEquals(LOC1, root.getLocation(REPEATED_INT32, 1)); |
||||
assertEquals(2, root.getLocations(REPEATED_INT32).size()); |
||||
} |
||||
|
||||
public void testGetNestedTreeThrowsIllegalArgumentExceptionForUnknownField() { |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
try { |
||||
root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 0); |
||||
fail("Did not detect unknown field"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
} |
||||
|
||||
public void testGetNestedTreesReturnsNoParseInfoTreesForUnknownField() { |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertTrue(root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).isEmpty()); |
||||
} |
||||
|
||||
public void testGetNestedTreeThrowsIllegalArgumentExceptionForInvalidIndex() { |
||||
rootBuilder.setLocation(REPEATED_INT32, LOC0); |
||||
rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
try { |
||||
root.getNestedTree(OPTIONAL_NESTED_MESSAGE, 1); |
||||
fail("Submessage index that is too large not detected"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
try { |
||||
rootBuilder.build().getNestedTree(OPTIONAL_NESTED_MESSAGE, -1); |
||||
fail("Invalid submessage index (-1) not detected"); |
||||
} catch (IllegalArgumentException expected) { |
||||
// pass
|
||||
} |
||||
} |
||||
|
||||
public void testGetNestedTreesReturnsSingleTree() { |
||||
rootBuilder.getBuilderForSubMessageField(OPTIONAL_NESTED_MESSAGE); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertEquals(1, root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).size()); |
||||
TextFormatParseInfoTree subtree = root.getNestedTrees(OPTIONAL_NESTED_MESSAGE).get(0); |
||||
assertNotNull(subtree); |
||||
} |
||||
|
||||
public void testGetNestedTreesReturnsMultipleTrees() { |
||||
TextFormatParseInfoTree.Builder subtree1Builder = |
||||
rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE); |
||||
subtree1Builder.getBuilderForSubMessageField(FIELD_BB); |
||||
subtree1Builder.getBuilderForSubMessageField(FIELD_BB); |
||||
TextFormatParseInfoTree.Builder subtree2Builder = |
||||
rootBuilder.getBuilderForSubMessageField(REPEATED_NESTED_MESSAGE); |
||||
subtree2Builder.getBuilderForSubMessageField(FIELD_BB); |
||||
TextFormatParseInfoTree root = rootBuilder.build(); |
||||
assertEquals(2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).size()); |
||||
assertEquals( |
||||
2, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(0).getNestedTrees(FIELD_BB).size()); |
||||
assertEquals( |
||||
1, root.getNestedTrees(REPEATED_NESTED_MESSAGE).get(1).getNestedTrees(FIELD_BB).size()); |
||||
} |
||||
} |
@ -0,0 +1,86 @@ |
||||
// Protocol Buffers - Google's data interchange format
|
||||
// Copyright 2008 Google Inc. All rights reserved.
|
||||
// https://developers.google.com/protocol-buffers/
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package com.google.protobuf; |
||||
|
||||
import junit.framework.TestCase; |
||||
|
||||
/** |
||||
* Test @{link TextFormatParseLocation}. |
||||
*/ |
||||
public class TextFormatParseLocationTest extends TestCase { |
||||
|
||||
public void testCreateEmpty() { |
||||
TextFormatParseLocation location = TextFormatParseLocation.create(-1, -1); |
||||
assertEquals(TextFormatParseLocation.EMPTY, location); |
||||
} |
||||
|
||||
public void testCreate() { |
||||
TextFormatParseLocation location = TextFormatParseLocation.create(2, 1); |
||||
assertEquals(2, location.getLine()); |
||||
assertEquals(1, location.getColumn()); |
||||
} |
||||
|
||||
public void testCreateThrowsIllegalArgumentExceptionForInvalidIndex() { |
||||
try { |
||||
TextFormatParseLocation.create(-1, 0); |
||||
fail("Should throw IllegalArgumentException if line is less than 0"); |
||||
} catch (IllegalArgumentException unused) { |
||||
// pass
|
||||
} |
||||
try { |
||||
TextFormatParseLocation.create(0, -1); |
||||
fail("Should throw, column < 0"); |
||||
} catch (IllegalArgumentException unused) { |
||||
// pass
|
||||
} |
||||
} |
||||
|
||||
public void testHashCode() { |
||||
TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1); |
||||
TextFormatParseLocation loc1 = TextFormatParseLocation.create(2, 1); |
||||
|
||||
assertEquals(loc0.hashCode(), loc1.hashCode()); |
||||
assertEquals( |
||||
TextFormatParseLocation.EMPTY.hashCode(), TextFormatParseLocation.EMPTY.hashCode()); |
||||
} |
||||
|
||||
public void testEquals() { |
||||
TextFormatParseLocation loc0 = TextFormatParseLocation.create(2, 1); |
||||
TextFormatParseLocation loc1 = TextFormatParseLocation.create(1, 2); |
||||
TextFormatParseLocation loc2 = TextFormatParseLocation.create(2, 2); |
||||
TextFormatParseLocation loc3 = TextFormatParseLocation.create(2, 1); |
||||
|
||||
assertEquals(loc0, loc3); |
||||
assertNotSame(loc0, loc1); |
||||
assertNotSame(loc0, loc2); |
||||
assertNotSame(loc1, loc2); |
||||
} |
||||
} |
@ -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; |
||||
}; |
File diff suppressed because it is too large
Load Diff
@ -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 |
@ -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); |
||||
} |
@ -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; |
||||
} |
@ -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); |
||||
} |
@ -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__
|
@ -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); |
||||
} |
||||
} |
@ -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(); |
||||
|
||||
?> |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue