Down-integrate from google3.

This commit is contained in:
Feng Xiao 2015-08-22 18:25:48 -07:00
parent c3bc155ace
commit eee38b0c01
226 changed files with 8507 additions and 3684 deletions

View File

@ -1,103 +1,103 @@
include(GNUInstallDirs)
foreach(_library
libprotobuf-lite
libprotobuf
libprotoc)
set_property(TARGET ${_library}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
install(TARGETS ${_library} EXPORT protobuf-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${_library}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library})
endforeach()
install(TARGETS protoc EXPORT protobuf-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc)
if(TRUE)
file(STRINGS extract_includes.bat.in _extract_strings
REGEX "^copy")
foreach(_extract_string ${_extract_strings})
string(REPLACE "copy \${PROTOBUF_SOURCE_WIN32_PATH}\\" ""
_extract_string ${_extract_string})
string(REPLACE "\\" "/" _extract_string ${_extract_string})
string(REGEX MATCH "^[^ ]+"
_extract_from ${_extract_string})
string(REGEX REPLACE "^${_extract_from} ([^$]+)" "\\1"
_extract_to ${_extract_string})
get_filename_component(_extract_from "${protobuf_SOURCE_DIR}/${_extract_from}" ABSOLUTE)
get_filename_component(_extract_name ${_extract_to} NAME)
get_filename_component(_extract_to ${_extract_to} PATH)
string(REPLACE "include/" "${CMAKE_INSTALL_INCLUDEDIR}/"
_extract_to "${_extract_to}")
if(EXISTS "${_extract_from}")
install(FILES "${_extract_from}"
DESTINATION "${_extract_to}"
COMPONENT protobuf-headers
RENAME "${_extract_name}")
else()
message(AUTHOR_WARNING "The file \"${_extract_from}\" is listed in "
"\"${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in\" "
"but there not exists. The file will not be installed.")
endif()
endforeach()
endif()
# Internal function for parsing auto tools scripts
function(_protobuf_auto_list FILE_NAME VARIABLE)
file(STRINGS ${FILE_NAME} _strings)
set(_list)
foreach(_string ${_strings})
set(_found)
string(REGEX MATCH "^[ \t]*${VARIABLE}[ \t]*=[ \t]*" _found "${_string}")
if(_found)
string(LENGTH "${_found}" _length)
string(SUBSTRING "${_string}" ${_length} -1 _draft_list)
foreach(_item ${_draft_list})
string(STRIP "${_item}" _item)
list(APPEND _list "${_item}")
endforeach()
endif()
endforeach()
set(${VARIABLE} ${_list} PARENT_SCOPE)
endfunction()
# Install well-known type proto files
_protobuf_auto_list("../src/Makefile.am" nobase_dist_proto_DATA)
foreach(_file ${nobase_dist_proto_DATA})
get_filename_component(_file_from "../src/${_file}" ABSOLUTE)
get_filename_component(_file_name ${_file} NAME)
get_filename_component(_file_path ${_file} PATH)
if(EXISTS "${_file_from}")
install(FILES "${_file_from}"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_file_path}"
COMPONENT protobuf-protos
RENAME "${_file_name}")
else()
message(AUTHOR_WARNING "The file \"${_file_from}\" is listed in "
"\"${protobuf_SOURCE_DIR}/../src/Makefile.am\" as nobase_dist_proto_DATA "
"but there not exists. The file will not be installed.")
endif()
endforeach()
# Export configuration
install(EXPORT protobuf-targets
DESTINATION "lib/cmake/protobuf"
COMPONENT protobuf-export)
configure_file(protobuf-config.cmake.in
protobuf-config.cmake @ONLY)
configure_file(protobuf-config-version.cmake.in
protobuf-config-version.cmake @ONLY)
configure_file(protobuf-module.cmake.in
protobuf-module.cmake @ONLY)
install(FILES
"${protobuf_BINARY_DIR}/protobuf-config.cmake"
"${protobuf_BINARY_DIR}/protobuf-config-version.cmake"
"${protobuf_BINARY_DIR}/protobuf-module.cmake"
DESTINATION "lib/cmake/protobuf"
COMPONENT protobuf-export)
include(GNUInstallDirs)
foreach(_library
libprotobuf-lite
libprotobuf
libprotoc)
set_property(TARGET ${_library}
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
install(TARGETS ${_library} EXPORT protobuf-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${_library}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${_library})
endforeach()
install(TARGETS protoc EXPORT protobuf-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT protoc)
if(TRUE)
file(STRINGS extract_includes.bat.in _extract_strings
REGEX "^copy")
foreach(_extract_string ${_extract_strings})
string(REPLACE "copy \${PROTOBUF_SOURCE_WIN32_PATH}\\" ""
_extract_string ${_extract_string})
string(REPLACE "\\" "/" _extract_string ${_extract_string})
string(REGEX MATCH "^[^ ]+"
_extract_from ${_extract_string})
string(REGEX REPLACE "^${_extract_from} ([^$]+)" "\\1"
_extract_to ${_extract_string})
get_filename_component(_extract_from "${protobuf_SOURCE_DIR}/${_extract_from}" ABSOLUTE)
get_filename_component(_extract_name ${_extract_to} NAME)
get_filename_component(_extract_to ${_extract_to} PATH)
string(REPLACE "include/" "${CMAKE_INSTALL_INCLUDEDIR}/"
_extract_to "${_extract_to}")
if(EXISTS "${_extract_from}")
install(FILES "${_extract_from}"
DESTINATION "${_extract_to}"
COMPONENT protobuf-headers
RENAME "${_extract_name}")
else()
message(AUTHOR_WARNING "The file \"${_extract_from}\" is listed in "
"\"${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in\" "
"but there not exists. The file will not be installed.")
endif()
endforeach()
endif()
# Internal function for parsing auto tools scripts
function(_protobuf_auto_list FILE_NAME VARIABLE)
file(STRINGS ${FILE_NAME} _strings)
set(_list)
foreach(_string ${_strings})
set(_found)
string(REGEX MATCH "^[ \t]*${VARIABLE}[ \t]*=[ \t]*" _found "${_string}")
if(_found)
string(LENGTH "${_found}" _length)
string(SUBSTRING "${_string}" ${_length} -1 _draft_list)
foreach(_item ${_draft_list})
string(STRIP "${_item}" _item)
list(APPEND _list "${_item}")
endforeach()
endif()
endforeach()
set(${VARIABLE} ${_list} PARENT_SCOPE)
endfunction()
# Install well-known type proto files
_protobuf_auto_list("../src/Makefile.am" nobase_dist_proto_DATA)
foreach(_file ${nobase_dist_proto_DATA})
get_filename_component(_file_from "../src/${_file}" ABSOLUTE)
get_filename_component(_file_name ${_file} NAME)
get_filename_component(_file_path ${_file} PATH)
if(EXISTS "${_file_from}")
install(FILES "${_file_from}"
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${_file_path}"
COMPONENT protobuf-protos
RENAME "${_file_name}")
else()
message(AUTHOR_WARNING "The file \"${_file_from}\" is listed in "
"\"${protobuf_SOURCE_DIR}/../src/Makefile.am\" as nobase_dist_proto_DATA "
"but there not exists. The file will not be installed.")
endif()
endforeach()
# Export configuration
install(EXPORT protobuf-targets
DESTINATION "lib/cmake/protobuf"
COMPONENT protobuf-export)
configure_file(protobuf-config.cmake.in
protobuf-config.cmake @ONLY)
configure_file(protobuf-config-version.cmake.in
protobuf-config-version.cmake @ONLY)
configure_file(protobuf-module.cmake.in
protobuf-module.cmake @ONLY)
install(FILES
"${protobuf_BINARY_DIR}/protobuf-config.cmake"
"${protobuf_BINARY_DIR}/protobuf-config-version.cmake"
"${protobuf_BINARY_DIR}/protobuf-module.cmake"
DESTINATION "lib/cmake/protobuf"
COMPONENT protobuf-export)

View File

@ -1 +1 @@
set(PACKAGE_VERSION @protobuf_VERSION@)
set(PACKAGE_VERSION @protobuf_VERSION@)

View File

@ -1,27 +1,27 @@
# Version info variables
set(PROTOBUF_VERSION "@protobuf_VERSION@")
set(PROTOBUF_VERSION_STRING "@protobuf_VERSION_STRING@")
# Current dir
get_filename_component(_PROTOBUF_PACKAGE_PREFIX
"${CMAKE_CURRENT_LIST_FILE}" PATH)
# Imported targets
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-targets.cmake")
# Compute the installation prefix relative to this file.
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_PACKAGE_PREFIX}" PATH)
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_IMPORT_PREFIX}" PATH)
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_IMPORT_PREFIX}" PATH)
# CMake FindProtobuf module compatible file
if(NOT DEFINED PROTOBUF_MODULE_COMPATIBLE OR "${PROTOBUF_MODULE_COMPATIBLE}")
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-module.cmake")
endif()
# Cleanup temporary variables.
set(_PROTOBUF_PACKAGE_PREFIX)
set(_PROTOBUF_IMPORT_PREFIX)
# Version info variables
set(PROTOBUF_VERSION "@protobuf_VERSION@")
set(PROTOBUF_VERSION_STRING "@protobuf_VERSION_STRING@")
# Current dir
get_filename_component(_PROTOBUF_PACKAGE_PREFIX
"${CMAKE_CURRENT_LIST_FILE}" PATH)
# Imported targets
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-targets.cmake")
# Compute the installation prefix relative to this file.
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_PACKAGE_PREFIX}" PATH)
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_IMPORT_PREFIX}" PATH)
get_filename_component(_PROTOBUF_IMPORT_PREFIX
"${_PROTOBUF_IMPORT_PREFIX}" PATH)
# CMake FindProtobuf module compatible file
if(NOT DEFINED PROTOBUF_MODULE_COMPATIBLE OR "${PROTOBUF_MODULE_COMPATIBLE}")
include("${_PROTOBUF_PACKAGE_PREFIX}/protobuf-module.cmake")
endif()
# Cleanup temporary variables.
set(_PROTOBUF_PACKAGE_PREFIX)
set(_PROTOBUF_IMPORT_PREFIX)

View File

@ -294,10 +294,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* <b>Performance notes:</b> The returned {@code ByteString} is an
* immutable tree of byte arrays ("chunks") of the stream data. The
* first chunk is small, with subsequent chunks each being double
* the size, up to 8K. If the caller knows the precise length of
* the stream and wishes to avoid all unnecessary copies and
* allocations, consider using the two-argument version of this
* method, below.
* the size, up to 8K.
*
* <p>Each byte read from the input stream will be copied twice to ensure
* that the resulting ByteString is truly immutable.
*
* @param streamToDrain The source stream, which is read completely
* but not closed.
@ -320,12 +320,10 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
*
* <b>Performance notes:</b> The returned {@code ByteString} is an
* immutable tree of byte arrays ("chunks") of the stream data. The
* chunkSize parameter sets the size of these byte arrays. In
* particular, if the chunkSize is precisely the same as the length
* of the stream, unnecessary allocations and copies will be
* avoided. Otherwise, the chunks will be of the given size, except
* for the last chunk, which will be resized (via a reallocation and
* copy) to contain the remainder of the stream.
* chunkSize parameter sets the size of these byte arrays.
*
* <p>Each byte read from the input stream will be copied twice to ensure
* that the resulting ByteString is truly immutable.
*
* @param streamToDrain The source stream, which is read completely
* but not closed.
@ -386,6 +384,7 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
if (bytesRead == 0) {
return null;
} else {
// Always make a copy since InputStream could steal a reference to buf.
return ByteString.copyFrom(buf, 0, bytesRead);
}
}
@ -736,7 +735,8 @@ public abstract class ByteString implements Iterable<Byte>, Serializable {
* returns the number of bytes remaining in the stream. The methods
* {@link InputStream#read(byte[])}, {@link InputStream#read(byte[],int,int)}
* and {@link InputStream#skip(long)} will read/skip as many bytes as are
* available.
* available. The method {@link InputStream#markSupported()} returns
* {@code true}.
* <p>
* The methods in the returned {@link InputStream} might <b>not</b> be
* thread safe.

View File

@ -30,9 +30,13 @@
package com.google.protobuf;
import com.google.protobuf.Utf8.UnpairedSurrogateException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Encodes and writes protocol message fields.
@ -49,6 +53,10 @@ import java.nio.ByteBuffer;
* @author kneton@google.com Kenton Varda
*/
public final class CodedOutputStream {
private static final Logger logger = Logger.getLogger(CodedOutputStream.class.getName());
// TODO(dweis): Consider migrating to a ByteBuffer.
private final byte[] buffer;
private final int limit;
private int position;
@ -415,15 +423,87 @@ public final class CodedOutputStream {
}
/** Write a {@code string} field to the stream. */
// TODO(dweis): Document behavior on ill-formed UTF-16 input.
public void writeStringNoTag(final String value) throws IOException {
try {
efficientWriteStringNoTag(value);
} catch (UnpairedSurrogateException e) {
logger.log(Level.WARNING,
"Converting ill-formed UTF-16. Your Protocol Buffer will not round trip correctly!", e);
inefficientWriteStringNoTag(value);
}
}
/** Write a {@code string} field to the stream. */
private void inefficientWriteStringNoTag(final String value) throws IOException {
// Unfortunately there does not appear to be any way to tell Java to encode
// UTF-8 directly into our buffer, so we have to let it create its own byte
// array and then copy.
// TODO(dweis): Consider using nio Charset methods instead.
final byte[] bytes = value.getBytes(Internal.UTF_8);
writeRawVarint32(bytes.length);
writeRawBytes(bytes);
}
/**
* Write a {@code string} field to the stream efficiently. If the {@code string} is malformed,
* this method rolls back its changes and throws an {@link UnpairedSurrogateException} with the
* intent that the caller will catch and retry with {@link #inefficientWriteStringNoTag(String)}.
*
* @param value the string to write to the stream
*
* @throws UnpairedSurrogateException when {@code value} is ill-formed UTF-16.
*/
private void efficientWriteStringNoTag(final String value) throws IOException {
// UTF-8 byte length of the string is at least its UTF-16 code unit length (value.length()),
// and at most 3 times of it. We take advantage of this in both branches below.
final int maxLength = value.length() * Utf8.MAX_BYTES_PER_CHAR;
final int maxLengthVarIntSize = computeRawVarint32Size(maxLength);
// If we are streaming and the potential length is too big to fit in our buffer, we take the
// slower path. Otherwise, we're good to try the fast path.
if (output != null && maxLengthVarIntSize + maxLength > limit - position) {
// Allocate a byte[] that we know can fit the string and encode into it. String.getBytes()
// does the same internally and then does *another copy* to return a byte[] of exactly the
// right size. We can skip that copy and just writeRawBytes up to the actualLength of the
// UTF-8 encoded bytes.
final byte[] encodedBytes = new byte[maxLength];
int actualLength = Utf8.encode(value, encodedBytes, 0, maxLength);
writeRawVarint32(actualLength);
writeRawBytes(encodedBytes, 0, actualLength);
} else {
// Optimize for the case where we know this length results in a constant varint length as this
// saves a pass for measuring the length of the string.
final int minLengthVarIntSize = computeRawVarint32Size(value.length());
int oldPosition = position;
final int length;
try {
if (minLengthVarIntSize == maxLengthVarIntSize) {
position = oldPosition + minLengthVarIntSize;
int newPosition = Utf8.encode(value, buffer, position, limit - position);
// Since this class is stateful and tracks the position, we rewind and store the state,
// prepend the length, then reset it back to the end of the string.
position = oldPosition;
length = newPosition - oldPosition - minLengthVarIntSize;
writeRawVarint32(length);
position = newPosition;
} else {
length = Utf8.encodedLength(value);
writeRawVarint32(length);
position = Utf8.encode(value, buffer, position, limit - position);
}
} catch (UnpairedSurrogateException e) {
// Be extra careful and restore the original position for retrying the write with the less
// efficient path.
position = oldPosition;
throw e;
} catch (ArrayIndexOutOfBoundsException e) {
throw new OutOfSpaceException(e);
}
totalBytesWritten += length;
}
}
/** Write a {@code group} field to the stream. */
public void writeGroupNoTag(final MessageLite value) throws IOException {
value.writeTo(this);
@ -826,9 +906,16 @@ public final class CodedOutputStream {
* {@code string} field.
*/
public static int computeStringSizeNoTag(final String value) {
final byte[] bytes = value.getBytes(Internal.UTF_8);
return computeRawVarint32Size(bytes.length) +
bytes.length;
int length;
try {
length = Utf8.encodedLength(value);
} catch (UnpairedSurrogateException e) {
// TODO(dweis): Consider using nio Charset methods instead.
final byte[] bytes = value.getBytes(Internal.UTF_8);
length = bytes.length;
}
return computeRawVarint32Size(length) + length;
}
/**
@ -1007,9 +1094,15 @@ public final class CodedOutputStream {
public static class OutOfSpaceException extends IOException {
private static final long serialVersionUID = -6947486886997889499L;
private static final String MESSAGE =
"CodedOutputStream was writing to a flat byte array and ran out of space.";
OutOfSpaceException() {
super("CodedOutputStream was writing to a flat byte array and ran " +
"out of space.");
super(MESSAGE);
}
OutOfSpaceException(Throwable cause) {
super(MESSAGE, cause);
}
}

View File

@ -1118,9 +1118,9 @@ public final class Descriptors {
static {
// Refuse to init if someone added a new declared type.
if (Type.values().length != FieldDescriptorProto.Type.values().length) {
throw new RuntimeException(
"descriptor.proto has a new declared type but Desrciptors.java " +
"wasn't updated.");
throw new RuntimeException(""
+ "descriptor.proto has a new declared type but Descriptors.java "
+ "wasn't updated.");
}
}

View File

@ -121,21 +121,43 @@ public abstract class GeneratedMessage extends AbstractMessage
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
for (final FieldDescriptor field : descriptor.getFields()) {
if (field.isRepeated()) {
final List<?> value = (List<?>) getField(field);
if (!value.isEmpty()) {
result.put(field, value);
final List<FieldDescriptor> fields = descriptor.getFields();
for (int i = 0; i < fields.size(); i++) {
FieldDescriptor field = fields.get(i);
final OneofDescriptor oneofDescriptor = field.getContainingOneof();
/*
* If the field is part of a Oneof, then at maximum one field in the Oneof is set
* and it is not repeated. There is no need to iterate through the others.
*/
if (oneofDescriptor != null) {
// Skip other fields in the Oneof we know are not set
i += oneofDescriptor.getFieldCount() - 1;
if (!hasOneof(oneofDescriptor)) {
// If no field is set in the Oneof, skip all the fields in the Oneof
continue;
}
// Get the pointer to the only field which is set in the Oneof
field = getOneofFieldDescriptor(oneofDescriptor);
} else {
if (hasField(field)) {
if (getBytesForString
&& field.getJavaType() == FieldDescriptor.JavaType.STRING) {
result.put(field, getFieldRaw(field));
} else {
result.put(field, getField(field));
// If we are not in a Oneof, we need to check if the field is set and if it is repeated
if (field.isRepeated()) {
final List<?> value = (List<?>) getField(field);
if (!value.isEmpty()) {
result.put(field, value);
}
continue;
}
if (!hasField(field)) {
continue;
}
}
// Add the field to the map
if (getBytesForString && field.getJavaType() == FieldDescriptor.JavaType.STRING) {
result.put(field, getFieldRaw(field));
} else {
result.put(field, getField(field));
}
}
return result;
@ -398,17 +420,40 @@ public abstract class GeneratedMessage extends AbstractMessage
final TreeMap<FieldDescriptor, Object> result =
new TreeMap<FieldDescriptor, Object>();
final Descriptor descriptor = internalGetFieldAccessorTable().descriptor;
for (final FieldDescriptor field : descriptor.getFields()) {
if (field.isRepeated()) {
final List value = (List) getField(field);
if (!value.isEmpty()) {
result.put(field, value);
final List<FieldDescriptor> fields = descriptor.getFields();
for (int i = 0; i < fields.size(); i++) {
FieldDescriptor field = fields.get(i);
final OneofDescriptor oneofDescriptor = field.getContainingOneof();
/*
* If the field is part of a Oneof, then at maximum one field in the Oneof is set
* and it is not repeated. There is no need to iterate through the others.
*/
if (oneofDescriptor != null) {
// Skip other fields in the Oneof we know are not set
i += oneofDescriptor.getFieldCount() - 1;
if (!hasOneof(oneofDescriptor)) {
// If no field is set in the Oneof, skip all the fields in the Oneof
continue;
}
// Get the pointer to the only field which is set in the Oneof
field = getOneofFieldDescriptor(oneofDescriptor);
} else {
if (hasField(field)) {
result.put(field, getField(field));
// If we are not in a Oneof, we need to check if the field is set and if it is repeated
if (field.isRepeated()) {
final List<?> value = (List<?>) getField(field);
if (!value.isEmpty()) {
result.put(field, value);
}
continue;
}
if (!hasField(field)) {
continue;
}
}
// Add the field to the map
result.put(field, getField(field));
}
return result;
}
@ -2696,4 +2741,38 @@ public abstract class GeneratedMessage extends AbstractMessage
return (Extension<MessageType, T>) extension;
}
protected static int computeStringSize(final int fieldNumber, final Object value) {
if (value instanceof String) {
return CodedOutputStream.computeStringSize(fieldNumber, (String) value);
} else {
return CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) value);
}
}
protected static int computeStringSizeNoTag(final Object value) {
if (value instanceof String) {
return CodedOutputStream.computeStringSizeNoTag((String) value);
} else {
return CodedOutputStream.computeBytesSizeNoTag((ByteString) value);
}
}
protected static void writeString(
CodedOutputStream output, final int fieldNumber, final Object value) throws IOException {
if (value instanceof String) {
output.writeString(fieldNumber, (String) value);
} else {
output.writeBytes(fieldNumber, (ByteString) value);
}
}
protected static void writeStringNoTag(
CodedOutputStream output, final Object value) throws IOException {
if (value instanceof String) {
output.writeStringNoTag((String) value);
} else {
output.writeBytesNoTag((ByteString) value);
}
}
}

View File

@ -48,7 +48,6 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Lite version of {@link GeneratedMessage}.
@ -60,24 +59,6 @@ public abstract class GeneratedMessageLite<
BuilderType extends GeneratedMessageLite.Builder<MessageType, BuilderType>>
extends AbstractMessageLite
implements Serializable {
/**
* Holds all the {@link PrototypeHolder}s for loaded classes.
*/
// TODO(dweis): Consider different concurrency values.
// TODO(dweis): This will prevent garbage collection of the class loader.
// Ideally we'd use something like ClassValue but that's Java 7 only.
private static final Map<Class<?>, PrototypeHolder<?, ?>> PROTOTYPE_MAP =
new ConcurrentHashMap<Class<?>, PrototypeHolder<?, ?>>();
// For use by generated code only.
protected static <
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
BuilderType extends GeneratedMessageLite.Builder<
MessageType, BuilderType>> void onLoad(Class<MessageType> clazz,
PrototypeHolder<MessageType, BuilderType> protoTypeHolder) {
PROTOTYPE_MAP.put(clazz, protoTypeHolder);
}
private static final long serialVersionUID = 1L;
@ -90,20 +71,17 @@ public abstract class GeneratedMessageLite<
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final Parser<MessageType> getParserForType() {
return (Parser<MessageType>) PROTOTYPE_MAP
.get(getClass()).getParserForType();
return (Parser<MessageType>) dynamicMethod(MethodToInvoke.GET_PARSER);
}
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final MessageType getDefaultInstanceForType() {
return (MessageType) PROTOTYPE_MAP
.get(getClass()).getDefaultInstanceForType();
return (MessageType) dynamicMethod(MethodToInvoke.GET_DEFAULT_INSTANCE);
}
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public final BuilderType newBuilderForType() {
return (BuilderType) PROTOTYPE_MAP
.get(getClass()).newBuilderForType();
return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER);
}
/**
@ -141,7 +119,9 @@ public abstract class GeneratedMessageLite<
MERGE_FROM,
MAKE_IMMUTABLE,
NEW_INSTANCE,
NEW_BUILDER;
NEW_BUILDER,
GET_DEFAULT_INSTANCE,
GET_PARSER;
}
/**
@ -168,9 +148,21 @@ public abstract class GeneratedMessageLite<
* <p>
* For use by generated code only.
*/
protected abstract Object dynamicMethod(
MethodToInvoke method,
Object... args);
protected abstract Object dynamicMethod(MethodToInvoke method, Object arg0, Object arg1);
/**
* Same as {@link #dynamicMethod(MethodToInvoke, Object, Object)} with {@code null} padding.
*/
protected Object dynamicMethod(MethodToInvoke method, Object arg0) {
return dynamicMethod(method, arg0, null);
}
/**
* Same as {@link #dynamicMethod(MethodToInvoke, Object, Object)} with {@code null} padding.
*/
protected Object dynamicMethod(MethodToInvoke method) {
return dynamicMethod(method, null, null);
}
/**
* Merge some unknown fields into the {@link UnknownFieldSetLite} for this
@ -1059,18 +1051,22 @@ public abstract class GeneratedMessageLite<
@SuppressWarnings("unchecked")
protected Object readResolve() throws ObjectStreamException {
try {
Class messageClass = Class.forName(messageClassName);
Parser<?> parser =
(Parser<?>) messageClass.getField("PARSER").get(null);
return parser.parsePartialFrom(asBytes);
Class<?> messageClass = Class.forName(messageClassName);
java.lang.reflect.Field defaultInstanceField =
messageClass.getDeclaredField("DEFAULT_INSTANCE");
defaultInstanceField.setAccessible(true);
MessageLite defaultInstance = (MessageLite) defaultInstanceField.get(null);
return defaultInstance.newBuilderForType()
.mergeFrom(asBytes)
.buildPartial();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to find proto buffer class", e);
throw new RuntimeException("Unable to find proto buffer class: " + messageClassName, e);
} catch (NoSuchFieldException e) {
throw new RuntimeException("Unable to find PARSER", e);
throw new RuntimeException("Unable to find DEFAULT_INSTANCE in " + messageClassName, e);
} catch (SecurityException e) {
throw new RuntimeException("Unable to call PARSER", e);
throw new RuntimeException("Unable to call DEFAULT_INSTANCE in " + messageClassName, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to call parseFrom method", e);
throw new RuntimeException("Unable to call parsePartialFrom", e);
} catch (InvalidProtocolBufferException e) {
throw new RuntimeException("Unable to understand proto buffer", e);
}
@ -1103,45 +1099,6 @@ public abstract class GeneratedMessageLite<
return (GeneratedExtension<MessageType, T>) extension;
}
/**
* Represents the state needed to implement *ForType methods. Generated code
* must provide a static singleton instance by adding it with
* {@link GeneratedMessageLite#onLoad(Class, PrototypeHolder)} on class load.
* <ul>
* <li>{@link #getDefaultInstanceForType()}
* <li>{@link #getParserForType()}
* <li>{@link #newBuilderForType()}
* </ul>
* This allows us to trade three generated methods for a static Map.
*/
protected static class PrototypeHolder<
MessageType extends GeneratedMessageLite<MessageType, BuilderType>,
BuilderType extends GeneratedMessageLite.Builder<
MessageType, BuilderType>> {
private final MessageType defaultInstance;
private final Parser<MessageType> parser;
public PrototypeHolder(
MessageType defaultInstance, Parser<MessageType> parser) {
this.defaultInstance = defaultInstance;
this.parser = parser;
}
public MessageType getDefaultInstanceForType() {
return defaultInstance;
}
public Parser<MessageType> getParserForType() {
return parser;
}
@SuppressWarnings("unchecked") // Guaranteed by runtime.
public BuilderType newBuilderForType() {
return (BuilderType) defaultInstance.toBuilder();
}
}
/**
* A static helper method for checking if a message is initialized, optionally memoizing.

View File

@ -31,6 +31,7 @@
package com.google.protobuf;
import java.io.IOException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.AbstractList;
@ -358,6 +359,17 @@ public class Internal {
}
}
@SuppressWarnings("unchecked")
public static <T extends MessageLite> T getDefaultInstance(Class<T> clazz) {
try {
Method method = clazz.getMethod("getDefaultInstance");
return (T) method.invoke(method);
} catch (Exception e) {
throw new RuntimeException(
"Failed to get default instance for " + clazz, e);
}
}
/**
* An empty byte array constant used in generated code.
*/

View File

@ -69,7 +69,7 @@ public class InvalidProtocolBufferException extends IOException {
static InvalidProtocolBufferException truncatedMessage() {
return new InvalidProtocolBufferException(
"While parsing a protocol message, the input ended unexpectedly " +
"in the middle of a field. This could mean either than the " +
"in the middle of a field. This could mean either that the " +
"input has been truncated or that an embedded message " +
"misreported its own length.");
}

View File

@ -215,6 +215,11 @@ public class LazyStringArrayList extends AbstractProtobufList<String>
modCount++;
}
@Override
public Object getRaw(int index) {
return list.get(index);
}
// @Override
public ByteString getByteString(int index) {
Object o = list.get(index);

View File

@ -56,7 +56,18 @@ public interface LazyStringList extends ProtocolStringList {
* ({@code index < 0 || index >= size()})
*/
ByteString getByteString(int index);
/**
* Returns the element at the specified position in this list as an Object
* that will either be a String or a ByteString.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index >= size()})
*/
Object getRaw(int index);
/**
* Returns the element at the specified position in this list as byte[].
*

View File

@ -121,6 +121,9 @@ public interface Message extends MessageLite, MessageOrBuilder {
* using the same merging rules.<br>
* * For repeated fields, the elements in {@code other} are concatenated
* with the elements in this message.
* * For oneof groups, if the other message has one of the fields set,
* the group of this message is cleared and replaced by the field
* of the other message, so that the oneof constraint is preserved.
*
* This is equivalent to the {@code Message::MergeFrom} method in C++.
*/

View File

@ -73,7 +73,7 @@ public class RepeatedFieldBuilder
private GeneratedMessage.BuilderParent parent;
// List of messages. Never null. It may be immutable, in which case
// isMessagesListImmutable will be true. See note below.
// isMessagesListMutable will be false. See note below.
private List<MType> messages;
// Whether messages is an mutable array that can be modified.

View File

@ -31,6 +31,7 @@
package com.google.protobuf;
import java.io.IOException;
import java.util.Arrays;
/**
* {@code UnknownFieldSetLite} is used to keep track of fields which were seen
@ -45,8 +46,11 @@ import java.io.IOException;
*/
public final class UnknownFieldSetLite {
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
private static final UnknownFieldSetLite DEFAULT_INSTANCE =
new UnknownFieldSetLite(ByteString.EMPTY);
new UnknownFieldSetLite(0, EMPTY_INT_ARRAY, EMPTY_OBJECT_ARRAY);
/**
* Get an empty {@code UnknownFieldSetLite}.
@ -71,19 +75,41 @@ public final class UnknownFieldSetLite {
* {@code second}.
*/
static UnknownFieldSetLite concat(UnknownFieldSetLite first, UnknownFieldSetLite second) {
return new UnknownFieldSetLite(first.byteString.concat(second.byteString));
int count = first.count + second.count;
int[] tags = Arrays.copyOf(first.tags, count);
System.arraycopy(second.tags, 0, tags, first.count, second.count);
Object[] objects = Arrays.copyOf(first.objects, count);
System.arraycopy(second.objects, 0, objects, first.count, second.count);
return new UnknownFieldSetLite(count, tags, objects);
}
/**
* The number of elements in the set.
*/
private int count;
/**
* The tag numbers for the elements in the set.
*/
private int[] tags;
/**
* The boxed values of the elements in the set.
*/
private Object[] objects;
/**
* The lazily computed serialized size of the set.
*/
private int memoizedSerializedSize = -1;
/**
* The internal representation of the unknown fields.
* Constructs the {@code UnknownFieldSetLite}.
*/
private final ByteString byteString;
/**
* Constructs the {@code UnknownFieldSetLite} as a thin wrapper around {@link ByteString}.
*/
private UnknownFieldSetLite(ByteString byteString) {
this.byteString = byteString;
private UnknownFieldSetLite(int count, int[] tags, Object[] objects) {
this.count = count;
this.tags = tags;
this.objects = objects;
}
/**
@ -92,17 +118,73 @@ public final class UnknownFieldSetLite {
* <p>For use by generated code only.
*/
public void writeTo(CodedOutputStream output) throws IOException {
output.writeRawBytes(byteString);
for (int i = 0; i < count; i++) {
int tag = tags[i];
int fieldNumber = WireFormat.getTagFieldNumber(tag);
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
output.writeUInt64(fieldNumber, (Long) objects[i]);
break;
case WireFormat.WIRETYPE_FIXED32:
output.writeFixed32(fieldNumber, (Integer) objects[i]);
break;
case WireFormat.WIRETYPE_FIXED64:
output.writeFixed64(fieldNumber, (Long) objects[i]);
break;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
output.writeBytes(fieldNumber, (ByteString) objects[i]);
break;
case WireFormat.WIRETYPE_START_GROUP:
output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
((UnknownFieldSetLite) objects[i]).writeTo(output);
output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
break;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
}
/**
* Get the number of bytes required to encode this set.
*
* <p>For use by generated code only.
*/
public int getSerializedSize() {
return byteString.size();
int size = memoizedSerializedSize;
if (size != -1) {
return size;
}
size = 0;
for (int i = 0; i < count; i++) {
int tag = tags[i];
int fieldNumber = WireFormat.getTagFieldNumber(tag);
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
size += CodedOutputStream.computeUInt64Size(fieldNumber, (Long) objects[i]);
break;
case WireFormat.WIRETYPE_FIXED32:
size += CodedOutputStream.computeFixed32Size(fieldNumber, (Integer) objects[i]);
break;
case WireFormat.WIRETYPE_FIXED64:
size += CodedOutputStream.computeFixed64Size(fieldNumber, (Long) objects[i]);
break;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
size += CodedOutputStream.computeBytesSize(fieldNumber, (ByteString) objects[i]);
break;
case WireFormat.WIRETYPE_START_GROUP:
size += CodedOutputStream.computeTagSize(fieldNumber) * 2
+ ((UnknownFieldSetLite) objects[i]).getSerializedSize();
break;
default:
throw new IllegalStateException(InvalidProtocolBufferException.invalidWireType());
}
}
memoizedSerializedSize = size;
return size;
}
@Override
@ -111,16 +193,34 @@ public final class UnknownFieldSetLite {
return true;
}
if (obj instanceof UnknownFieldSetLite) {
return byteString.equals(((UnknownFieldSetLite) obj).byteString);
if (obj == null) {
return false;
}
return false;
if (!(obj instanceof UnknownFieldSetLite)) {
return false;
}
UnknownFieldSetLite other = (UnknownFieldSetLite) obj;
if (count != other.count
// TODO(dweis): Only have to compare up to count but at worst 2x worse than we need to do.
|| !Arrays.equals(tags, other.tags)
|| !Arrays.deepEquals(objects, other.objects)) {
return false;
}
return true;
}
@Override
public int hashCode() {
return byteString.hashCode();
int hashCode = 17;
hashCode = 31 * hashCode + count;
hashCode = 31 * hashCode + Arrays.hashCode(tags);
hashCode = 31 * hashCode + Arrays.deepHashCode(objects);
return hashCode;
}
/**
@ -131,28 +231,49 @@ public final class UnknownFieldSetLite {
* <p>For use by generated code only.
*/
public static final class Builder {
// Arbitrarily chosen.
// TODO(dweis): Tune this number?
private static final int MIN_CAPACITY = 8;
private int count = 0;
private int[] tags = EMPTY_INT_ARRAY;
private Object[] objects = EMPTY_OBJECT_ARRAY;
private ByteString.Output byteStringOutput;
private CodedOutputStream output;
private boolean built;
/**
* Constructs a {@code Builder}. Lazily initialized by
* {@link #ensureInitializedButNotBuilt()}.
* Constructs a {@code Builder}.
*/
private Builder() {}
/**
* Ensures internal state is initialized for use.
*/
private void ensureInitializedButNotBuilt() {
private void ensureNotBuilt() {
if (built) {
throw new IllegalStateException("Do not reuse UnknownFieldSetLite Builders.");
}
if (output == null && byteStringOutput == null) {
byteStringOutput = ByteString.newOutput(100 /* initialCapacity */);
output = CodedOutputStream.newInstance(byteStringOutput);
}
private void storeField(int tag, Object value) {
ensureCapacity();
tags[count] = tag;
objects[count] = value;
count++;
}
/**
* Ensures that our arrays are long enough to store more metadata.
*/
private void ensureCapacity() {
if (count == tags.length) {
int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1;
int newLength = count + increment;
tags = Arrays.copyOf(tags, newLength);
objects = Arrays.copyOf(objects, newLength);
}
}
@ -166,31 +287,28 @@ public final class UnknownFieldSetLite {
*/
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
throws IOException {
ensureInitializedButNotBuilt();
ensureNotBuilt();
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
output.writeUInt64(fieldNumber, input.readInt64());
storeField(tag, input.readInt64());
return true;
case WireFormat.WIRETYPE_FIXED32:
output.writeFixed32(fieldNumber, input.readFixed32());
storeField(tag, input.readFixed32());
return true;
case WireFormat.WIRETYPE_FIXED64:
output.writeFixed64(fieldNumber, input.readFixed64());
storeField(tag, input.readFixed64());
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
output.writeBytes(fieldNumber, input.readBytes());
storeField(tag, input.readBytes());
return true;
case WireFormat.WIRETYPE_START_GROUP:
final Builder subBuilder = newBuilder();
subBuilder.mergeFrom(input);
input.checkLastTagWas(
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
output.writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP);
subBuilder.build().writeTo(output);
output.writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP);
storeField(tag, subBuilder.build());
return true;
case WireFormat.WIRETYPE_END_GROUP:
return false;
@ -210,12 +328,10 @@ public final class UnknownFieldSetLite {
if (fieldNumber == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
ensureInitializedButNotBuilt();
try {
output.writeUInt64(fieldNumber, value);
} catch (IOException e) {
// Should never happen.
}
ensureNotBuilt();
storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_VARINT), (long) value);
return this;
}
@ -229,11 +345,24 @@ public final class UnknownFieldSetLite {
if (fieldNumber == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
ensureInitializedButNotBuilt();
try {
output.writeBytes(fieldNumber, value);
} catch (IOException e) {
// Should never happen.
ensureNotBuilt();
storeField(WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED), value);
return this;
}
/**
* Parse an entire message from {@code input} and merge its fields into
* this set.
*/
private Builder mergeFrom(final CodedInputStream input) throws IOException {
// Ensures initialization in mergeFieldFrom.
while (true) {
final int tag = input.readTag();
if (tag == 0 || !mergeFieldFrom(tag, input)) {
break;
}
}
return this;
}
@ -254,44 +383,12 @@ public final class UnknownFieldSetLite {
}
built = true;
final UnknownFieldSetLite result;
// If we were never initialized, no data was written.
if (output == null) {
result = getDefaultInstance();
} else {
try {
output.flush();
} catch (IOException e) {
// Should never happen.
}
ByteString byteString = byteStringOutput.toByteString();
if (byteString.isEmpty()) {
result = getDefaultInstance();
} else {
result = new UnknownFieldSetLite(byteString);
}
if (count == 0) {
return DEFAULT_INSTANCE;
}
// Allow for garbage collection.
output = null;
byteStringOutput = null;
return result;
}
/**
* Parse an entire message from {@code input} and merge its fields into
* this set.
*/
private Builder mergeFrom(final CodedInputStream input) throws IOException {
// Ensures initialization in mergeFieldFrom.
while (true) {
final int tag = input.readTag();
if (tag == 0 || !mergeFieldFrom(tag, input)) {
break;
}
}
return this;
return new UnknownFieldSetLite(count, tags, objects);
}
}
}

View File

@ -57,6 +57,11 @@ public class UnmodifiableLazyStringList extends AbstractList<String>
public String get(int index) {
return list.get(index);
}
@Override
public Object getRaw(int index) {
return list.getRaw(index);
}
@Override
public int size() {

View File

@ -66,6 +66,12 @@ package com.google.protobuf;
*/
final class Utf8 {
private Utf8() {}
/**
* Maximum number of bytes per Java UTF-16 char in UTF-8.
* @see java.nio.charset.CharsetEncoder#maxBytesPerChar()
*/
static final int MAX_BYTES_PER_CHAR = 3;
/**
* State value indicating that the byte sequence is well-formed and
@ -346,4 +352,130 @@ final class Utf8 {
default: throw new AssertionError();
}
}
// These UTF-8 handling methods are copied from Guava's Utf8 class with a modification to throw
// a protocol buffer local exception. This exception is then caught in CodedOutputStream so it can
// fallback to more lenient behavior.
static class UnpairedSurrogateException extends IllegalArgumentException {
private UnpairedSurrogateException(int index) {
super("Unpaired surrogate at index " + index);
}
}
/**
* Returns the number of bytes in the UTF-8-encoded form of {@code sequence}. For a string,
* this method is equivalent to {@code string.getBytes(UTF_8).length}, but is more efficient in
* both time and space.
*
* @throws IllegalArgumentException if {@code sequence} contains ill-formed UTF-16 (unpaired
* surrogates)
*/
static int encodedLength(CharSequence sequence) {
// Warning to maintainers: this implementation is highly optimized.
int utf16Length = sequence.length();
int utf8Length = utf16Length;
int i = 0;
// This loop optimizes for pure ASCII.
while (i < utf16Length && sequence.charAt(i) < 0x80) {
i++;
}
// This loop optimizes for chars less than 0x800.
for (; i < utf16Length; i++) {
char c = sequence.charAt(i);
if (c < 0x800) {
utf8Length += ((0x7f - c) >>> 31); // branch free!
} else {
utf8Length += encodedLengthGeneral(sequence, i);
break;
}
}
if (utf8Length < utf16Length) {
// Necessary and sufficient condition for overflow because of maximum 3x expansion
throw new IllegalArgumentException("UTF-8 length does not fit in int: "
+ (utf8Length + (1L << 32)));
}
return utf8Length;
}
private static int encodedLengthGeneral(CharSequence sequence, int start) {
int utf16Length = sequence.length();
int utf8Length = 0;
for (int i = start; i < utf16Length; i++) {
char c = sequence.charAt(i);
if (c < 0x800) {
utf8Length += (0x7f - c) >>> 31; // branch free!
} else {
utf8Length += 2;
// jdk7+: if (Character.isSurrogate(c)) {
if (Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE) {
// Check that we have a well-formed surrogate pair.
int cp = Character.codePointAt(sequence, i);
if (cp < Character.MIN_SUPPLEMENTARY_CODE_POINT) {
throw new UnpairedSurrogateException(i);
}
i++;
}
}
}
return utf8Length;
}
static int encode(CharSequence sequence, byte[] bytes, int offset, int length) {
int utf16Length = sequence.length();
int j = offset;
int i = 0;
int limit = offset + length;
// Designed to take advantage of
// https://wikis.oracle.com/display/HotSpotInternals/RangeCheckElimination
for (char c; i < utf16Length && i + j < limit && (c = sequence.charAt(i)) < 0x80; i++) {
bytes[j + i] = (byte) c;
}
if (i == utf16Length) {
return j + utf16Length;
}
j += i;
for (char c; i < utf16Length; i++) {
c = sequence.charAt(i);
if (c < 0x80 && j < limit) {
bytes[j++] = (byte) c;
} else if (c < 0x800 && j <= limit - 2) { // 11 bits, two UTF-8 bytes
bytes[j++] = (byte) ((0xF << 6) | (c >>> 6));
bytes[j++] = (byte) (0x80 | (0x3F & c));
} else if ((c < Character.MIN_SURROGATE || Character.MAX_SURROGATE < c) && j <= limit - 3) {
// Maximum single-char code point is 0xFFFF, 16 bits, three UTF-8 bytes
bytes[j++] = (byte) ((0xF << 5) | (c >>> 12));
bytes[j++] = (byte) (0x80 | (0x3F & (c >>> 6)));
bytes[j++] = (byte) (0x80 | (0x3F & c));
} else if (j <= limit - 4) {
// Minimum code point represented by a surrogate pair is 0x10000, 17 bits, four UTF-8 bytes
final char low;
if (i + 1 == sequence.length()
|| !Character.isSurrogatePair(c, (low = sequence.charAt(++i)))) {
throw new UnpairedSurrogateException((i - 1));
}
int codePoint = Character.toCodePoint(c, low);
bytes[j++] = (byte) ((0xF << 4) | (codePoint >>> 18));
bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 12)));
bytes[j++] = (byte) (0x80 | (0x3F & (codePoint >>> 6)));
bytes[j++] = (byte) (0x80 | (0x3F & codePoint));
} else {
// If we are surrogates and we're not a surrogate pair, always throw an
// IllegalArgumentException instead of an ArrayOutOfBoundsException.
if ((Character.MIN_SURROGATE <= c && c <= Character.MAX_SURROGATE)
&& (i + 1 == sequence.length()
|| !Character.isSurrogatePair(c, sequence.charAt(i + 1)))) {
throw new UnpairedSurrogateException(i);
}
throw new ArrayIndexOutOfBoundsException("Failed writing " + c + " at index " + j);
}
}
return j;
}
// End Guava UTF-8 methods.
}

View File

@ -58,7 +58,7 @@ public final class WireFormat {
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
/** Given a tag value, determines the wire type (the lower 3 bits). */
static int getTagWireType(final int tag) {
public static int getTagWireType(final int tag) {
return tag & TAG_TYPE_MASK;
}

View File

@ -85,6 +85,7 @@ public class BoundedByteStringTest extends LiteralByteStringTest {
testString.substring(2, testString.length() - 6), roundTripString);
}
@Override
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);

View File

@ -58,8 +58,7 @@ public class CheckUtf8Test extends TestCase {
public void testParseRequiredStringWithGoodUtf8() throws Exception {
ByteString serialized =
BytesWrapper.newBuilder().setReq(UTF8_BYTE_STRING).build().toByteString();
assertEquals(UTF8_BYTE_STRING_TEXT,
StringWrapper.PARSER.parseFrom(serialized).getReq());
assertEquals(UTF8_BYTE_STRING_TEXT, StringWrapper.parser().parseFrom(serialized).getReq());
}
public void testBuildRequiredStringWithBadUtf8() throws Exception {
@ -93,7 +92,7 @@ public class CheckUtf8Test extends TestCase {
ByteString serialized =
BytesWrapper.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
StringWrapper.PARSER.parseFrom(serialized);
StringWrapper.parser().parseFrom(serialized);
fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());
@ -131,7 +130,7 @@ public class CheckUtf8Test extends TestCase {
ByteString serialized =
BytesWrapperSize.newBuilder().setReq(NON_UTF8_BYTE_STRING).build().toByteString();
try {
StringWrapperSize.PARSER.parseFrom(serialized);
StringWrapperSize.parser().parseFrom(serialized);
fail("Expected InvalidProtocolBufferException for non UTF-8 byte string.");
} catch (InvalidProtocolBufferException exception) {
assertEquals("Protocol message had invalid UTF-8.", exception.getMessage());

View File

@ -40,6 +40,7 @@ import junit.framework.TestCase;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
@ -325,10 +326,41 @@ public class CodedOutputStreamTest extends TestCase {
for (int i = 0; i < 1024; ++i) {
codedStream.writeRawBytes(value, 0, value.length);
}
String string =
"abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
// Ensure we take the slower fast path.
assertTrue(CodedOutputStream.computeRawVarint32Size(string.length())
!= CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR));
codedStream.writeStringNoTag(string);
int stringSize = CodedOutputStream.computeStringSizeNoTag(string);
// Make sure we have written more bytes than the buffer could hold. This is
// to make the test complete.
assertTrue(codedStream.getTotalBytesWritten() > BUFFER_SIZE);
assertEquals(value.length * 1024, codedStream.getTotalBytesWritten());
// Verify that the total bytes written is correct
assertEquals((value.length * 1024) + stringSize, codedStream.getTotalBytesWritten());
}
// TODO(dweis): Write a comprehensive test suite for CodedOutputStream that covers more than just
// this case.
public void testWriteStringNoTag_fastpath() throws Exception {
int bufferSize = 153;
String threeBytesPer = "\u0981";
String string = threeBytesPer;
for (int i = 0; i < 50; i++) {
string += threeBytesPer;
}
// These checks ensure we will tickle the slower fast path.
assertEquals(1, CodedOutputStream.computeRawVarint32Size(string.length()));
assertEquals(
2, CodedOutputStream.computeRawVarint32Size(string.length() * Utf8.MAX_BYTES_PER_CHAR));
assertEquals(bufferSize, string.length() * Utf8.MAX_BYTES_PER_CHAR);
CodedOutputStream output =
CodedOutputStream.newInstance(ByteBuffer.allocate(bufferSize), bufferSize);
output.writeStringNoTag(string);
}
public void testWriteToByteBuffer() throws Exception {
@ -398,4 +430,80 @@ public class CodedOutputStreamTest extends TestCase {
assertEqualBytes(bytes(0x02, 0x33, 0x44, 0x00), destination);
assertEquals(3, codedStream.getTotalBytesWritten());
}
public void testSerializeInvalidUtf8() throws Exception {
String[] invalidStrings = new String[] {
newString(Character.MIN_HIGH_SURROGATE),
"foobar" + newString(Character.MIN_HIGH_SURROGATE),
newString(Character.MIN_LOW_SURROGATE),
"foobar" + newString(Character.MIN_LOW_SURROGATE),
newString(Character.MIN_HIGH_SURROGATE, Character.MIN_HIGH_SURROGATE)
};
CodedOutputStream outputWithStream = CodedOutputStream.newInstance(new ByteArrayOutputStream());
CodedOutputStream outputWithArray = CodedOutputStream.newInstance(new byte[10000]);
for (String s : invalidStrings) {
// TODO(dweis): These should all fail; instead they are corrupting data.
CodedOutputStream.computeStringSizeNoTag(s);
outputWithStream.writeStringNoTag(s);
outputWithArray.writeStringNoTag(s);
}
}
private static String newString(char... chars) {
return new String(chars);
}
/** Regression test for https://github.com/google/protobuf/issues/292 */
public void testCorrectExceptionThrowWhenEncodingStringsWithoutEnoughSpace() throws Exception {
String testCase = "Foooooooo";
assertEquals(CodedOutputStream.computeRawVarint32Size(testCase.length()),
CodedOutputStream.computeRawVarint32Size(testCase.length() * 3));
assertEquals(11, CodedOutputStream.computeStringSize(1, testCase));
// Tag is one byte, varint describing string length is 1 byte, string length is 9 bytes.
// An array of size 1 will cause a failure when trying to write the varint.
for (int i = 0; i < 11; i++) {
CodedOutputStream output = CodedOutputStream.newInstance(new byte[i]);
try {
output.writeString(1, testCase);
fail("Should have thrown an out of space exception");
} catch (CodedOutputStream.OutOfSpaceException expected) {}
}
}
public void testDifferentStringLengths() throws Exception {
// Test string serialization roundtrip using strings of the following lengths,
// with ASCII and Unicode characters requiring different UTF-8 byte counts per
// char, hence causing the length delimiter varint to sometimes require more
// bytes for the Unicode strings than the ASCII string of the same length.
int[] lengths = new int[] {
0,
1,
(1 << 4) - 1, // 1 byte for ASCII and Unicode
(1 << 7) - 1, // 1 byte for ASCII, 2 bytes for Unicode
(1 << 11) - 1, // 2 bytes for ASCII and Unicode
(1 << 14) - 1, // 2 bytes for ASCII, 3 bytes for Unicode
(1 << 17) - 1, // 3 bytes for ASCII and Unicode
};
for (int i : lengths) {
testEncodingOfString('q', i); // 1 byte per char
testEncodingOfString('\u07FF', i); // 2 bytes per char
testEncodingOfString('\u0981', i); // 3 bytes per char
}
}
private void testEncodingOfString(char c, int length) throws Exception {
String fullString = fullString(c, length);
TestAllTypes testAllTypes = TestAllTypes.newBuilder()
.setOptionalString(fullString)
.build();
assertEquals(
fullString, TestAllTypes.parseFrom(testAllTypes.toByteArray()).getOptionalString());
}
private String fullString(char c, int length) {
char[] result = new char[length];
Arrays.fill(result, c);
return new String(result);
}
}

View File

@ -142,6 +142,16 @@ public class FieldPresenceTest extends TestCase {
"OneofNestedMessage"));
}
public void testOneofEquals() throws Exception {
TestAllTypes.Builder builder = TestAllTypes.newBuilder();
TestAllTypes message1 = builder.build();
// Set message2's oneof_uint32 field to defalut value. The two
// messages should be different when check with oneof case.
builder.setOneofUint32(0);
TestAllTypes message2 = builder.build();
assertFalse(message1.equals(message2));
}
public void testFieldPresence() {
// Optional non-message fields set to their default value are treated the
// same way as not set.

View File

@ -187,8 +187,7 @@ public class GeneratedMessageTest extends TestCase {
}
public void testParsedMessagesAreImmutable() throws Exception {
TestAllTypes value = TestAllTypes.PARSER.parseFrom(
TestUtil.getAllSet().toByteString());
TestAllTypes value = TestAllTypes.parser().parseFrom(TestUtil.getAllSet().toByteString());
assertIsUnmodifiable(value.getRepeatedInt32List());
assertIsUnmodifiable(value.getRepeatedInt64List());
assertIsUnmodifiable(value.getRepeatedUint32List());

View File

@ -89,7 +89,7 @@ public class LazyStringEndToEndTest extends TestCase {
TEST_ALL_TYPES_SERIALIZED_WITH_ILLEGAL_UTF8,
ByteString.copyFrom(sink));
}
public void testCaching() {
String a = "a";
String b = "b";
@ -106,24 +106,13 @@ public class LazyStringEndToEndTest extends TestCase {
assertSame(c, proto.getRepeatedString(1));
// There's no way to directly observe that the ByteString is cached
// correctly on serialization, but we can observe that it had to recompute
// the string after serialization.
// Ensure serialization keeps strings cached.
proto.toByteString();
String aPrime = proto.getOptionalString();
assertNotSame(a, aPrime);
assertEquals(a, aPrime);
String bPrime = proto.getRepeatedString(0);
assertNotSame(b, bPrime);
assertEquals(b, bPrime);
String cPrime = proto.getRepeatedString(1);
assertNotSame(c, cPrime);
assertEquals(c, cPrime);
// And now the string should stay cached.
assertSame(aPrime, proto.getOptionalString());
assertSame(bPrime, proto.getRepeatedString(0));
assertSame(cPrime, proto.getRepeatedString(1));
assertSame(a, proto.getOptionalString());
assertSame(b, proto.getRepeatedString(0));
assertSame(c, proto.getRepeatedString(1));
}
public void testNoStringCachingIfOnlyBytesAccessed() throws Exception {

View File

@ -42,6 +42,7 @@ import com.google.protobuf.UnittestLite.TestAllTypesLite.NestedMessage;
import com.google.protobuf.UnittestLite.TestAllTypesLite.OneofFieldCase;
import com.google.protobuf.UnittestLite.TestAllTypesLite.OptionalGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup;
import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder;
import com.google.protobuf.UnittestLite.TestNestedExtensionLite;
import junit.framework.TestCase;
@ -1400,6 +1401,8 @@ public class LiteTest extends TestCase {
assertEquals("hi", messageAfterBuild.getOneofString());
assertEquals(OneofFieldCase.ONEOF_UINT32, builder.getOneofFieldCase());
assertEquals(1, builder.getOneofUint32());
TestAllTypesLiteOrBuilder messageOrBuilder = builder;
assertEquals(OneofFieldCase.ONEOF_UINT32, messageOrBuilder.getOneofFieldCase());
TestAllExtensionsLite.Builder extendableMessageBuilder =
TestAllExtensionsLite.newBuilder();

View File

@ -34,6 +34,7 @@ import junit.framework.TestCase;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@ -209,6 +210,62 @@ public class LiteralByteStringTest extends TestCase {
Arrays.equals(referenceBytes, myBuffer.array()));
}
public void testMarkSupported() {
InputStream stream = stringUnderTest.newInput();
assertTrue(classUnderTest + ".newInput() must support marking", stream.markSupported());
}
public void testMarkAndReset() throws IOException {
int fraction = stringUnderTest.size() / 3;
InputStream stream = stringUnderTest.newInput();
stream.mark(stringUnderTest.size()); // First, mark() the end.
skipFully(stream, fraction); // Skip a large fraction, but not all.
int available = stream.available();
assertTrue(
classUnderTest + ": after skipping to the 'middle', half the bytes are available",
(stringUnderTest.size() - fraction) == available);
stream.reset();
skipFully(stream, stringUnderTest.size()); // Skip to the end.
available = stream.available();
assertTrue(
classUnderTest + ": after skipping to the end, no more bytes are available",
0 == available);
}
/**
* Discards {@code n} bytes of data from the input stream. This method
* will block until the full amount has been skipped. Does not close the
* stream.
* <p>Copied from com.google.common.io.ByteStreams to avoid adding dependency.
*
* @param in the input stream to read from
* @param n the number of bytes to skip
* @throws EOFException if this stream reaches the end before skipping all
* the bytes
* @throws IOException if an I/O error occurs, or the stream does not
* support skipping
*/
static void skipFully(InputStream in, long n) throws IOException {
long toSkip = n;
while (n > 0) {
long amt = in.skip(n);
if (amt == 0) {
// Force a blocking read to avoid infinite loop
if (in.read() == -1) {
long skipped = toSkip - n;
throw new EOFException("reached end of stream after skipping "
+ skipped + " bytes; " + toSkip + " bytes expected");
}
n--;
} else {
n -= amt;
}
}
}
public void testAsReadOnlyByteBuffer() {
ByteBuffer byteBuffer = stringUnderTest.asReadOnlyByteBuffer();
byte[] roundTripBytes = new byte[referenceBytes.length];
@ -305,13 +362,13 @@ public class LiteralByteStringTest extends TestCase {
assertEquals(classUnderTest + " unicode must match", testString, roundTripString);
}
public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException{
public void testToString_returnsCanonicalEmptyString() {
assertSame(classUnderTest + " must be the same string references",
ByteString.EMPTY.toString(Internal.UTF_8),
new LiteralByteString(new byte[]{}).toString(Internal.UTF_8));
}
public void testToString_raisesException() throws UnsupportedEncodingException{
public void testToString_raisesException() {
try {
ByteString.EMPTY.toString("invalid");
fail("Should have thrown an exception.");

View File

@ -74,6 +74,16 @@ public class MapForProto2LiteTest extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
.putAllInt32ToStringField(source.getInt32ToStringField())
.putAllInt32ToBytesField(source.getInt32ToBytesField())
.putAllInt32ToEnumField(source.getInt32ToEnumField())
.putAllInt32ToMessageField(source.getInt32ToMessageField())
.putAllStringToInt32Field(source.getStringToInt32Field());
}
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@ -330,26 +340,36 @@ public class MapForProto2LiteTest extends TestCase {
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build();
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
assertMapValuesSet(destination.build());
}
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}

View File

@ -78,6 +78,16 @@ public class MapForProto2Test extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
.putAllInt32ToStringField(source.getInt32ToStringField())
.putAllInt32ToBytesField(source.getInt32ToBytesField())
.putAllInt32ToEnumField(source.getInt32ToEnumField())
.putAllInt32ToMessageField(source.getInt32ToMessageField())
.putAllStringToInt32Field(source.getStringToInt32Field());
}
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@ -310,26 +320,36 @@ public class MapForProto2Test extends TestCase {
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build();
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
assertMapValuesSet(destination.build());
}
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}

View File

@ -79,6 +79,16 @@ public class MapTest extends TestCase {
builder.getMutableStringToInt32Field().put("3", 33);
}
private void copyMapValues(TestMap source, TestMap.Builder destination) {
destination
.putAllInt32ToInt32Field(source.getInt32ToInt32Field())
.putAllInt32ToStringField(source.getInt32ToStringField())
.putAllInt32ToBytesField(source.getInt32ToBytesField())
.putAllInt32ToEnumField(source.getInt32ToEnumField())
.putAllInt32ToMessageField(source.getInt32ToMessageField())
.putAllStringToInt32Field(source.getStringToInt32Field());
}
private void assertMapValuesSet(TestMap message) {
assertEquals(3, message.getInt32ToInt32Field().size());
assertEquals(11, message.getInt32ToInt32Field().get(1).intValue());
@ -311,26 +321,52 @@ public class MapTest extends TestCase {
assertMapValuesCleared(message);
}
public void testPutAll() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
setMapValues(sourceBuilder);
TestMap source = sourceBuilder.build();
TestMap.Builder destination = TestMap.newBuilder();
copyMapValues(source, destination);
assertMapValuesSet(destination.build());
}
public void testPutAllForUnknownEnumValues() throws Exception {
TestMap.Builder sourceBuilder = TestMap.newBuilder();
sourceBuilder.getMutableInt32ToEnumFieldValue().put(0, 0);
sourceBuilder.getMutableInt32ToEnumFieldValue().put(1, 1);
sourceBuilder.getMutableInt32ToEnumFieldValue().put(2, 1000); // unknown value.
TestMap source = sourceBuilder.build();
TestMap.Builder destinationBuilder = TestMap.newBuilder();
destinationBuilder.putAllInt32ToEnumFieldValue(source.getInt32ToEnumFieldValue());
TestMap destination = destinationBuilder.build();
assertEquals(0, destination.getInt32ToEnumFieldValue().get(0).intValue());
assertEquals(1, destination.getInt32ToEnumFieldValue().get(1).intValue());
assertEquals(1000, destination.getInt32ToEnumFieldValue().get(2).intValue());
}
public void testSerializeAndParse() throws Exception {
TestMap.Builder builder = TestMap.newBuilder();
setMapValues(builder);
TestMap message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesSet(message);
builder = message.toBuilder();
updateMapValues(builder);
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesUpdated(message);
builder = message.toBuilder();
builder.clear();
message = builder.build();
assertEquals(message.getSerializedSize(), message.toByteString().size());
message = TestMap.PARSER.parseFrom(message.toByteString());
message = TestMap.parser().parseFrom(message.toByteString());
assertMapValuesCleared(message);
}

View File

@ -58,8 +58,7 @@ import java.io.InputStream;
public class ParserTest extends TestCase {
public void testGeneratedMessageParserSingleton() throws Exception {
for (int i = 0; i < 10; i++) {
assertEquals(TestAllTypes.PARSER,
TestUtil.getAllSet().getParserForType());
assertEquals(TestAllTypes.parser(), TestUtil.getAllSet().getParserForType());
}
}
@ -125,8 +124,7 @@ public class ParserTest extends TestCase {
public void testParsePartial() throws Exception {
assertParsePartial(TestRequired.PARSER,
TestRequired.newBuilder().setA(1).buildPartial());
assertParsePartial(TestRequired.parser(), TestRequired.newBuilder().setA(1).buildPartial());
}
private <T extends MessageLite> void assertParsePartial(
@ -216,8 +214,8 @@ public class ParserTest extends TestCase {
public void testParseUnknownFields() throws Exception {
// All fields will be treated as unknown fields in emptyMessage.
TestEmptyMessage emptyMessage = TestEmptyMessage.PARSER.parseFrom(
TestUtil.getAllSet().toByteString());
TestEmptyMessage emptyMessage =
TestEmptyMessage.parser().parseFrom(TestUtil.getAllSet().toByteString());
assertEquals(
TestUtil.getAllSet().toByteString(),
emptyMessage.toByteString());
@ -298,8 +296,7 @@ public class ParserTest extends TestCase {
// Parse TestParsingMerge.
ExtensionRegistry registry = ExtensionRegistry.newInstance();
UnittestProto.registerAllExtensions(registry);
TestParsingMerge parsingMerge =
TestParsingMerge.PARSER.parseFrom(data, registry);
TestParsingMerge parsingMerge = TestParsingMerge.parser().parseFrom(data, registry);
// Required and optional fields should be merged.
assertMessageMerged(parsingMerge.getRequiredAllTypes());
@ -361,8 +358,7 @@ public class ParserTest extends TestCase {
// Parse TestParsingMergeLite.
ExtensionRegistry registry = ExtensionRegistry.newInstance();
UnittestLite.registerAllExtensions(registry);
TestParsingMergeLite parsingMerge =
TestParsingMergeLite.PARSER.parseFrom(data, registry);
TestParsingMergeLite parsingMerge = TestParsingMergeLite.parser().parseFrom(data, registry);
// Required and optional fields should be merged.
assertMessageMerged(parsingMerge.getRequiredAllTypes());

View File

@ -119,7 +119,7 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
@Override
public void testCharsetToString() throws UnsupportedEncodingException {
public void testCharsetToString() {
String sourceString = "I love unicode \u1234\u5678 characters";
ByteString sourceByteString = ByteString.copyFromUtf8(sourceString);
int copies = 250;
@ -145,14 +145,15 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
@Override
public void testToString_returnsCanonicalEmptyString() throws UnsupportedEncodingException {
public void testToString_returnsCanonicalEmptyString() {
RopeByteString ropeByteString =
RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
assertSame(classUnderTest + " must be the same string references",
ByteString.EMPTY.toString(Internal.UTF_8), ropeByteString.toString(Internal.UTF_8));
}
public void testToString_raisesException() throws UnsupportedEncodingException{
@Override
public void testToString_raisesException() {
try {
ByteString byteString =
RopeByteString.newInstanceForTest(ByteString.EMPTY, ByteString.EMPTY);
@ -172,6 +173,7 @@ public class RopeByteStringTest extends LiteralByteStringTest {
}
}
@Override
public void testJavaSerialization() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(out);

View File

@ -732,6 +732,7 @@ public final class TestUtil {
Assert.assertEquals("424", message.getDefaultStringPiece());
Assert.assertEquals("425", message.getDefaultCord());
Assert.assertEquals(TestAllTypes.OneofFieldCase.ONEOF_BYTES, message.getOneofFieldCase());
Assert.assertFalse(message.hasOneofUint32());
Assert.assertFalse(message.hasOneofNestedMessage());
Assert.assertFalse(message.hasOneofString());

View File

@ -32,7 +32,6 @@ package com.google.protobuf;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.TextFormat.Parser.SingularOverwritePolicy;
import protobuf_unittest.UnittestMset.TestMessageSet;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import protobuf_unittest.UnittestProto.OneString;
@ -41,6 +40,7 @@ import protobuf_unittest.UnittestProto.TestAllTypes;
import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage;
import protobuf_unittest.UnittestProto.TestEmptyMessage;
import protobuf_unittest.UnittestProto.TestOneof2;
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
import junit.framework.TestCase;

View File

@ -461,7 +461,7 @@ public class UnknownFieldSetTest extends TestCase {
TestAllExtensions allExtensions = TestUtil.getAllExtensionsSet();
ByteString allExtensionsData = allExtensions.toByteString();
UnittestLite.TestEmptyMessageLite emptyMessageLite =
UnittestLite.TestEmptyMessageLite.PARSER.parseFrom(allExtensionsData);
UnittestLite.TestEmptyMessageLite.parser().parseFrom(allExtensionsData);
ByteString data = emptyMessageLite.toByteString();
TestAllExtensions message =
TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry());

View File

@ -44,10 +44,10 @@ import protobuf_unittest.UnittestProto.TestOneof2;
import protobuf_unittest.UnittestProto.TestOneofBackwardsCompatible;
import protobuf_unittest.UnittestProto.TestPackedExtensions;
import protobuf_unittest.UnittestProto.TestPackedTypes;
import protobuf_unittest.UnittestMset.TestMessageSet;
import protobuf_unittest.UnittestMset.RawMessageSet;
import protobuf_unittest.UnittestMset.TestMessageSetExtension1;
import protobuf_unittest.UnittestMset.TestMessageSetExtension2;
import proto2_wireformat_unittest.UnittestMsetWireFormat.TestMessageSet;
import com.google.protobuf.UnittestLite.TestAllExtensionsLite;
import com.google.protobuf.UnittestLite.TestPackedExtensionsLite;

View File

@ -41,6 +41,7 @@ are:
__author__ = 'petar@google.com (Petar Petrov)'
import collections
import sys
if sys.version_info[0] < 3:
@ -63,7 +64,6 @@ if sys.version_info[0] < 3:
# Note: deriving from object is critical. It is the only thing that makes
# this a true type, allowing us to derive from it in C++ cleanly and making
# __slots__ properly disallow arbitrary element assignment.
from collections import Mapping as _Mapping
class Mapping(object):
__slots__ = ()
@ -106,7 +106,7 @@ if sys.version_info[0] < 3:
__hash__ = None
def __eq__(self, other):
if not isinstance(other, _Mapping):
if not isinstance(other, collections.Mapping):
return NotImplemented
return dict(self.items()) == dict(other.items())
@ -173,12 +173,13 @@ if sys.version_info[0] < 3:
self[key] = default
return default
_Mapping.register(Mapping)
collections.Mapping.register(Mapping)
collections.MutableMapping.register(MutableMapping)
else:
# In Python 3 we can just use MutableMapping directly, because it defines
# __slots__.
from collections import MutableMapping
MutableMapping = collections.MutableMapping
class BaseContainer(object):
@ -336,6 +337,8 @@ class RepeatedScalarFieldContainer(BaseContainer):
# We are presumably comparing against some other sequence type.
return other == self._values
collections.MutableSequence.register(BaseContainer)
class RepeatedCompositeFieldContainer(BaseContainer):

View File

@ -47,6 +47,7 @@ from google.protobuf import unittest_custom_options_pb2
from google.protobuf import unittest_import_pb2
from google.protobuf import unittest_import_public_pb2
from google.protobuf import unittest_mset_pb2
from google.protobuf import unittest_mset_wire_format_pb2
from google.protobuf import unittest_no_generic_services_pb2
from google.protobuf import unittest_pb2
from google.protobuf import service
@ -142,7 +143,7 @@ class GeneratorTest(unittest.TestCase):
self.assertTrue(not non_extension_descriptor.is_extension)
def testOptions(self):
proto = unittest_mset_pb2.TestMessageSet()
proto = unittest_mset_wire_format_pb2.TestMessageSet()
self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format)
def testMessageWithCustomOptions(self):

View File

@ -43,6 +43,7 @@ abstract interface.
__author__ = 'gps@google.com (Gregory P. Smith)'
import collections
import copy
import math
import operator
@ -56,6 +57,7 @@ from google.protobuf import map_unittest_pb2
from google.protobuf import unittest_pb2
from google.protobuf import unittest_proto3_arena_pb2
from google.protobuf.internal import api_implementation
from google.protobuf.internal import packed_field_test_pb2
from google.protobuf.internal import test_util
from google.protobuf import message
@ -421,6 +423,31 @@ class MessageTest(unittest.TestCase):
self.assertEqual(message.repeated_nested_message[4].bb, 5)
self.assertEqual(message.repeated_nested_message[5].bb, 6)
def testSortingRepeatedCompositeFieldsStable(self, message_module):
"""Check passing a custom comparator to sort a repeated composite field."""
message = message_module.TestAllTypes()
message.repeated_nested_message.add().bb = 21
message.repeated_nested_message.add().bb = 20
message.repeated_nested_message.add().bb = 13
message.repeated_nested_message.add().bb = 33
message.repeated_nested_message.add().bb = 11
message.repeated_nested_message.add().bb = 24
message.repeated_nested_message.add().bb = 10
message.repeated_nested_message.sort(key=lambda z: z.bb // 10)
self.assertEquals(
[13, 11, 10, 21, 20, 24, 33],
[n.bb for n in message.repeated_nested_message])
# Make sure that for the C++ implementation, the underlying fields
# are actually reordered.
pb = message.SerializeToString()
message.Clear()
message.MergeFromString(pb)
self.assertEquals(
[13, 11, 10, 21, 20, 24, 33],
[n.bb for n in message.repeated_nested_message])
def testRepeatedCompositeFieldSortArguments(self, message_module):
"""Check sorting a repeated composite field using list.sort() arguments."""
message = message_module.TestAllTypes()
@ -514,6 +541,12 @@ class MessageTest(unittest.TestCase):
# TODO(anuraag): Implement extensiondict comparison in C++ and then add test
def testRepeatedFieldsAreSequences(self, message_module):
m = message_module.TestAllTypes()
self.assertIsInstance(m.repeated_int32, collections.MutableSequence)
self.assertIsInstance(m.repeated_nested_message,
collections.MutableSequence)
def ensureNestedMessageExists(self, msg, attribute):
"""Make sure that a nested message object exists.
@ -556,6 +589,18 @@ class MessageTest(unittest.TestCase):
self.assertFalse(m.HasField('oneof_uint32'))
self.assertTrue(m.HasField('oneof_string'))
# Read nested message accessor without accessing submessage.
m.oneof_nested_message
self.assertEqual('oneof_string', m.WhichOneof('oneof_field'))
self.assertTrue(m.HasField('oneof_string'))
self.assertFalse(m.HasField('oneof_nested_message'))
# Read accessor of nested message without accessing submessage.
m.oneof_nested_message.bb
self.assertEqual('oneof_string', m.WhichOneof('oneof_field'))
self.assertTrue(m.HasField('oneof_string'))
self.assertFalse(m.HasField('oneof_nested_message'))
m.oneof_nested_message.bb = 11
self.assertEqual('oneof_nested_message', m.WhichOneof('oneof_field'))
self.assertFalse(m.HasField('oneof_string'))
@ -1583,6 +1628,21 @@ class Proto3Test(unittest.TestCase):
del msg.map_int32_int32[4]
self.assertEqual(0, len(msg.map_int32_int32))
def testMapsAreMapping(self):
msg = map_unittest_pb2.TestMap()
self.assertIsInstance(msg.map_int32_int32, collections.Mapping)
self.assertIsInstance(msg.map_int32_int32, collections.MutableMapping)
self.assertIsInstance(msg.map_int32_foreign_message, collections.Mapping)
self.assertIsInstance(msg.map_int32_foreign_message,
collections.MutableMapping)
def testMapFindInitializationErrorsSmokeTest(self):
msg = map_unittest_pb2.TestMap()
msg.map_string_string['abc'] = '123'
msg.map_int32_int32[35] = 64
msg.map_string_foreign_message['foo'].c = 5
self.assertEqual(0, len(msg.FindInitializationErrors()))
class ValidTypeNamesTest(unittest.TestCase):
@ -1606,6 +1666,61 @@ class ValidTypeNamesTest(unittest.TestCase):
self.assertImportFromName(pb.repeated_int32, 'Scalar')
self.assertImportFromName(pb.repeated_nested_message, 'Composite')
class PackedFieldTest(unittest.TestCase):
def setMessage(self, message):
message.repeated_int32.append(1)
message.repeated_int64.append(1)
message.repeated_uint32.append(1)
message.repeated_uint64.append(1)
message.repeated_sint32.append(1)
message.repeated_sint64.append(1)
message.repeated_fixed32.append(1)
message.repeated_fixed64.append(1)
message.repeated_sfixed32.append(1)
message.repeated_sfixed64.append(1)
message.repeated_float.append(1.0)
message.repeated_double.append(1.0)
message.repeated_bool.append(True)
message.repeated_nested_enum.append(1)
def testPackedFields(self):
message = packed_field_test_pb2.TestPackedTypes()
self.setMessage(message)
golden_data = (b'\x0A\x01\x01'
b'\x12\x01\x01'
b'\x1A\x01\x01'
b'\x22\x01\x01'
b'\x2A\x01\x02'
b'\x32\x01\x02'
b'\x3A\x04\x01\x00\x00\x00'
b'\x42\x08\x01\x00\x00\x00\x00\x00\x00\x00'
b'\x4A\x04\x01\x00\x00\x00'
b'\x52\x08\x01\x00\x00\x00\x00\x00\x00\x00'
b'\x5A\x04\x00\x00\x80\x3f'
b'\x62\x08\x00\x00\x00\x00\x00\x00\xf0\x3f'
b'\x6A\x01\x01'
b'\x72\x01\x01')
self.assertEqual(golden_data, message.SerializeToString())
def testUnpackedFields(self):
message = packed_field_test_pb2.TestUnpackedTypes()
self.setMessage(message)
golden_data = (b'\x08\x01'
b'\x10\x01'
b'\x18\x01'
b'\x20\x01'
b'\x28\x02'
b'\x30\x02'
b'\x3D\x01\x00\x00\x00'
b'\x41\x01\x00\x00\x00\x00\x00\x00\x00'
b'\x4D\x01\x00\x00\x00'
b'\x51\x01\x00\x00\x00\x00\x00\x00\x00'
b'\x5D\x00\x00\x80\x3f'
b'\x61\x00\x00\x00\x00\x00\x00\xf0\x3f'
b'\x68\x01'
b'\x70\x01')
self.assertEqual(golden_data, message.SerializeToString())
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,73 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
syntax = "proto3";
package google.protobuf.python.internal;
message TestPackedTypes {
enum NestedEnum {
FOO = 0;
BAR = 1;
BAZ = 2;
}
repeated int32 repeated_int32 = 1;
repeated int64 repeated_int64 = 2;
repeated uint32 repeated_uint32 = 3;
repeated uint64 repeated_uint64 = 4;
repeated sint32 repeated_sint32 = 5;
repeated sint64 repeated_sint64 = 6;
repeated fixed32 repeated_fixed32 = 7;
repeated fixed64 repeated_fixed64 = 8;
repeated sfixed32 repeated_sfixed32 = 9;
repeated sfixed64 repeated_sfixed64 = 10;
repeated float repeated_float = 11;
repeated double repeated_double = 12;
repeated bool repeated_bool = 13;
repeated NestedEnum repeated_nested_enum = 14;
}
message TestUnpackedTypes {
repeated int32 repeated_int32 = 1 [packed = false];
repeated int64 repeated_int64 = 2 [packed = false];
repeated uint32 repeated_uint32 = 3 [packed = false];
repeated uint64 repeated_uint64 = 4 [packed = false];
repeated sint32 repeated_sint32 = 5 [packed = false];
repeated sint64 repeated_sint64 = 6 [packed = false];
repeated fixed32 repeated_fixed32 = 7 [packed = false];
repeated fixed64 repeated_fixed64 = 8 [packed = false];
repeated sfixed32 repeated_sfixed32 = 9 [packed = false];
repeated sfixed64 repeated_sfixed64 = 10 [packed = false];
repeated float repeated_float = 11 [packed = false];
repeated double repeated_double = 12 [packed = false];
repeated bool repeated_bool = 13 [packed = false];
repeated TestPackedTypes.NestedEnum repeated_nested_enum = 14 [packed = false];
}

View File

@ -85,34 +85,108 @@ from google.protobuf import text_format
_FieldDescriptor = descriptor_mod.FieldDescriptor
def NewMessage(bases, descriptor, dictionary):
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
_AddSlots(descriptor, dictionary)
return bases
class GeneratedProtocolMessageType(type):
"""Metaclass for protocol message classes created at runtime from Descriptors.
def InitMessage(descriptor, cls):
cls._decoders_by_tag = {}
cls._extensions_by_name = {}
cls._extensions_by_number = {}
if (descriptor.has_options and
descriptor.GetOptions().message_set_wire_format):
cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = (
decoder.MessageSetItemDecoder(cls._extensions_by_number), None)
We add implementations for all methods described in the Message class. We
also create properties to allow getting/setting all fields in the protocol
message. Finally, we create slots to prevent users from accidentally
"setting" nonexistent fields in the protocol message, which then wouldn't get
serialized / deserialized properly.
# Attach stuff to each FieldDescriptor for quick lookup later on.
for field in descriptor.fields:
_AttachFieldHelpers(cls, field)
The protocol compiler currently uses this metaclass to create protocol
message classes at runtime. Clients can also manually create their own
classes at runtime, as in this example:
descriptor._concrete_class = cls # pylint: disable=protected-access
_AddEnumValues(descriptor, cls)
_AddInitMethod(descriptor, cls)
_AddPropertiesForFields(descriptor, cls)
_AddPropertiesForExtensions(descriptor, cls)
_AddStaticMethods(cls)
_AddMessageMethods(descriptor, cls)
_AddPrivateHelperMethods(descriptor, cls)
copyreg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
mydescriptor = Descriptor(.....)
class MyProtoClass(Message):
__metaclass__ = GeneratedProtocolMessageType
DESCRIPTOR = mydescriptor
myproto_instance = MyProtoClass()
myproto.foo_field = 23
...
The above example will not work for nested types. If you wish to include them,
use reflection.MakeClass() instead of manually instantiating the class in
order to create the appropriate class structure.
"""
# Must be consistent with the protocol-compiler code in
# proto2/compiler/internal/generator.*.
_DESCRIPTOR_KEY = 'DESCRIPTOR'
def __new__(cls, name, bases, dictionary):
"""Custom allocation for runtime-generated class types.
We override __new__ because this is apparently the only place
where we can meaningfully set __slots__ on the class we're creating(?).
(The interplay between metaclasses and slots is not very well-documented).
Args:
name: Name of the class (ignored, but required by the
metaclass protocol).
bases: Base classes of the class we're constructing.
(Should be message.Message). We ignore this field, but
it's required by the metaclass protocol
dictionary: The class dictionary of the class we're
constructing. dictionary[_DESCRIPTOR_KEY] must contain
a Descriptor object describing this protocol message
type.
Returns:
Newly-allocated class.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
_AddClassAttributesForNestedExtensions(descriptor, dictionary)
_AddSlots(descriptor, dictionary)
superclass = super(GeneratedProtocolMessageType, cls)
new_class = superclass.__new__(cls, name, bases, dictionary)
return new_class
def __init__(cls, name, bases, dictionary):
"""Here we perform the majority of our work on the class.
We add enum getters, an __init__ method, implementations
of all Message methods, and properties for all fields
in the protocol type.
Args:
name: Name of the class (ignored, but required by the
metaclass protocol).
bases: Base classes of the class we're constructing.
(Should be message.Message). We ignore this field, but
it's required by the metaclass protocol
dictionary: The class dictionary of the class we're
constructing. dictionary[_DESCRIPTOR_KEY] must contain
a Descriptor object describing this protocol message
type.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
cls._decoders_by_tag = {}
cls._extensions_by_name = {}
cls._extensions_by_number = {}
if (descriptor.has_options and
descriptor.GetOptions().message_set_wire_format):
cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = (
decoder.MessageSetItemDecoder(cls._extensions_by_number), None)
# Attach stuff to each FieldDescriptor for quick lookup later on.
for field in descriptor.fields:
_AttachFieldHelpers(cls, field)
descriptor._concrete_class = cls # pylint: disable=protected-access
_AddEnumValues(descriptor, cls)
_AddInitMethod(descriptor, cls)
_AddPropertiesForFields(descriptor, cls)
_AddPropertiesForExtensions(descriptor, cls)
_AddStaticMethods(cls)
_AddMessageMethods(descriptor, cls)
_AddPrivateHelperMethods(descriptor, cls)
copyreg.pickle(cls, lambda obj: (cls, (), obj.__getstate__()))
superclass = super(GeneratedProtocolMessageType, cls)
superclass.__init__(name, bases, dictionary)
# Stateless helpers for GeneratedProtocolMessageType below.
@ -362,9 +436,10 @@ def _DefaultValueConstructorForField(field):
message_type = field.message_type
def MakeSubMessageDefault(message):
result = message_type._concrete_class()
result._SetListener(message._listener_for_children)
if field.containing_oneof:
message._UpdateOneofState(field)
result._SetListener(
_OneofListener(message, field)
if field.containing_oneof is not None
else message._listener_for_children)
return result
return MakeSubMessageDefault
@ -634,21 +709,11 @@ def _AddPropertiesForNonRepeatedCompositeField(field, cls):
proto_field_name = field.name
property_name = _PropertyName(proto_field_name)
# TODO(komarek): Can anyone explain to me why we cache the message_type this
# way, instead of referring to field.message_type inside of getter(self)?
# What if someone sets message_type later on (which makes for simpler
# dyanmic proto descriptor and class creation code).
message_type = field.message_type
def getter(self):
field_value = self._fields.get(field)
if field_value is None:
# Construct a new object to represent this field.
field_value = message_type._concrete_class() # use field.message_type?
field_value._SetListener(
_OneofListener(self, field)
if field.containing_oneof is not None
else self._listener_for_children)
field_value = field._default_constructor(self)
# Atomically check if another thread has preempted us and, if not, swap
# in the new object we just created. If someone has preempted us, we
@ -1121,7 +1186,7 @@ def _AddIsInitializedMethod(message_descriptor, cls):
if _IsMessageMapField(field):
for key in value:
element = value[key]
prefix = "%s[%d]." % (name, key)
prefix = "%s[%s]." % (name, key)
sub_errors = element.FindInitializationErrors()
errors += [prefix + error for error in sub_errors]
else:
@ -1173,8 +1238,6 @@ def _AddMergeFromMethod(cls):
# Construct a new object to represent this field.
field_value = field._default_constructor(self)
fields[field] = field_value
if field.containing_oneof:
self._UpdateOneofState(field)
field_value.MergeFrom(value)
else:
self._fields[field] = value

View File

@ -52,6 +52,7 @@ from google.protobuf import text_format
from google.protobuf.internal import api_implementation
from google.protobuf.internal import more_extensions_pb2
from google.protobuf.internal import more_messages_pb2
from google.protobuf.internal import message_set_extensions_pb2
from google.protobuf.internal import wire_format
from google.protobuf.internal import test_util
from google.protobuf.internal import decoder
@ -1682,8 +1683,8 @@ class ReflectionTest(unittest.TestCase):
proto.optional_string = 'abc'
def testStringUTF8Serialization(self):
proto = unittest_mset_pb2.TestMessageSet()
extension_message = unittest_mset_pb2.TestMessageSetExtension2
proto = message_set_extensions_pb2.TestMessageSet()
extension_message = message_set_extensions_pb2.TestMessageSetExtension2
extension = extension_message.message_set_extension
test_utf8 = u'Тест'
@ -1703,15 +1704,14 @@ class ReflectionTest(unittest.TestCase):
bytes_read = raw.MergeFromString(serialized)
self.assertEqual(len(serialized), bytes_read)
message2 = unittest_mset_pb2.TestMessageSetExtension2()
message2 = message_set_extensions_pb2.TestMessageSetExtension2()
self.assertEqual(1, len(raw.item))
# Check that the type_id is the same as the tag ID in the .proto file.
self.assertEqual(raw.item[0].type_id, 1547769)
self.assertEqual(raw.item[0].type_id, 98418634)
# Check the actual bytes on the wire.
self.assertTrue(
raw.item[0].message.endswith(test_utf8_bytes))
self.assertTrue(raw.item[0].message.endswith(test_utf8_bytes))
bytes_read = message2.MergeFromString(raw.item[0].message)
self.assertEqual(len(raw.item[0].message), bytes_read)
@ -2395,9 +2395,9 @@ class SerializationTest(unittest.TestCase):
self.assertEqual(42, second_proto.optional_nested_message.bb)
def testMessageSetWireFormat(self):
proto = unittest_mset_pb2.TestMessageSet()
extension_message1 = unittest_mset_pb2.TestMessageSetExtension1
extension_message2 = unittest_mset_pb2.TestMessageSetExtension2
proto = message_set_extensions_pb2.TestMessageSet()
extension_message1 = message_set_extensions_pb2.TestMessageSetExtension1
extension_message2 = message_set_extensions_pb2.TestMessageSetExtension2
extension1 = extension_message1.message_set_extension
extension2 = extension_message2.message_set_extension
proto.Extensions[extension1].i = 123
@ -2415,20 +2415,20 @@ class SerializationTest(unittest.TestCase):
raw.MergeFromString(serialized))
self.assertEqual(2, len(raw.item))
message1 = unittest_mset_pb2.TestMessageSetExtension1()
message1 = message_set_extensions_pb2.TestMessageSetExtension1()
self.assertEqual(
len(raw.item[0].message),
message1.MergeFromString(raw.item[0].message))
self.assertEqual(123, message1.i)
message2 = unittest_mset_pb2.TestMessageSetExtension2()
message2 = message_set_extensions_pb2.TestMessageSetExtension2()
self.assertEqual(
len(raw.item[1].message),
message2.MergeFromString(raw.item[1].message))
self.assertEqual('foo', message2.str)
# Deserialize using the MessageSet wire format.
proto2 = unittest_mset_pb2.TestMessageSet()
proto2 = message_set_extensions_pb2.TestMessageSet()
self.assertEqual(
len(serialized),
proto2.MergeFromString(serialized))
@ -2446,37 +2446,37 @@ class SerializationTest(unittest.TestCase):
# Add an item.
item = raw.item.add()
item.type_id = 1545008
extension_message1 = unittest_mset_pb2.TestMessageSetExtension1
message1 = unittest_mset_pb2.TestMessageSetExtension1()
item.type_id = 98418603
extension_message1 = message_set_extensions_pb2.TestMessageSetExtension1
message1 = message_set_extensions_pb2.TestMessageSetExtension1()
message1.i = 12345
item.message = message1.SerializeToString()
# Add a second, unknown extension.
item = raw.item.add()
item.type_id = 1545009
extension_message1 = unittest_mset_pb2.TestMessageSetExtension1
message1 = unittest_mset_pb2.TestMessageSetExtension1()
item.type_id = 98418604
extension_message1 = message_set_extensions_pb2.TestMessageSetExtension1
message1 = message_set_extensions_pb2.TestMessageSetExtension1()
message1.i = 12346
item.message = message1.SerializeToString()
# Add another unknown extension.
item = raw.item.add()
item.type_id = 1545010
message1 = unittest_mset_pb2.TestMessageSetExtension2()
item.type_id = 98418605
message1 = message_set_extensions_pb2.TestMessageSetExtension2()
message1.str = 'foo'
item.message = message1.SerializeToString()
serialized = raw.SerializeToString()
# Parse message using the message set wire format.
proto = unittest_mset_pb2.TestMessageSet()
proto = message_set_extensions_pb2.TestMessageSet()
self.assertEqual(
len(serialized),
proto.MergeFromString(serialized))
# Check that the message parsed well.
extension_message1 = unittest_mset_pb2.TestMessageSetExtension1
extension_message1 = message_set_extensions_pb2.TestMessageSetExtension1
extension1 = extension_message1.message_set_extension
self.assertEquals(12345, proto.Extensions[extension1].i)
@ -2805,7 +2805,7 @@ class SerializationTest(unittest.TestCase):
class OptionsTest(unittest.TestCase):
def testMessageOptions(self):
proto = unittest_mset_pb2.TestMessageSet()
proto = message_set_extensions_pb2.TestMessageSet()
self.assertEqual(True,
proto.DESCRIPTOR.GetOptions().message_set_wire_format)
proto = unittest_pb2.TestAllTypes()
@ -2824,7 +2824,7 @@ class OptionsTest(unittest.TestCase):
proto.packed_double.append(3.0)
for field_descriptor, _ in proto.ListFields():
self.assertEqual(True, field_descriptor.GetOptions().packed)
self.assertEqual(reflection._FieldDescriptor.LABEL_REPEATED,
self.assertEqual(descriptor.FieldDescriptor.LABEL_REPEATED,
field_descriptor.label)

View File

@ -604,7 +604,8 @@ def GoldenFile(filename):
# Search internally.
path = '.'
full_path = os.path.join(path, 'third_party/py/google/protobuf/testdata', filename)
full_path = os.path.join(path, 'third_party/py/google/protobuf/testdata',
filename)
if os.path.exists(full_path):
# Found it. Load the golden file from the testdata directory.
return open(full_path, 'rb')

View File

@ -35,6 +35,7 @@
__author__ = 'kenton@google.com (Kenton Varda)'
import re
import string
import unittest
import unittest
@ -497,6 +498,36 @@ class OnlyWorksWithProto2RightNowTests(TextFormatBase):
' }\n'
'}\n')
def testMapOrderEnforcement(self):
message = map_unittest_pb2.TestMap()
for letter in string.ascii_uppercase[13:26]:
message.map_string_string[letter] = 'dummy'
for letter in reversed(string.ascii_uppercase[0:13]):
message.map_string_string[letter] = 'dummy'
golden = ''.join((
'map_string_string {\n key: "%c"\n value: "dummy"\n}\n' % (letter,)
for letter in string.ascii_uppercase))
self.CompareToGoldenText(text_format.MessageToString(message), golden)
def testMapOrderSemantics(self):
golden_lines = self.ReadGolden('map_test_data.txt')
# The C++ implementation emits defaulted-value fields, while the Python
# implementation does not. Adjusting for this is awkward, but it is
# valuable to test against a common golden file.
line_blacklist = (' key: 0\n',
' value: 0\n',
' key: false\n',
' value: false\n')
golden_lines = [line for line in golden_lines if line not in line_blacklist]
message = map_unittest_pb2.TestMap()
text_format.ParseLines(golden_lines, message)
candidate = text_format.MessageToString(message)
# The Python implementation emits "1.0" for the double value that the C++
# implementation emits as "1".
candidate = candidate.replace('1.0', '1', 2)
self.assertMultiLineEqual(candidate, ''.join(golden_lines))
# Tests of proto2-only features (MessageSet, extensions, etc.).
class Proto2Tests(TextFormatBase):

View File

@ -41,11 +41,18 @@ from google.protobuf import unittest_pb2
from google.protobuf import unittest_proto3_arena_pb2
from google.protobuf.internal import api_implementation
from google.protobuf.internal import encoder
from google.protobuf.internal import message_set_extensions_pb2
from google.protobuf.internal import missing_enum_values_pb2
from google.protobuf.internal import test_util
from google.protobuf.internal import type_checkers
def SkipIfCppImplementation(func):
return unittest.skipIf(
api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
'C++ implementation does not expose unknown fields to Python')(func)
class UnknownFieldsTest(unittest.TestCase):
def setUp(self):
@ -83,15 +90,15 @@ class UnknownFieldsTest(unittest.TestCase):
# Add an unknown extension.
item = raw.item.add()
item.type_id = 1545009
message1 = unittest_mset_pb2.TestMessageSetExtension1()
item.type_id = 98418603
message1 = message_set_extensions_pb2.TestMessageSetExtension1()
message1.i = 12345
item.message = message1.SerializeToString()
serialized = raw.SerializeToString()
# Parse message using the message set wire format.
proto = unittest_mset_pb2.TestMessageSet()
proto = message_set_extensions_pb2.TestMessageSet()
proto.MergeFromString(serialized)
# Verify that the unknown extension is serialized unchanged
@ -100,13 +107,6 @@ class UnknownFieldsTest(unittest.TestCase):
new_raw.MergeFromString(reserialized)
self.assertEqual(raw, new_raw)
# C++ implementation for proto2 does not currently take into account unknown
# fields when checking equality.
#
# TODO(haberman): fix this.
@unittest.skipIf(
api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
'C++ implementation does not expose unknown fields to Python')
def testEquals(self):
message = unittest_pb2.TestEmptyMessage()
message.ParseFromString(self.all_fields_data)
@ -117,9 +117,6 @@ class UnknownFieldsTest(unittest.TestCase):
self.assertNotEqual(self.empty_message, message)
@unittest.skipIf(
api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
'C++ implementation does not expose unknown fields to Python')
class UnknownFieldsAccessorsTest(unittest.TestCase):
def setUp(self):
@ -129,7 +126,14 @@ class UnknownFieldsAccessorsTest(unittest.TestCase):
self.all_fields_data = self.all_fields.SerializeToString()
self.empty_message = unittest_pb2.TestEmptyMessage()
self.empty_message.ParseFromString(self.all_fields_data)
self.unknown_fields = self.empty_message._unknown_fields
if api_implementation.Type() != 'cpp':
# _unknown_fields is an implementation detail.
self.unknown_fields = self.empty_message._unknown_fields
# All the tests that use GetField() check an implementation detail of the
# Python implementation, which stores unknown fields as serialized strings.
# These tests are skipped by the C++ implementation: it's enough to check that
# the message is correctly serialized.
def GetField(self, name):
field_descriptor = self.descriptor.fields_by_name[name]
@ -142,30 +146,37 @@ class UnknownFieldsAccessorsTest(unittest.TestCase):
decoder(value, 0, len(value), self.all_fields, result_dict)
return result_dict[field_descriptor]
@SkipIfCppImplementation
def testEnum(self):
value = self.GetField('optional_nested_enum')
self.assertEqual(self.all_fields.optional_nested_enum, value)
@SkipIfCppImplementation
def testRepeatedEnum(self):
value = self.GetField('repeated_nested_enum')
self.assertEqual(self.all_fields.repeated_nested_enum, value)
@SkipIfCppImplementation
def testVarint(self):
value = self.GetField('optional_int32')
self.assertEqual(self.all_fields.optional_int32, value)
@SkipIfCppImplementation
def testFixed32(self):
value = self.GetField('optional_fixed32')
self.assertEqual(self.all_fields.optional_fixed32, value)
@SkipIfCppImplementation
def testFixed64(self):
value = self.GetField('optional_fixed64')
self.assertEqual(self.all_fields.optional_fixed64, value)
@SkipIfCppImplementation
def testLengthDelimited(self):
value = self.GetField('optional_string')
self.assertEqual(self.all_fields.optional_string, value)
@SkipIfCppImplementation
def testGroup(self):
value = self.GetField('optionalgroup')
self.assertEqual(self.all_fields.optionalgroup, value)
@ -173,7 +184,7 @@ class UnknownFieldsAccessorsTest(unittest.TestCase):
def testCopyFrom(self):
message = unittest_pb2.TestEmptyMessage()
message.CopyFrom(self.empty_message)
self.assertEqual(self.unknown_fields, message._unknown_fields)
self.assertEqual(message.SerializeToString(), self.all_fields_data)
def testMergeFrom(self):
message = unittest_pb2.TestAllTypes()
@ -187,27 +198,26 @@ class UnknownFieldsAccessorsTest(unittest.TestCase):
message.optional_uint32 = 4
destination = unittest_pb2.TestEmptyMessage()
destination.ParseFromString(message.SerializeToString())
unknown_fields = destination._unknown_fields[:]
destination.MergeFrom(source)
self.assertEqual(unknown_fields + source._unknown_fields,
destination._unknown_fields)
# Check that the fields where correctly merged, even stored in the unknown
# fields set.
message.ParseFromString(destination.SerializeToString())
self.assertEqual(message.optional_int32, 1)
self.assertEqual(message.optional_uint32, 2)
self.assertEqual(message.optional_int64, 3)
def testClear(self):
self.empty_message.Clear()
self.assertEqual(0, len(self.empty_message._unknown_fields))
# All cleared, even unknown fields.
self.assertEqual(self.empty_message.SerializeToString(), b'')
def testUnknownExtensions(self):
message = unittest_pb2.TestEmptyMessageWithExtensions()
message.ParseFromString(self.all_fields_data)
self.assertEqual(self.empty_message._unknown_fields,
message._unknown_fields)
self.assertEqual(message.SerializeToString(), self.all_fields_data)
@unittest.skipIf(
api_implementation.Type() == 'cpp' and api_implementation.Version() == 2,
'C++ implementation does not expose unknown fields to Python')
class UnknownEnumValuesTest(unittest.TestCase):
def setUp(self):
@ -227,7 +237,14 @@ class UnknownEnumValuesTest(unittest.TestCase):
self.message_data = self.message.SerializeToString()
self.missing_message = missing_enum_values_pb2.TestMissingEnumValues()
self.missing_message.ParseFromString(self.message_data)
self.unknown_fields = self.missing_message._unknown_fields
if api_implementation.Type() != 'cpp':
# _unknown_fields is an implementation detail.
self.unknown_fields = self.missing_message._unknown_fields
# All the tests that use GetField() check an implementation detail of the
# Python implementation, which stores unknown fields as serialized strings.
# These tests are skipped by the C++ implementation: it's enough to check that
# the message is correctly serialized.
def GetField(self, name):
field_descriptor = self.descriptor.fields_by_name[name]
@ -241,15 +258,18 @@ class UnknownEnumValuesTest(unittest.TestCase):
decoder(value, 0, len(value), self.message, result_dict)
return result_dict[field_descriptor]
@SkipIfCppImplementation
def testUnknownEnumValue(self):
self.assertFalse(self.missing_message.HasField('optional_nested_enum'))
value = self.GetField('optional_nested_enum')
self.assertEqual(self.message.optional_nested_enum, value)
@SkipIfCppImplementation
def testUnknownRepeatedEnumValue(self):
value = self.GetField('repeated_nested_enum')
self.assertEqual(self.message.repeated_nested_enum, value)
@SkipIfCppImplementation
def testUnknownPackedEnumValue(self):
value = self.GetField('packed_nested_enum')
self.assertEqual(self.message.packed_nested_enum, value)

View File

@ -37,21 +37,29 @@ Descriptor objects at runtime backed by the protocol buffer C++ API.
__author__ = 'tibell@google.com (Johan Tibell)'
from google.protobuf.pyext import _message
from google.protobuf import message
def NewMessage(bases, message_descriptor, dictionary):
"""Creates a new protocol message *class*."""
new_bases = []
for base in bases:
if base is message.Message:
# _message.Message must come before message.Message as it
# overrides methods in that class.
new_bases.append(_message.Message)
new_bases.append(base)
return tuple(new_bases)
class GeneratedProtocolMessageType(_message.MessageMeta):
"""Metaclass for protocol message classes created at runtime from Descriptors.
def InitMessage(message_descriptor, cls):
"""Finalizes the creation of a message class."""
cls.AddDescriptors(message_descriptor)
The protocol compiler currently uses this metaclass to create protocol
message classes at runtime. Clients can also manually create their own
classes at runtime, as in this example:
mydescriptor = Descriptor(.....)
class MyProtoClass(Message):
__metaclass__ = GeneratedProtocolMessageType
DESCRIPTOR = mydescriptor
myproto_instance = MyProtoClass()
myproto.foo_field = 23
...
The above example will not work for nested types. If you wish to include them,
use reflection.MakeClass() instead of manually instantiating the class in
order to create the appropriate class structure.
"""
# Must be consistent with the protocol-compiler code in
# proto2/compiler/internal/generator.*.
_DESCRIPTOR_KEY = 'DESCRIPTOR'

View File

@ -193,7 +193,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) {
io::CodedInputStream input(
reinterpret_cast<const uint8*>(serialized.c_str()), serialized.size());
input.SetExtensionRegistry(GetDescriptorPool()->pool,
cmessage::GetMessageFactory());
GetDescriptorPool()->message_factory);
bool success = cmsg->message->MergePartialFromCodedStream(&input);
if (!success) {
PyErr_Format(PyExc_ValueError, "Error parsing Options message");

View File

@ -33,6 +33,7 @@
#include <Python.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/pyext/descriptor_pool.h>
#include <google/protobuf/pyext/descriptor.h>
#include <google/protobuf/pyext/message.h>
@ -67,6 +68,11 @@ PyDescriptorPool* NewDescriptorPool() {
// as underlay.
cdescriptor_pool->pool = new DescriptorPool(DescriptorPool::generated_pool());
DynamicMessageFactory* message_factory = new DynamicMessageFactory();
// This option might be the default some day.
message_factory->SetDelegateToGeneratedFactory(true);
cdescriptor_pool->message_factory = message_factory;
// TODO(amauryfa): Rewrite the SymbolDatabase in C so that it uses the same
// storage.
cdescriptor_pool->classes_by_descriptor =
@ -93,6 +99,7 @@ static void Dealloc(PyDescriptorPool* self) {
Py_DECREF(it->second);
}
delete self->descriptor_options;
delete self->message_factory;
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
}

View File

@ -38,6 +38,8 @@
namespace google {
namespace protobuf {
class MessageFactory;
namespace python {
// Wraps operations to the global DescriptorPool which contains information
@ -55,6 +57,14 @@ typedef struct PyDescriptorPool {
DescriptorPool* pool;
// DynamicMessageFactory used to create C++ instances of messages.
// This object cache the descriptors that were used, so the DescriptorPool
// needs to get rid of it before it can delete itself.
//
// Note: A C++ MessageFactory is different from the Python MessageFactory.
// The C++ one creates messages, when the Python one creates classes.
MessageFactory* message_factory;
// Make our own mapping to retrieve Python classes from C++ descriptors.
//
// Descriptor pointers stored here are owned by the DescriptorPool above.

View File

@ -33,6 +33,7 @@
#include <google/protobuf/pyext/extension_dict.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
@ -183,7 +184,8 @@ PyObject* ClearExtension(ExtensionDict* self, PyObject* extension) {
return NULL;
}
}
if (cmessage::ClearFieldByDescriptor(self->parent, descriptor) == NULL) {
if (ScopedPyObjectPtr(cmessage::ClearFieldByDescriptor(
self->parent, descriptor)) == NULL) {
return NULL;
}
if (PyDict_DelItem(self->values, extension) < 0) {
@ -268,7 +270,7 @@ PyTypeObject ExtensionDict_Type = {
0, // tp_as_number
0, // tp_as_sequence
&extension_dict::MpMethods, // tp_as_mapping
0, // tp_hash
PyObject_HashNotImplemented, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro

View File

@ -49,9 +49,10 @@
#endif
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/util/message_differencer.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/message.h>
#include <google/protobuf/text_format.h>
#include <google/protobuf/pyext/descriptor.h>
@ -88,12 +89,308 @@ namespace google {
namespace protobuf {
namespace python {
static PyObject* kDESCRIPTOR;
static PyObject* k_extensions_by_name;
static PyObject* k_extensions_by_number;
PyObject* EnumTypeWrapper_class;
static PyObject* PythonMessage_class;
static PyObject* kEmptyWeakref;
// Defines the Metaclass of all Message classes.
// It allows us to cache some C++ pointers in the class object itself, they are
// faster to extract than from the type's dictionary.
struct PyMessageMeta {
// This is how CPython subclasses C structures: the base structure must be
// the first member of the object.
PyHeapTypeObject super;
// C++ descriptor of this message.
const Descriptor* message_descriptor;
// Owned reference, used to keep the pointer above alive.
PyObject* py_message_descriptor;
};
namespace message_meta {
static int InsertEmptyWeakref(PyTypeObject* base);
// Add the number of a field descriptor to the containing message class.
// Equivalent to:
// _cls.<field>_FIELD_NUMBER = <number>
static bool AddFieldNumberToClass(
PyObject* cls, const FieldDescriptor* field_descriptor) {
string constant_name = field_descriptor->name() + "_FIELD_NUMBER";
UpperString(&constant_name);
ScopedPyObjectPtr attr_name(PyString_FromStringAndSize(
constant_name.c_str(), constant_name.size()));
if (attr_name == NULL) {
return false;
}
ScopedPyObjectPtr number(PyInt_FromLong(field_descriptor->number()));
if (number == NULL) {
return false;
}
if (PyObject_SetAttr(cls, attr_name, number) == -1) {
return false;
}
return true;
}
// Finalize the creation of the Message class.
// Called from its metaclass: GeneratedProtocolMessageType.__init__().
static int AddDescriptors(PyObject* cls, PyObject* descriptor) {
const Descriptor* message_descriptor =
cdescriptor_pool::RegisterMessageClass(
GetDescriptorPool(), cls, descriptor);
if (message_descriptor == NULL) {
return -1;
}
// If there are extension_ranges, the message is "extendable", and extension
// classes will register themselves in this class.
if (message_descriptor->extension_range_count() > 0) {
ScopedPyObjectPtr by_name(PyDict_New());
if (PyObject_SetAttr(cls, k_extensions_by_name, by_name) < 0) {
return -1;
}
ScopedPyObjectPtr by_number(PyDict_New());
if (PyObject_SetAttr(cls, k_extensions_by_number, by_number) < 0) {
return -1;
}
}
// For each field set: cls.<field>_FIELD_NUMBER = <number>
for (int i = 0; i < message_descriptor->field_count(); ++i) {
if (!AddFieldNumberToClass(cls, message_descriptor->field(i))) {
return -1;
}
}
// For each enum set cls.<enum name> = EnumTypeWrapper(<enum descriptor>).
//
// The enum descriptor we get from
// <messagedescriptor>.enum_types_by_name[name]
// which was built previously.
for (int i = 0; i < message_descriptor->enum_type_count(); ++i) {
const EnumDescriptor* enum_descriptor = message_descriptor->enum_type(i);
ScopedPyObjectPtr enum_type(
PyEnumDescriptor_FromDescriptor(enum_descriptor));
if (enum_type == NULL) {
return -1;
}
// Add wrapped enum type to message class.
ScopedPyObjectPtr wrapped(PyObject_CallFunctionObjArgs(
EnumTypeWrapper_class, enum_type.get(), NULL));
if (wrapped == NULL) {
return -1;
}
if (PyObject_SetAttrString(
cls, enum_descriptor->name().c_str(), wrapped) == -1) {
return -1;
}
// For each enum value add cls.<name> = <number>
for (int j = 0; j < enum_descriptor->value_count(); ++j) {
const EnumValueDescriptor* enum_value_descriptor =
enum_descriptor->value(j);
ScopedPyObjectPtr value_number(PyInt_FromLong(
enum_value_descriptor->number()));
if (value_number == NULL) {
return -1;
}
if (PyObject_SetAttrString(
cls, enum_value_descriptor->name().c_str(), value_number) == -1) {
return -1;
}
}
}
// For each extension set cls.<extension name> = <extension descriptor>.
//
// Extension descriptors come from
// <message descriptor>.extensions_by_name[name]
// which was defined previously.
for (int i = 0; i < message_descriptor->extension_count(); ++i) {
const google::protobuf::FieldDescriptor* field = message_descriptor->extension(i);
ScopedPyObjectPtr extension_field(PyFieldDescriptor_FromDescriptor(field));
if (extension_field == NULL) {
return -1;
}
// Add the extension field to the message class.
if (PyObject_SetAttrString(
cls, field->name().c_str(), extension_field) == -1) {
return -1;
}
// For each extension set cls.<extension name>_FIELD_NUMBER = <number>.
if (!AddFieldNumberToClass(cls, field)) {
return -1;
}
}
return 0;
}
static PyObject* New(PyTypeObject* type,
PyObject* args, PyObject* kwargs) {
static char *kwlist[] = {"name", "bases", "dict", 0};
PyObject *bases, *dict;
const char* name;
// Check arguments: (name, bases, dict)
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "sO!O!:type", kwlist,
&name,
&PyTuple_Type, &bases,
&PyDict_Type, &dict)) {
return NULL;
}
// Check bases: only (), or (message.Message,) are allowed
if (!(PyTuple_GET_SIZE(bases) == 0 ||
(PyTuple_GET_SIZE(bases) == 1 &&
PyTuple_GET_ITEM(bases, 0) == PythonMessage_class))) {
PyErr_SetString(PyExc_TypeError,
"A Message class can only inherit from Message");
return NULL;
}
// Check dict['DESCRIPTOR']
PyObject* descriptor = PyDict_GetItem(dict, kDESCRIPTOR);
if (descriptor == NULL) {
PyErr_SetString(PyExc_TypeError, "Message class has no DESCRIPTOR");
return NULL;
}
if (!PyObject_TypeCheck(descriptor, &PyMessageDescriptor_Type)) {
PyErr_Format(PyExc_TypeError, "Expected a message Descriptor, got %s",
descriptor->ob_type->tp_name);
return NULL;
}
// Build the arguments to the base metaclass.
// We change the __bases__ classes.
ScopedPyObjectPtr new_args(Py_BuildValue(
"s(OO)O", name, &CMessage_Type, PythonMessage_class, dict));
if (new_args == NULL) {
return NULL;
}
// Call the base metaclass.
ScopedPyObjectPtr result(PyType_Type.tp_new(type, new_args, NULL));
if (result == NULL) {
return NULL;
}
PyMessageMeta* newtype = reinterpret_cast<PyMessageMeta*>(result.get());
// Insert the empty weakref into the base classes.
if (InsertEmptyWeakref(
reinterpret_cast<PyTypeObject*>(PythonMessage_class)) < 0 ||
InsertEmptyWeakref(&CMessage_Type) < 0) {
return NULL;
}
// Cache the descriptor, both as Python object and as C++ pointer.
const Descriptor* message_descriptor =
PyMessageDescriptor_AsDescriptor(descriptor);
if (message_descriptor == NULL) {
return NULL;
}
Py_INCREF(descriptor);
newtype->py_message_descriptor = descriptor;
newtype->message_descriptor = message_descriptor;
// Continue with type initialization: add other descriptors, enum values...
if (AddDescriptors(result, descriptor) < 0) {
return NULL;
}
return result.release();
}
static void Dealloc(PyMessageMeta *self) {
Py_DECREF(self->py_message_descriptor);
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
}
static PyObject* GetDescriptor(PyMessageMeta *self, void *closure) {
Py_INCREF(self->py_message_descriptor);
return self->py_message_descriptor;
}
// This function inserts and empty weakref at the end of the list of
// subclasses for the main protocol buffer Message class.
//
// This eliminates a O(n^2) behaviour in the internal add_subclass
// routine.
static int InsertEmptyWeakref(PyTypeObject *base_type) {
#if PY_MAJOR_VERSION >= 3
// Python 3.4 has already included the fix for the issue that this
// hack addresses. For further background and the fix please see
// https://bugs.python.org/issue17936.
return 0;
#else
PyObject *subclasses = base_type->tp_subclasses;
if (subclasses && PyList_CheckExact(subclasses)) {
return PyList_Append(subclasses, kEmptyWeakref);
}
return 0;
#endif // PY_MAJOR_VERSION >= 3
}
} // namespace message_meta
PyTypeObject PyMessageMeta_Type {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
FULL_MODULE_NAME ".MessageMeta", // tp_name
sizeof(PyMessageMeta), // tp_basicsize
0, // tp_itemsize
(destructor)message_meta::Dealloc, // tp_dealloc
0, // tp_print
0, // tp_getattr
0, // tp_setattr
0, // tp_compare
0, // tp_repr
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro
0, // tp_setattro
0, // tp_as_buffer
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
"The metaclass of ProtocolMessages", // tp_doc
0, // tp_traverse
0, // tp_clear
0, // tp_richcompare
0, // tp_weaklistoffset
0, // tp_iter
0, // tp_iternext
0, // tp_methods
0, // tp_members
0, // tp_getset
0, // tp_base
0, // tp_dict
0, // tp_descr_get
0, // tp_descr_set
0, // tp_dictoffset
0, // tp_init
0, // tp_alloc
message_meta::New, // tp_new
};
static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) {
if (!PyObject_TypeCheck(cls, &PyMessageMeta_Type)) {
PyErr_Format(PyExc_TypeError, "Class %s is not a Message", cls->tp_name);
return NULL;
}
return reinterpret_cast<PyMessageMeta*>(cls)->message_descriptor;
}
// Forward declarations
namespace cmessage {
static const FieldDescriptor* GetFieldDescriptor(
CMessage* self, PyObject* name);
static const Descriptor* GetMessageDescriptor(PyTypeObject* cls);
static string GetMessageName(CMessage* self);
int InternalReleaseFieldByDescriptor(
CMessage* self,
const FieldDescriptor* field_descriptor,
@ -180,7 +477,7 @@ int ForEachCompositeField(CMessage* self, Visitor visitor) {
if (self->composite_fields) {
// Never use self->message in this function, it may be already freed.
const Descriptor* message_descriptor =
cmessage::GetMessageDescriptor(Py_TYPE(self));
GetMessageDescriptor(Py_TYPE(self));
while (PyDict_Next(self->composite_fields, &pos, &key, &field)) {
Py_ssize_t key_str_size;
char *key_str_data;
@ -213,8 +510,6 @@ int ForEachCompositeField(CMessage* self, Visitor visitor) {
// ---------------------------------------------------------------------
static DynamicMessageFactory* message_factory;
// Constants used for integer type range checking.
PyObject* kPythonZero;
PyObject* kint32min_py;
@ -224,17 +519,13 @@ PyObject* kint64min_py;
PyObject* kint64max_py;
PyObject* kuint64max_py;
PyObject* EnumTypeWrapper_class;
PyObject* EncodeError_class;
PyObject* DecodeError_class;
PyObject* PickleError_class;
// Constant PyString values used for GetAttr/GetItem.
static PyObject* kDESCRIPTOR;
static PyObject* k_cdescriptor;
static PyObject* kfull_name;
static PyObject* k_extensions_by_name;
static PyObject* k_extensions_by_number;
/* Is 64bit */
void FormatTypeError(PyObject* arg, char* expected_types) {
@ -432,10 +723,6 @@ bool CheckFieldBelongsToMessage(const FieldDescriptor* field_descriptor,
namespace cmessage {
DynamicMessageFactory* GetMessageFactory() {
return message_factory;
}
static int MaybeReleaseOverlappingOneofField(
CMessage* cmessage,
const FieldDescriptor* field) {
@ -486,7 +773,7 @@ static Message* GetMutableMessage(
return NULL;
}
return reflection->MutableMessage(
parent_message, parent_field, message_factory);
parent_message, parent_field, GetDescriptorPool()->message_factory);
}
struct FixupMessageReference : public ChildVisitor {
@ -527,8 +814,9 @@ int AssureWritable(CMessage* self) {
// If parent is NULL but we are trying to modify a read-only message, this
// is a reference to a constant default instance that needs to be replaced
// with a mutable top-level message.
const Message* prototype = message_factory->GetPrototype(
self->message->GetDescriptor());
const Message* prototype =
GetDescriptorPool()->message_factory->GetPrototype(
self->message->GetDescriptor());
self->message = prototype->New();
self->owner.reset(self->message);
// Cascade the new owner to eventual children: even if this message is
@ -567,23 +855,6 @@ int AssureWritable(CMessage* self) {
// --- Globals:
// Retrieve the C++ Descriptor of a message class.
// On error, returns NULL with an exception set.
static const Descriptor* GetMessageDescriptor(PyTypeObject* cls) {
ScopedPyObjectPtr descriptor(PyObject_GetAttr(
reinterpret_cast<PyObject*>(cls), kDESCRIPTOR));
if (descriptor == NULL) {
PyErr_SetString(PyExc_TypeError, "Message class has no DESCRIPTOR");
return NULL;
}
if (!PyObject_TypeCheck(descriptor, &PyMessageDescriptor_Type)) {
PyErr_Format(PyExc_TypeError, "Expected a message Descriptor, got %s",
descriptor->ob_type->tp_name);
return NULL;
}
return PyMessageDescriptor_AsDescriptor(descriptor);
}
// Retrieve a C++ FieldDescriptor for a message attribute.
// The C++ message must be valid.
// TODO(amauryfa): This function should stay internal, because exception
@ -846,9 +1117,9 @@ int InitAttributes(CMessage* self, PyObject* kwargs) {
return -1;
}
} else {
if (repeated_scalar_container::Extend(
if (ScopedPyObjectPtr(repeated_scalar_container::Extend(
reinterpret_cast<RepeatedScalarContainer*>(container.get()),
value) ==
value)) ==
NULL) {
return -1;
}
@ -927,7 +1198,7 @@ static PyObject* New(PyTypeObject* type,
return NULL;
}
const Message* default_message =
message_factory->GetPrototype(message_descriptor);
GetDescriptorPool()->message_factory->GetPrototype(message_descriptor);
if (default_message == NULL) {
PyErr_SetString(PyExc_TypeError, message_descriptor->full_name().c_str());
return NULL;
@ -1257,6 +1528,7 @@ int SetOwner(CMessage* self, const shared_ptr<Message>& new_owner) {
Message* ReleaseMessage(CMessage* self,
const Descriptor* descriptor,
const FieldDescriptor* field_descriptor) {
MessageFactory* message_factory = GetDescriptorPool()->message_factory;
Message* released_message = self->message->GetReflection()->ReleaseMessage(
self->message, field_descriptor, message_factory);
// ReleaseMessage will return NULL which differs from
@ -1492,34 +1764,35 @@ static PyObject* SerializePartialToString(CMessage* self) {
// appropriate.
class PythonFieldValuePrinter : public TextFormat::FieldValuePrinter {
public:
PythonFieldValuePrinter() : float_holder_(PyFloat_FromDouble(0)) {}
// Python has some differences from C++ when printing floating point numbers.
//
// 1) Trailing .0 is always printed.
// 2) Outputted is rounded to 12 digits.
// 2) (Python2) Output is rounded to 12 digits.
// 3) (Python3) The full precision of the double is preserved (and Python uses
// David M. Gay's dtoa(), when the C++ code uses SimpleDtoa. There are some
// differences, but they rarely happen)
//
// We override floating point printing with the C-API function for printing
// Python floats to ensure consistency.
string PrintFloat(float value) const { return PrintDouble(value); }
string PrintDouble(double value) const {
reinterpret_cast<PyFloatObject*>(float_holder_.get())->ob_fval = value;
ScopedPyObjectPtr s(PyObject_Str(float_holder_.get()));
if (s == NULL) return string();
// Same as float.__str__()
char* buf = PyOS_double_to_string(
value,
#if PY_MAJOR_VERSION < 3
char *cstr = PyBytes_AS_STRING(static_cast<PyObject*>(s));
'g', PyFloat_STR_PRECISION, // Output is rounded to 12 digits.
#else
char *cstr = PyUnicode_AsUTF8(s);
'r', 0,
#endif
return string(cstr);
Py_DTSF_ADD_DOT_0, // Trailing .0 is always printed.
NULL);
if (!buf) {
return string();
}
string result(buf);
PyMem_Free(buf);
return result;
}
private:
// Holder for a python float object which we use to allow us to use
// the Python API for printing doubles. We initialize once and then
// directly modify it for every float printed to save on allocations
// and refcounting.
ScopedPyObjectPtr float_holder_;
};
static PyObject* ToStr(CMessage* self) {
@ -1590,7 +1863,7 @@ static PyObject* CopyFrom(CMessage* self, PyObject* arg) {
// CopyFrom on the message will not clean up self->composite_fields,
// which can leave us in an inconsistent state, so clear it out here.
Clear(self);
(void)ScopedPyObjectPtr(Clear(self));
self->message->CopyFrom(*other_message->message);
@ -1607,7 +1880,8 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) {
AssureWritable(self);
io::CodedInputStream input(
reinterpret_cast<const uint8*>(data), data_length);
input.SetExtensionRegistry(GetDescriptorPool()->pool, message_factory);
input.SetExtensionRegistry(GetDescriptorPool()->pool,
GetDescriptorPool()->message_factory);
bool success = self->message->MergePartialFromCodedStream(&input);
if (success) {
return PyInt_FromLong(input.CurrentPosition());
@ -1618,7 +1892,7 @@ static PyObject* MergeFromString(CMessage* self, PyObject* arg) {
}
static PyObject* ParseFromString(CMessage* self, PyObject* arg) {
if (Clear(self) == NULL) {
if (ScopedPyObjectPtr(Clear(self)) == NULL) {
return NULL;
}
return MergeFromString(self, arg);
@ -1790,6 +2064,7 @@ static PyObject* ListFields(CMessage* self) {
// Steals reference to 'extension'
PyTuple_SET_ITEM(t.get(), 1, extension);
} else {
// Normal field
const string& field_name = fields[i]->name();
ScopedPyObjectPtr py_field_name(PyString_FromStringAndSize(
field_name.c_str(), field_name.length()));
@ -1841,28 +2116,34 @@ PyObject* FindInitializationErrors(CMessage* self) {
}
static PyObject* RichCompare(CMessage* self, PyObject* other, int opid) {
if (!PyObject_TypeCheck(other, &CMessage_Type)) {
if (opid == Py_EQ) {
Py_RETURN_FALSE;
} else if (opid == Py_NE) {
Py_RETURN_TRUE;
}
}
if (opid == Py_EQ || opid == Py_NE) {
ScopedPyObjectPtr self_fields(ListFields(self));
if (!self_fields) {
return NULL;
}
ScopedPyObjectPtr other_fields(ListFields(
reinterpret_cast<CMessage*>(other)));
if (!other_fields) {
return NULL;
}
return PyObject_RichCompare(self_fields, other_fields, opid);
} else {
// Only equality comparisons are implemented.
if (opid != Py_EQ && opid != Py_NE) {
Py_INCREF(Py_NotImplemented);
return Py_NotImplemented;
}
bool equals = true;
// If other is not a message, it cannot be equal.
if (!PyObject_TypeCheck(other, &CMessage_Type)) {
equals = false;
}
const google::protobuf::Message* other_message =
reinterpret_cast<CMessage*>(other)->message;
// If messages don't have the same descriptors, they are not equal.
if (equals &&
self->message->GetDescriptor() != other_message->GetDescriptor()) {
equals = false;
}
// Check the message contents.
if (equals && !google::protobuf::util::MessageDifferencer::Equals(
*self->message,
*reinterpret_cast<CMessage*>(other)->message)) {
equals = false;
}
if (equals ^ (opid == Py_EQ)) {
Py_RETURN_FALSE;
} else {
Py_RETURN_TRUE;
}
}
PyObject* InternalGetScalar(const Message* message,
@ -1950,7 +2231,7 @@ PyObject* InternalGetSubMessage(
CMessage* self, const FieldDescriptor* field_descriptor) {
const Reflection* reflection = self->message->GetReflection();
const Message& sub_message = reflection->GetMessage(
*self->message, field_descriptor, message_factory);
*self->message, field_descriptor, GetDescriptorPool()->message_factory);
PyObject *message_class = cdescriptor_pool::GetMessageClass(
GetDescriptorPool(), field_descriptor->message_type());
@ -2085,125 +2366,6 @@ PyObject* FromString(PyTypeObject* cls, PyObject* serialized) {
return py_cmsg;
}
// Add the number of a field descriptor to the containing message class.
// Equivalent to:
// _cls.<field>_FIELD_NUMBER = <number>
static bool AddFieldNumberToClass(
PyObject* cls, const FieldDescriptor* field_descriptor) {
string constant_name = field_descriptor->name() + "_FIELD_NUMBER";
UpperString(&constant_name);
ScopedPyObjectPtr attr_name(PyString_FromStringAndSize(
constant_name.c_str(), constant_name.size()));
if (attr_name == NULL) {
return false;
}
ScopedPyObjectPtr number(PyInt_FromLong(field_descriptor->number()));
if (number == NULL) {
return false;
}
if (PyObject_SetAttr(cls, attr_name, number) == -1) {
return false;
}
return true;
}
// Finalize the creation of the Message class.
// Called from its metaclass: GeneratedProtocolMessageType.__init__().
static PyObject* AddDescriptors(PyObject* cls, PyObject* descriptor) {
const Descriptor* message_descriptor =
cdescriptor_pool::RegisterMessageClass(
GetDescriptorPool(), cls, descriptor);
if (message_descriptor == NULL) {
return NULL;
}
// If there are extension_ranges, the message is "extendable", and extension
// classes will register themselves in this class.
if (message_descriptor->extension_range_count() > 0) {
ScopedPyObjectPtr by_name(PyDict_New());
if (PyObject_SetAttr(cls, k_extensions_by_name, by_name) < 0) {
return NULL;
}
ScopedPyObjectPtr by_number(PyDict_New());
if (PyObject_SetAttr(cls, k_extensions_by_number, by_number) < 0) {
return NULL;
}
}
// For each field set: cls.<field>_FIELD_NUMBER = <number>
for (int i = 0; i < message_descriptor->field_count(); ++i) {
if (!AddFieldNumberToClass(cls, message_descriptor->field(i))) {
return NULL;
}
}
// For each enum set cls.<enum name> = EnumTypeWrapper(<enum descriptor>).
//
// The enum descriptor we get from
// <messagedescriptor>.enum_types_by_name[name]
// which was built previously.
for (int i = 0; i < message_descriptor->enum_type_count(); ++i) {
const EnumDescriptor* enum_descriptor = message_descriptor->enum_type(i);
ScopedPyObjectPtr enum_type(
PyEnumDescriptor_FromDescriptor(enum_descriptor));
if (enum_type == NULL) {
return NULL;
}
// Add wrapped enum type to message class.
ScopedPyObjectPtr wrapped(PyObject_CallFunctionObjArgs(
EnumTypeWrapper_class, enum_type.get(), NULL));
if (wrapped == NULL) {
return NULL;
}
if (PyObject_SetAttrString(
cls, enum_descriptor->name().c_str(), wrapped) == -1) {
return NULL;
}
// For each enum value add cls.<name> = <number>
for (int j = 0; j < enum_descriptor->value_count(); ++j) {
const EnumValueDescriptor* enum_value_descriptor =
enum_descriptor->value(j);
ScopedPyObjectPtr value_number(PyInt_FromLong(
enum_value_descriptor->number()));
if (value_number == NULL) {
return NULL;
}
if (PyObject_SetAttrString(
cls, enum_value_descriptor->name().c_str(), value_number) == -1) {
return NULL;
}
}
}
// For each extension set cls.<extension name> = <extension descriptor>.
//
// Extension descriptors come from
// <message descriptor>.extensions_by_name[name]
// which was defined previously.
for (int i = 0; i < message_descriptor->extension_count(); ++i) {
const google::protobuf::FieldDescriptor* field = message_descriptor->extension(i);
ScopedPyObjectPtr extension_field(PyFieldDescriptor_FromDescriptor(field));
if (extension_field == NULL) {
return NULL;
}
// Add the extension field to the message class.
if (PyObject_SetAttrString(
cls, field->name().c_str(), extension_field) == -1) {
return NULL;
}
// For each extension set cls.<extension name>_FIELD_NUMBER = <number>.
if (!AddFieldNumberToClass(cls, field)) {
return NULL;
}
}
Py_RETURN_NONE;
}
PyObject* DeepCopy(CMessage* self, PyObject* arg) {
PyObject* clone = PyObject_CallObject(
reinterpret_cast<PyObject*>(Py_TYPE(self)), NULL);
@ -2214,8 +2376,9 @@ PyObject* DeepCopy(CMessage* self, PyObject* arg) {
Py_DECREF(clone);
return NULL;
}
if (MergeFrom(reinterpret_cast<CMessage*>(clone),
reinterpret_cast<PyObject*>(self)) == NULL) {
if (ScopedPyObjectPtr(MergeFrom(
reinterpret_cast<CMessage*>(clone),
reinterpret_cast<PyObject*>(self))) == NULL) {
Py_DECREF(clone);
return NULL;
}
@ -2281,7 +2444,7 @@ PyObject* SetState(CMessage* self, PyObject* state) {
if (serialized == NULL) {
return NULL;
}
if (ParseFromString(self, serialized) == NULL) {
if (ScopedPyObjectPtr(ParseFromString(self, serialized)) == NULL) {
return NULL;
}
Py_RETURN_NONE;
@ -2314,8 +2477,6 @@ static PyMethodDef Methods[] = {
"Inputs picklable representation of the message." },
{ "__unicode__", (PyCFunction)ToUnicode, METH_NOARGS,
"Outputs a unicode representation of the message." },
{ "AddDescriptors", (PyCFunction)AddDescriptors, METH_O | METH_CLASS,
"Adds field descriptors to the class" },
{ "ByteSize", (PyCFunction)ByteSize, METH_NOARGS,
"Returns the size of the message in bytes." },
{ "Clear", (PyCFunction)Clear, METH_NOARGS,
@ -2441,6 +2602,9 @@ PyObject* GetAttr(CMessage* self, PyObject* name) {
if (field_descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
PyObject* sub_message = InternalGetSubMessage(self, field_descriptor);
if (sub_message == NULL) {
return NULL;
}
if (!SetCompositeField(self, name, sub_message)) {
Py_DECREF(sub_message);
return NULL;
@ -2484,7 +2648,7 @@ int SetAttr(CMessage* self, PyObject* name, PyObject* value) {
} // namespace cmessage
PyTypeObject CMessage_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
PyVarObject_HEAD_INIT(&PyMessageMeta_Type, 0)
FULL_MODULE_NAME ".CMessage", // tp_name
sizeof(CMessage), // tp_basicsize
0, // tp_itemsize
@ -2497,7 +2661,7 @@ PyTypeObject CMessage_Type = {
0, // tp_as_number
0, // tp_as_sequence
0, // tp_as_mapping
0, // tp_hash
PyObject_HashNotImplemented, // tp_hash
0, // tp_call
(reprfunc)cmessage::ToStr, // tp_str
(getattrofunc)cmessage::GetAttr, // tp_getattro
@ -2580,8 +2744,9 @@ void InitGlobals() {
k_extensions_by_name = PyString_FromString("_extensions_by_name");
k_extensions_by_number = PyString_FromString("_extensions_by_number");
message_factory = new DynamicMessageFactory();
message_factory->SetDelegateToGeneratedFactory(true);
PyObject *dummy_obj = PySet_New(NULL);
kEmptyWeakref = PyWeakref_NewRef(dummy_obj, NULL);
Py_DECREF(dummy_obj);
}
bool InitProto2MessageModule(PyObject *m) {
@ -2598,7 +2763,13 @@ bool InitProto2MessageModule(PyObject *m) {
// Initialize constants defined in this file.
InitGlobals();
CMessage_Type.tp_hash = PyObject_HashNotImplemented;
PyMessageMeta_Type.tp_base = &PyType_Type;
if (PyType_Ready(&PyMessageMeta_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMeta",
reinterpret_cast<PyObject*>(&PyMessageMeta_Type));
if (PyType_Ready(&CMessage_Type) < 0) {
return false;
}
@ -2628,86 +2799,106 @@ bool InitProto2MessageModule(PyObject *m) {
PyModule_AddObject(m, "Message", reinterpret_cast<PyObject*>(&CMessage_Type));
RepeatedScalarContainer_Type.tp_hash =
PyObject_HashNotImplemented;
if (PyType_Ready(&RepeatedScalarContainer_Type) < 0) {
return false;
// Initialize Repeated container types.
{
if (PyType_Ready(&RepeatedScalarContainer_Type) < 0) {
return false;
}
PyModule_AddObject(m, "RepeatedScalarContainer",
reinterpret_cast<PyObject*>(
&RepeatedScalarContainer_Type));
if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) {
return false;
}
PyModule_AddObject(
m, "RepeatedCompositeContainer",
reinterpret_cast<PyObject*>(
&RepeatedCompositeContainer_Type));
// Register them as collections.Sequence
ScopedPyObjectPtr collections(PyImport_ImportModule("collections"));
if (collections == NULL) {
return false;
}
ScopedPyObjectPtr mutable_sequence(PyObject_GetAttrString(
collections, "MutableSequence"));
if (mutable_sequence == NULL) {
return false;
}
if (ScopedPyObjectPtr(PyObject_CallMethod(mutable_sequence, "register", "O",
&RepeatedScalarContainer_Type))
== NULL) {
return false;
}
if (ScopedPyObjectPtr(PyObject_CallMethod(mutable_sequence, "register", "O",
&RepeatedCompositeContainer_Type))
== NULL) {
return false;
}
}
PyModule_AddObject(m, "RepeatedScalarContainer",
reinterpret_cast<PyObject*>(
&RepeatedScalarContainer_Type));
// Initialize Map container types.
{
// ScalarMapContainer_Type derives from our MutableMapping type.
ScopedPyObjectPtr containers(PyImport_ImportModule(
"google.protobuf.internal.containers"));
if (containers == NULL) {
return false;
}
RepeatedCompositeContainer_Type.tp_hash = PyObject_HashNotImplemented;
if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) {
return false;
ScopedPyObjectPtr mutable_mapping(
PyObject_GetAttrString(containers, "MutableMapping"));
if (mutable_mapping == NULL) {
return false;
}
if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) {
return false;
}
Py_INCREF(mutable_mapping);
ScalarMapContainer_Type.tp_base =
reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
if (PyType_Ready(&ScalarMapContainer_Type) < 0) {
return false;
}
PyModule_AddObject(m, "ScalarMapContainer",
reinterpret_cast<PyObject*>(&ScalarMapContainer_Type));
if (PyType_Ready(&ScalarMapIterator_Type) < 0) {
return false;
}
PyModule_AddObject(m, "ScalarMapIterator",
reinterpret_cast<PyObject*>(&ScalarMapIterator_Type));
Py_INCREF(mutable_mapping);
MessageMapContainer_Type.tp_base =
reinterpret_cast<PyTypeObject*>(mutable_mapping.get());
if (PyType_Ready(&MessageMapContainer_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMapContainer",
reinterpret_cast<PyObject*>(&MessageMapContainer_Type));
if (PyType_Ready(&MessageMapIterator_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMapIterator",
reinterpret_cast<PyObject*>(&MessageMapIterator_Type));
}
PyModule_AddObject(
m, "RepeatedCompositeContainer",
reinterpret_cast<PyObject*>(
&RepeatedCompositeContainer_Type));
// ScalarMapContainer_Type derives from our MutableMapping type.
PyObject* containers =
PyImport_ImportModule("google.protobuf.internal.containers");
if (containers == NULL) {
return false;
}
PyObject* mutable_mapping =
PyObject_GetAttrString(containers, "MutableMapping");
Py_DECREF(containers);
if (mutable_mapping == NULL) {
return false;
}
if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) {
Py_DECREF(mutable_mapping);
return false;
}
ScalarMapContainer_Type.tp_base =
reinterpret_cast<PyTypeObject*>(mutable_mapping);
if (PyType_Ready(&ScalarMapContainer_Type) < 0) {
return false;
}
PyModule_AddObject(m, "ScalarMapContainer",
reinterpret_cast<PyObject*>(&ScalarMapContainer_Type));
if (PyType_Ready(&ScalarMapIterator_Type) < 0) {
return false;
}
PyModule_AddObject(m, "ScalarMapIterator",
reinterpret_cast<PyObject*>(&ScalarMapIterator_Type));
Py_INCREF(mutable_mapping);
MessageMapContainer_Type.tp_base =
reinterpret_cast<PyTypeObject*>(mutable_mapping);
if (PyType_Ready(&MessageMapContainer_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMapContainer",
reinterpret_cast<PyObject*>(&MessageMapContainer_Type));
if (PyType_Ready(&MessageMapIterator_Type) < 0) {
return false;
}
PyModule_AddObject(m, "MessageMapIterator",
reinterpret_cast<PyObject*>(&MessageMapIterator_Type));
ExtensionDict_Type.tp_hash = PyObject_HashNotImplemented;
if (PyType_Ready(&ExtensionDict_Type) < 0) {
return false;
}
PyModule_AddObject(
m, "ExtensionDict",
reinterpret_cast<PyObject*>(&ExtensionDict_Type));
@ -2751,6 +2942,7 @@ bool InitProto2MessageModule(PyObject *m) {
}
EncodeError_class = PyObject_GetAttrString(message_module, "EncodeError");
DecodeError_class = PyObject_GetAttrString(message_module, "DecodeError");
PythonMessage_class = PyObject_GetAttrString(message_module, "Message");
Py_DECREF(message_module);
PyObject* pickle_module = PyImport_ImportModule("pickle");

View File

@ -49,7 +49,6 @@ class Message;
class Reflection;
class FieldDescriptor;
class Descriptor;
class DynamicMessageFactory;
using internal::shared_ptr;
@ -221,9 +220,6 @@ PyObject* FindInitializationErrors(CMessage* self);
int SetOwner(CMessage* self, const shared_ptr<Message>& new_owner);
int AssureWritable(CMessage* self);
DynamicMessageFactory* GetMessageFactory();
} // namespace cmessage

View File

@ -32,6 +32,7 @@
#include <google/protobuf/pyext/message_map_container.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/message.h>
#include <google/protobuf/pyext/message.h>

View File

@ -38,11 +38,13 @@
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/message.h>
#include <google/protobuf/pyext/descriptor.h>
#include <google/protobuf/pyext/descriptor_pool.h>
#include <google/protobuf/pyext/message.h>
#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
@ -74,125 +76,6 @@ namespace repeated_composite_container {
GOOGLE_CHECK((self)->parent == NULL); \
} while (0);
// Returns a new reference.
static PyObject* GetKey(PyObject* x) {
// Just the identity function.
Py_INCREF(x);
return x;
}
#define GET_KEY(keyfunc, value) \
((keyfunc) == NULL ? \
GetKey((value)) : \
PyObject_CallFunctionObjArgs((keyfunc), (value), NULL))
// Converts a comparison function that returns -1, 0, or 1 into a
// less-than predicate.
//
// Returns -1 on error, 1 if x < y, 0 if x >= y.
static int islt(PyObject *x, PyObject *y, PyObject *compare) {
if (compare == NULL)
return PyObject_RichCompareBool(x, y, Py_LT);
ScopedPyObjectPtr res(PyObject_CallFunctionObjArgs(compare, x, y, NULL));
if (res == NULL)
return -1;
if (!PyInt_Check(res)) {
PyErr_Format(PyExc_TypeError,
"comparison function must return int, not %.200s",
Py_TYPE(res)->tp_name);
return -1;
}
return PyInt_AsLong(res) < 0;
}
// Copied from uarrsort.c but swaps memcpy swaps with protobuf/python swaps
// TODO(anuraag): Is there a better way to do this then reinventing the wheel?
static int InternalQuickSort(RepeatedCompositeContainer* self,
Py_ssize_t start,
Py_ssize_t limit,
PyObject* cmp,
PyObject* keyfunc) {
if (limit - start <= 1)
return 0; // Nothing to sort.
GOOGLE_CHECK_ATTACHED(self);
Message* message = self->message;
const Reflection* reflection = message->GetReflection();
const FieldDescriptor* descriptor = self->parent_field_descriptor;
Py_ssize_t left;
Py_ssize_t right;
PyObject* children = self->child_messages;
do {
left = start;
right = limit;
ScopedPyObjectPtr mid(
GET_KEY(keyfunc, PyList_GET_ITEM(children, (start + limit) / 2)));
do {
ScopedPyObjectPtr key(GET_KEY(keyfunc, PyList_GET_ITEM(children, left)));
int is_lt = islt(key, mid, cmp);
if (is_lt == -1)
return -1;
/* array[left]<x */
while (is_lt) {
++left;
ScopedPyObjectPtr key(GET_KEY(keyfunc,
PyList_GET_ITEM(children, left)));
is_lt = islt(key, mid, cmp);
if (is_lt == -1)
return -1;
}
key.reset(GET_KEY(keyfunc, PyList_GET_ITEM(children, right - 1)));
is_lt = islt(mid, key, cmp);
if (is_lt == -1)
return -1;
while (is_lt) {
--right;
ScopedPyObjectPtr key(GET_KEY(keyfunc,
PyList_GET_ITEM(children, right - 1)));
is_lt = islt(mid, key, cmp);
if (is_lt == -1)
return -1;
}
if (left < right) {
--right;
if (left < right) {
reflection->SwapElements(message, descriptor, left, right);
PyObject* tmp = PyList_GET_ITEM(children, left);
PyList_SET_ITEM(children, left, PyList_GET_ITEM(children, right));
PyList_SET_ITEM(children, right, tmp);
}
++left;
}
} while (left < right);
if ((right - start) < (limit - left)) {
/* sort [start..right[ */
if (start < (right - 1)) {
InternalQuickSort(self, start, right, cmp, keyfunc);
}
/* sort [left..limit[ */
start = left;
} else {
/* sort [left..limit[ */
if (left < (limit - 1)) {
InternalQuickSort(self, left, limit, cmp, keyfunc);
}
/* sort [start..right[ */
limit = right;
}
} while (start < (limit - 1));
return 0;
}
#undef GET_KEY
// ---------------------------------------------------------------------
// len()
@ -329,7 +212,7 @@ PyObject* Extend(RepeatedCompositeContainer* self, PyObject* value) {
return NULL;
}
CMessage* new_cmessage = reinterpret_cast<CMessage*>(new_message.get());
if (cmessage::MergeFrom(new_cmessage, next) == NULL) {
if (ScopedPyObjectPtr(cmessage::MergeFrom(new_cmessage, next)) == NULL) {
return NULL;
}
}
@ -455,58 +338,39 @@ static PyObject* RichCompare(RepeatedCompositeContainer* self,
// ---------------------------------------------------------------------
// sort()
static PyObject* SortAttached(RepeatedCompositeContainer* self,
PyObject* args,
PyObject* kwds) {
// Sort the underlying Message array.
PyObject *compare = NULL;
int reverse = 0;
PyObject *keyfunc = NULL;
static char *kwlist[] = {"cmp", "key", "reverse", 0};
if (args != NULL) {
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOi:sort",
kwlist, &compare, &keyfunc, &reverse))
return NULL;
}
if (compare == Py_None)
compare = NULL;
if (keyfunc == Py_None)
keyfunc = NULL;
static void ReorderAttached(RepeatedCompositeContainer* self) {
Message* message = self->message;
const Reflection* reflection = message->GetReflection();
const FieldDescriptor* descriptor = self->parent_field_descriptor;
const Py_ssize_t length = Length(self);
if (InternalQuickSort(self, 0, length, compare, keyfunc) < 0)
return NULL;
// Finally reverse the result if requested.
if (reverse) {
Message* message = self->message;
const Reflection* reflection = message->GetReflection();
const FieldDescriptor* descriptor = self->parent_field_descriptor;
// Since Python protobuf objects are never arena-allocated, adding and
// removing message pointers to the underlying array is just updating
// pointers.
for (Py_ssize_t i = 0; i < length; ++i)
reflection->ReleaseLast(message, descriptor);
// Reverse the Message array.
for (int i = 0; i < length / 2; ++i)
reflection->SwapElements(message, descriptor, i, length - i - 1);
// Reverse the Python list.
ScopedPyObjectPtr res(PyObject_CallMethod(self->child_messages,
"reverse", NULL));
if (res == NULL)
return NULL;
for (Py_ssize_t i = 0; i < length; ++i) {
CMessage* py_cmsg = reinterpret_cast<CMessage*>(
PyList_GET_ITEM(self->child_messages, i));
reflection->AddAllocatedMessage(message, descriptor, py_cmsg->message);
}
Py_RETURN_NONE;
}
static PyObject* SortReleased(RepeatedCompositeContainer* self,
PyObject* args,
PyObject* kwds) {
// Returns 0 if successful; returns -1 and sets an exception if
// unsuccessful.
static int SortPythonMessages(RepeatedCompositeContainer* self,
PyObject* args,
PyObject* kwds) {
ScopedPyObjectPtr m(PyObject_GetAttrString(self->child_messages, "sort"));
if (m == NULL)
return NULL;
return -1;
if (PyObject_Call(m, args, kwds) == NULL)
return NULL;
Py_RETURN_NONE;
return -1;
if (self->message != NULL) {
ReorderAttached(self);
}
return 0;
}
static PyObject* Sort(RepeatedCompositeContainer* self,
@ -527,11 +391,10 @@ static PyObject* Sort(RepeatedCompositeContainer* self,
if (UpdateChildMessages(self) < 0) {
return NULL;
}
if (self->message == NULL) {
return SortReleased(self, args, kwds);
} else {
return SortAttached(self, args, kwds);
if (SortPythonMessages(self, args, kwds) < 0) {
return NULL;
}
Py_RETURN_NONE;
}
// ---------------------------------------------------------------------
@ -584,18 +447,6 @@ void ReleaseLastTo(CMessage* parent,
parent->message->GetReflection()->ReleaseLast(parent->message, field));
// TODO(tibell): Deal with proto1.
// ReleaseMessage will return NULL which differs from
// child_cmessage->message, if the field does not exist. In this case,
// the latter points to the default instance via a const_cast<>, so we
// have to reset it to a new mutable object since we are taking ownership.
if (released_message.get() == NULL) {
const Message* prototype =
cmessage::GetMessageFactory()->GetPrototype(
target->message->GetDescriptor());
GOOGLE_CHECK_NOTNULL(prototype);
released_message.reset(prototype->New());
}
target->parent = NULL;
target->parent_field_descriptor = NULL;
target->message = released_message.get();
@ -732,7 +583,7 @@ PyTypeObject RepeatedCompositeContainer_Type = {
0, // tp_as_number
&repeated_composite_container::SqMethods, // tp_as_sequence
&repeated_composite_container::MpMethods, // tp_as_mapping
0, // tp_hash
PyObject_HashNotImplemented, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro

View File

@ -39,10 +39,12 @@
#endif
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/message.h>
#include <google/protobuf/pyext/descriptor.h>
#include <google/protobuf/pyext/descriptor_pool.h>
#include <google/protobuf/pyext/message.h>
#include <google/protobuf/pyext/scoped_pyobject_ptr.h>
@ -68,7 +70,7 @@ static int InternalAssignRepeatedField(
self->parent_field_descriptor);
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(list); ++i) {
PyObject* value = PyList_GET_ITEM(list, i);
if (Append(self, value) == NULL) {
if (ScopedPyObjectPtr(Append(self, value)) == NULL) {
return -1;
}
}
@ -510,7 +512,7 @@ PyObject* Extend(RepeatedScalarContainer* self, PyObject* value) {
}
ScopedPyObjectPtr next;
while ((next.reset(PyIter_Next(iter))) != NULL) {
if (Append(self, next) == NULL) {
if (ScopedPyObjectPtr(Append(self, next)) == NULL) {
return NULL;
}
}
@ -690,8 +692,7 @@ static int InitializeAndCopyToParentContainer(
if (values == NULL) {
return -1;
}
Message* new_message = cmessage::GetMessageFactory()->GetPrototype(
from->message->GetDescriptor())->New();
Message* new_message = from->message->New();
to->parent = NULL;
to->parent_field_descriptor = from->parent_field_descriptor;
to->message = new_message;
@ -781,7 +782,7 @@ PyTypeObject RepeatedScalarContainer_Type = {
0, // tp_as_number
&repeated_scalar_container::SqMethods, // tp_as_sequence
&repeated_scalar_container::MpMethods, // tp_as_mapping
0, // tp_hash
PyObject_HashNotImplemented, // tp_hash
0, // tp_call
0, // tp_str
0, // tp_getattro

View File

@ -32,6 +32,7 @@
#include <google/protobuf/pyext/scalar_map_container.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/message.h>
#include <google/protobuf/pyext/message.h>

View File

@ -51,16 +51,22 @@ class ScopedPyObjectPtr {
// Reset. Deletes the current owned object, if any.
// Then takes ownership of a new object, if given.
// this->reset(this->get()) works.
// This function must be called with a reference that you own.
// this->reset(this->get()) is wrong!
// this->reset(this->release()) is OK.
PyObject* reset(PyObject* p = NULL) {
if (p != ptr_) {
Py_XDECREF(ptr_);
ptr_ = p;
}
Py_XDECREF(ptr_);
ptr_ = p;
return ptr_;
}
// ScopedPyObjectPtr should not be copied.
// We explicitly list and delete this overload to avoid automatic conversion
// to PyObject*, which is wrong in this case.
PyObject* reset(const ScopedPyObjectPtr& other) = delete;
// Releases ownership of the object.
// The caller now owns the returned reference.
PyObject* release() {
PyObject* p = ptr_;
ptr_ = NULL;

View File

@ -49,101 +49,23 @@ __author__ = 'robinson@google.com (Will Robinson)'
from google.protobuf.internal import api_implementation
from google.protobuf import descriptor as descriptor_mod
from google.protobuf import message
_FieldDescriptor = descriptor_mod.FieldDescriptor
if api_implementation.Type() == 'cpp':
from google.protobuf.pyext import cpp_message as message_impl
else:
from google.protobuf.internal import python_message as message_impl
_NewMessage = message_impl.NewMessage
_InitMessage = message_impl.InitMessage
class GeneratedProtocolMessageType(type):
"""Metaclass for protocol message classes created at runtime from Descriptors.
We add implementations for all methods described in the Message class. We
also create properties to allow getting/setting all fields in the protocol
message. Finally, we create slots to prevent users from accidentally
"setting" nonexistent fields in the protocol message, which then wouldn't get
serialized / deserialized properly.
The protocol compiler currently uses this metaclass to create protocol
message classes at runtime. Clients can also manually create their own
classes at runtime, as in this example:
mydescriptor = Descriptor(.....)
class MyProtoClass(Message):
__metaclass__ = GeneratedProtocolMessageType
DESCRIPTOR = mydescriptor
myproto_instance = MyProtoClass()
myproto.foo_field = 23
...
The above example will not work for nested types. If you wish to include them,
use reflection.MakeClass() instead of manually instantiating the class in
order to create the appropriate class structure.
"""
# Must be consistent with the protocol-compiler code in
# proto2/compiler/internal/generator.*.
_DESCRIPTOR_KEY = 'DESCRIPTOR'
def __new__(cls, name, bases, dictionary):
"""Custom allocation for runtime-generated class types.
We override __new__ because this is apparently the only place
where we can meaningfully set __slots__ on the class we're creating(?).
(The interplay between metaclasses and slots is not very well-documented).
Args:
name: Name of the class (ignored, but required by the
metaclass protocol).
bases: Base classes of the class we're constructing.
(Should be message.Message). We ignore this field, but
it's required by the metaclass protocol
dictionary: The class dictionary of the class we're
constructing. dictionary[_DESCRIPTOR_KEY] must contain
a Descriptor object describing this protocol message
type.
Returns:
Newly-allocated class.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
bases = _NewMessage(bases, descriptor, dictionary)
superclass = super(GeneratedProtocolMessageType, cls)
new_class = superclass.__new__(cls, name, bases, dictionary)
return new_class
def __init__(cls, name, bases, dictionary):
"""Here we perform the majority of our work on the class.
We add enum getters, an __init__ method, implementations
of all Message methods, and properties for all fields
in the protocol type.
Args:
name: Name of the class (ignored, but required by the
metaclass protocol).
bases: Base classes of the class we're constructing.
(Should be message.Message). We ignore this field, but
it's required by the metaclass protocol
dictionary: The class dictionary of the class we're
constructing. dictionary[_DESCRIPTOR_KEY] must contain
a Descriptor object describing this protocol message
type.
"""
descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY]
_InitMessage(descriptor, cls)
superclass = super(GeneratedProtocolMessageType, cls)
superclass.__init__(name, bases, dictionary)
# The type of all Message classes.
# Part of the public interface.
#
# Used by generated files, but clients can also use it at runtime:
# mydescriptor = pool.FindDescriptor(.....)
# class MyProtoClass(Message):
# __metaclass__ = GeneratedProtocolMessageType
# DESCRIPTOR = mydescriptor
GeneratedProtocolMessageType = message_impl.GeneratedProtocolMessageType
def ParseMessage(descriptor, byte_str):

View File

@ -113,7 +113,7 @@ def PrintMessage(message, out, indent=0, as_utf8=False, as_one_line=False,
fields.sort(key=lambda x: x[0].index)
for field, value in fields:
if _IsMapEntry(field):
for key in value:
for key in sorted(value):
# This is slow for maps with submessage entires because it copies the
# entire tree. Unfortunately this would take significant refactoring
# of this file to work around.

View File

@ -50,7 +50,7 @@ AnyMetadata::AnyMetadata(UrlType* type_url, ValueType* value)
void AnyMetadata::PackFrom(const Message& message) {
type_url_->SetNoArena(&::google::protobuf::internal::GetEmptyString(),
GetTypeUrl(message.GetDescriptor()));
GetTypeUrl(message.GetDescriptor()));
message.SerializeToString(value_->MutableNoArena(
&::google::protobuf::internal::GetEmptyStringAlreadyInited()));
}
@ -76,7 +76,7 @@ bool ParseAnyTypeUrl(const string& type_url, string* full_type_name) {
type_url.size() - prefix_len);
return true;
}
return true;
return false;
}

View File

@ -34,9 +34,9 @@
#include <string>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arenastring.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include <google/protobuf/arenastring.h>
namespace google {
namespace protobuf {

View File

@ -66,8 +66,12 @@ void Arena::Init() {
first_block->size = options_.initial_block_size;
first_block->pos = kHeaderSize;
first_block->next = NULL;
first_block->owner = &first_block->owner;
AddBlock(first_block);
// Thread which calls Init() owns the first block. This allows the
// single-threaded case to allocate on the first block without taking any
// locks.
first_block->owner = &thread_cache();
SetThreadCacheBlock(first_block);
AddBlockInternal(first_block);
owns_first_block_ = false;
}
@ -80,7 +84,7 @@ void Arena::Init() {
}
Arena::~Arena() {
uint64 space_allocated = Reset();
uint64 space_allocated = ResetInternal();
// Call the destruction hook
if (options_.on_arena_destruction != NULL) {
@ -89,10 +93,14 @@ Arena::~Arena() {
}
uint64 Arena::Reset() {
CleanupList();
uint64 space_allocated = FreeBlocks();
// Invalidate any ThreadCaches pointing to any blocks we just destroyed.
lifecycle_id_ = lifecycle_id_generator_.GetNext();
return ResetInternal();
}
uint64 Arena::ResetInternal() {
CleanupList();
uint64 space_allocated = FreeBlocks();
// Call the reset hook
if (options_.on_arena_reset != NULL) {
@ -137,6 +145,10 @@ Arena::Block* Arena::NewBlock(void* me, Block* my_last_block, size_t n,
void Arena::AddBlock(Block* b) {
MutexLock l(&blocks_lock_);
AddBlockInternal(b);
}
void Arena::AddBlockInternal(Block* b) {
b->next = reinterpret_cast<Block*>(google::protobuf::internal::NoBarrier_Load(&blocks_));
google::protobuf::internal::Release_Store(&blocks_, reinterpret_cast<google::protobuf::internal::AtomicWord>(b));
if (b->avail() != 0) {
@ -181,16 +193,6 @@ void* Arena::AllocateAligned(const std::type_info* allocated, size_t n) {
void* me = &thread_cache();
Block* b = reinterpret_cast<Block*>(google::protobuf::internal::Acquire_Load(&hint_));
if (!b || b->owner != me || b->avail() < n) {
// If the next block to allocate from is the first block, try to claim it
// for this thread.
if (!owns_first_block_ && b->next == NULL) {
MutexLock l(&blocks_lock_);
if (b->owner == &b->owner && b->avail() >= n) {
b->owner = me;
SetThreadCacheBlock(b);
return AllocFromBlock(b, n);
}
}
return SlowAlloc(n);
}
return AllocFromBlock(b, n);
@ -267,8 +269,12 @@ uint64 Arena::FreeBlocks() {
// Make the first block that was passed in through ArenaOptions
// available for reuse.
first_block->pos = kHeaderSize;
first_block->owner = &first_block->owner;
AddBlock(first_block);
// Thread which calls Reset() owns the first block. This allows the
// single-threaded case to allocate on the first block without taking any
// locks.
first_block->owner = &thread_cache();
SetThreadCacheBlock(first_block);
AddBlockInternal(first_block);
}
return space_allocated;
}

View File

@ -31,6 +31,7 @@
#ifndef GOOGLE_PROTOBUF_ARENA_H__
#define GOOGLE_PROTOBUF_ARENA_H__
#include <limits>
#if __cplusplus >= 201103L
#include <google/protobuf/stubs/type_traits.h>
#endif
@ -39,7 +40,8 @@
#include <google/protobuf/stubs/atomic_sequence_num.h>
#include <google/protobuf/stubs/atomicops.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/platform_macros.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/mutex.h>
#include <google/protobuf/stubs/type_traits.h>
namespace google {
@ -414,6 +416,9 @@ class LIBPROTOBUF_EXPORT Arena {
// trivially destructible.
template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
static T* CreateArray(::google::protobuf::Arena* arena, size_t num_elements) {
GOOGLE_CHECK_LE(num_elements,
std::numeric_limits<size_t>::max() / sizeof(T))
<< "Requested size is too large to fit into size_t.";
if (arena == NULL) {
return static_cast<T*>(::operator new[](num_elements * sizeof(T)));
} else {
@ -425,16 +430,16 @@ class LIBPROTOBUF_EXPORT Arena {
// of the underlying blocks. The total space used may not include the new
// blocks that are allocated by this arena from other threads concurrently
// with the call to this method.
uint64 SpaceAllocated() const GOOGLE_ATTRIBUTE_NOINLINE;
GOOGLE_ATTRIBUTE_NOINLINE uint64 SpaceAllocated() const;
// As above, but does not include any free space in underlying blocks.
uint64 SpaceUsed() const GOOGLE_ATTRIBUTE_NOINLINE;
GOOGLE_ATTRIBUTE_NOINLINE uint64 SpaceUsed() const;
// Frees all storage allocated by this arena after calling destructors
// registered with OwnDestructor() and freeing objects registered with Own().
// Any objects allocated on this arena are unusable after this call. It also
// returns the total space used by the arena which is the sums of the sizes
// of the allocated blocks. This method is not thread-safe.
uint64 Reset() GOOGLE_ATTRIBUTE_NOINLINE;
GOOGLE_ATTRIBUTE_NOINLINE uint64 Reset();
// Adds |object| to a list of heap-allocated objects to be freed with |delete|
// when the arena is destroyed or reset.
@ -459,8 +464,8 @@ class LIBPROTOBUF_EXPORT Arena {
// will be manually called when the arena is destroyed or reset. This differs
// from OwnDestructor() in that any member function may be specified, not only
// the class destructor.
void OwnCustomDestructor(void* object, void (*destruct)(void*))
GOOGLE_ATTRIBUTE_NOINLINE {
GOOGLE_ATTRIBUTE_NOINLINE void OwnCustomDestructor(void* object,
void (*destruct)(void*)) {
AddListNode(object, destruct);
}
@ -469,7 +474,7 @@ class LIBPROTOBUF_EXPORT Arena {
// latter is a virtual call, while this method is a templated call that
// resolves at compile-time.
template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
static inline ::google::protobuf::Arena* GetArena(const T* value) {
static ::google::protobuf::Arena* GetArena(const T* value) {
return GetArenaInternal(value, static_cast<T*>(0));
}
@ -507,7 +512,7 @@ class LIBPROTOBUF_EXPORT Arena {
// aligned at a multiple of 8 bytes.
size_t pos;
size_t size; // total size of the block.
size_t avail() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { return size - pos; }
GOOGLE_ATTRIBUTE_ALWAYS_INLINE size_t avail() const { return size - pos; }
// data follows
};
@ -555,6 +560,33 @@ class LIBPROTOBUF_EXPORT Arena {
return google::protobuf::internal::has_trivial_destructor<T>::value;
}
// Helper typetrait that indicates whether the desctructor of type T should be
// called when arena is destroyed at compile time. This is only to allow
// construction of higher-level templated utilities.
// is_destructor_skippable<T>::value is an instance of google::protobuf::internal::true_type if the
// destructor of the message type T should not be called when arena is
// destroyed or google::protobuf::internal::has_trivial_destructor<T>::value == true, and
// google::protobuf::internal::false_type otherwise.
//
// This is inside Arena because only Arena has the friend relationships
// necessary to see the underlying generated code traits.
template<typename T>
struct is_destructor_skippable {
template<typename U>
static char DestructorSkippable(
const typename U::DestructorSkippable_*);
template<typename U>
static double DestructorSkippable(...);
// This will resolve to either google::protobuf::internal::true_type or google::protobuf::internal::false_type.
typedef google::protobuf::internal::integral_constant<bool,
sizeof(DestructorSkippable<const T>(static_cast<const T*>(0))) ==
sizeof(char) || google::protobuf::internal::has_trivial_destructor<T>::value == true>
type;
static const type value;
};
// CreateMessage<T> requires that T supports arenas, but this private method
// works whether or not T supports arenas. These are not exposed to user code
// as it can cause confusing API usages, and end up having double free in
@ -574,14 +606,16 @@ class LIBPROTOBUF_EXPORT Arena {
// Just allocate the required size for the given type assuming the
// type has a trivial constructor.
template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateInternalRawArray(size_t num_elements) {
T* CreateInternalRawArray(size_t num_elements) {
GOOGLE_CHECK_LE(num_elements,
std::numeric_limits<size_t>::max() / sizeof(T))
<< "Requested size is too large to fit into size_t.";
return static_cast<T*>(
AllocateAligned(RTTI_TYPE_ID(T), sizeof(T) * num_elements));
}
template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateInternal(
bool skip_explicit_ownership) {
T* CreateInternal(bool skip_explicit_ownership) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T();
if (!skip_explicit_ownership) {
AddListNode(t, &internal::arena_destruct_object<T>);
@ -590,8 +624,7 @@ class LIBPROTOBUF_EXPORT Arena {
}
template <typename T, typename Arg> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateInternal(
bool skip_explicit_ownership, const Arg& arg) {
T* CreateInternal(bool skip_explicit_ownership, const Arg& arg) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg);
if (!skip_explicit_ownership) {
AddListNode(t, &internal::arena_destruct_object<T>);
@ -600,7 +633,7 @@ class LIBPROTOBUF_EXPORT Arena {
}
template <typename T, typename Arg1, typename Arg2> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateInternal(
T* CreateInternal(
bool skip_explicit_ownership, const Arg1& arg1, const Arg2& arg2) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T))) T(arg1, arg2);
if (!skip_explicit_ownership) {
@ -610,10 +643,10 @@ class LIBPROTOBUF_EXPORT Arena {
}
template <typename T, typename Arg1, typename Arg2, typename Arg3>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3);
if (!skip_explicit_ownership) {
@ -624,11 +657,11 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T, typename Arg1, typename Arg2, typename Arg3,
typename Arg4>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3, arg4);
if (!skip_explicit_ownership) {
@ -639,12 +672,12 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3, arg4, arg5);
if (!skip_explicit_ownership) {
@ -655,13 +688,13 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5, typename Arg6>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3, arg4, arg5, arg6);
if (!skip_explicit_ownership) {
@ -672,14 +705,14 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5, typename Arg6, typename Arg7>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6,
const Arg7& arg7) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6,
const Arg7& arg7) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
if (!skip_explicit_ownership) {
@ -691,15 +724,15 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T, typename Arg1, typename Arg2, typename Arg3,
typename Arg4, typename Arg5, typename Arg6, typename Arg7,
typename Arg8>
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6,
const Arg7& arg7,
const Arg8& arg8) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE T* CreateInternal(bool skip_explicit_ownership,
const Arg1& arg1,
const Arg2& arg2,
const Arg3& arg3,
const Arg4& arg4,
const Arg5& arg5,
const Arg6& arg6,
const Arg7& arg7,
const Arg8& arg8) {
T* t = new (AllocateAligned(RTTI_TYPE_ID(T), sizeof(T)))
T(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
if (!skip_explicit_ownership) {
@ -709,21 +742,21 @@ class LIBPROTOBUF_EXPORT Arena {
}
template <typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*) {
T* CreateMessageInternal(typename T::InternalArenaConstructable_*) {
return CreateInternal<T, Arena*>(SkipDeleteList<T>(static_cast<T*>(0)),
this);
}
template <typename T, typename Arg> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*,
const Arg& arg) {
T* CreateMessageInternal(typename T::InternalArenaConstructable_*,
const Arg& arg) {
return CreateInternal<T, Arena*>(SkipDeleteList<T>(static_cast<T*>(0)),
this, arg);
}
template <typename T, typename Arg1, typename Arg2> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*,
const Arg1& arg1, const Arg2& arg2) {
T* CreateMessageInternal(typename T::InternalArenaConstructable_*,
const Arg1& arg1, const Arg2& arg2) {
return CreateInternal<T, Arena*>(SkipDeleteList<T>(static_cast<T*>(0)),
this, arg1, arg2);
}
@ -734,19 +767,29 @@ class LIBPROTOBUF_EXPORT Arena {
template <typename T>
static void CreateInArenaStorage(T* ptr, Arena* arena) {
CreateInArenaStorageInternal(ptr, arena, is_arena_constructable<T>::value);
RegisterDestructorInternal(ptr, arena, is_destructor_skippable<T>::value);
}
template <typename T>
static void CreateInArenaStorageInternal(
T* ptr, Arena* arena, google::protobuf::internal::true_type) {
new (ptr) T(arena);
}
template <typename T>
static void CreateInArenaStorageInternal(
T* ptr, Arena* arena, google::protobuf::internal::false_type) {
new (ptr) T;
}
template <typename T>
static void RegisterDestructorInternal(
T* ptr, Arena* arena, google::protobuf::internal::true_type) {}
template <typename T>
static void RegisterDestructorInternal(
T* ptr, Arena* arena, google::protobuf::internal::false_type) {
arena->OwnDestructor(ptr);
}
// These implement Own(), which registers an object for deletion (destructor
// call and operator delete()). The second parameter has type 'true_type' if T
// is a subtype of ::google::protobuf::Message and 'false_type' otherwise. Collapsing
@ -769,13 +812,13 @@ class LIBPROTOBUF_EXPORT Arena {
// InternalArenaConstructable_ tags can be associated with an arena, and such
// objects must implement a GetArenaNoVirtual() method.
template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
static inline ::google::protobuf::Arena* GetArenaInternal(const T* value,
typename T::InternalArenaConstructable_*) {
static ::google::protobuf::Arena* GetArenaInternal(
const T* value, typename T::InternalArenaConstructable_*) {
return value->GetArenaNoVirtual();
}
template<typename T> GOOGLE_ATTRIBUTE_ALWAYS_INLINE
static inline ::google::protobuf::Arena* GetArenaInternal(const T* value, ...) {
static ::google::protobuf::Arena* GetArenaInternal(const T* value, ...) {
return NULL;
}
@ -785,7 +828,7 @@ class LIBPROTOBUF_EXPORT Arena {
void* AllocateAligned(const std::type_info* allocated, size_t n);
// Allocate an internal allocation, avoiding optional typed monitoring.
GOOGLE_ATTRIBUTE_ALWAYS_INLINE inline void* AllocateAligned(size_t n) {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE void* AllocateAligned(size_t n) {
return AllocateAligned(NULL, n);
}
@ -803,6 +846,7 @@ class LIBPROTOBUF_EXPORT Arena {
void AddListNode(void* elem, void (*cleanup)(void*));
// Delete or Destruct all objects owned by the arena.
void CleanupList();
uint64 ResetInternal();
inline void SetThreadCacheBlock(Block* block) {
thread_cache().last_block_used_ = block;
@ -829,6 +873,9 @@ class LIBPROTOBUF_EXPORT Arena {
Mutex blocks_lock_;
void AddBlock(Block* b);
// Access must be synchronized, either by blocks_lock_ or by being called from
// Init()/Reset().
void AddBlockInternal(Block* b);
void* SlowAlloc(size_t n);
Block* FindBlock(void* me);
Block* NewBlock(void* me, Block* my_last_block, size_t n,
@ -854,6 +901,11 @@ const typename Arena::is_arena_constructable<T>::type
Arena::is_arena_constructable<T>::value =
typename Arena::is_arena_constructable<T>::type();
template<typename T>
const typename Arena::is_destructor_skippable<T>::type
Arena::is_destructor_skippable<T>::value =
typename Arena::is_destructor_skippable<T>::type();
} // namespace protobuf
} // namespace google

View File

@ -28,6 +28,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/arena_test_util.h>

View File

@ -40,7 +40,9 @@
#include <typeinfo>
#include <vector>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/scoped_ptr.h>
#include <google/protobuf/arena_test_util.h>
#include <google/protobuf/test_util.h>
#include <google/protobuf/unittest.pb.h>
@ -619,8 +621,6 @@ TEST(ArenaTest, RepeatedPtrFieldAddClearedTest) {
}
}
// N.B.: no reflection version of this test because all the arena-specific code
// is in RepeatedPtrField, and the reflection works implicitly based on that.
TEST(ArenaTest, AddAllocatedToRepeatedField) {
// Heap->arena case.
Arena arena1;
@ -680,6 +680,55 @@ TEST(ArenaTest, AddAllocatedToRepeatedField) {
}
}
TEST(ArenaTest, AddAllocatedToRepeatedFieldViaReflection) {
// Heap->arena case.
Arena arena1;
TestAllTypes* arena1_message = Arena::CreateMessage<TestAllTypes>(&arena1);
const Reflection* r = arena1_message->GetReflection();
const Descriptor* d = arena1_message->GetDescriptor();
const FieldDescriptor* fd =
d->FindFieldByName("repeated_nested_message");
for (int i = 0; i < 10; i++) {
TestAllTypes::NestedMessage* heap_submessage =
new TestAllTypes::NestedMessage;
heap_submessage->set_bb(42);
r->AddAllocatedMessage(arena1_message, fd, heap_submessage);
// Should not copy object -- will use arena_->Own().
EXPECT_EQ(heap_submessage,
&arena1_message->repeated_nested_message(i));
EXPECT_EQ(42, arena1_message->repeated_nested_message(i).bb());
}
// Arena1->Arena2 case.
arena1_message->Clear();
for (int i = 0; i < 10; i++) {
Arena arena2;
TestAllTypes::NestedMessage* arena2_submessage =
Arena::CreateMessage<TestAllTypes::NestedMessage>(&arena2);
arena2_submessage->set_bb(42);
r->AddAllocatedMessage(arena1_message, fd, arena2_submessage);
// Should copy object.
EXPECT_NE(arena2_submessage,
&arena1_message->repeated_nested_message(i));
EXPECT_EQ(42, arena1_message->repeated_nested_message(i).bb());
}
// Arena->heap case.
TestAllTypes* heap_message = new TestAllTypes;
for (int i = 0; i < 10; i++) {
Arena arena2;
TestAllTypes::NestedMessage* arena2_submessage =
Arena::CreateMessage<TestAllTypes::NestedMessage>(&arena2);
arena2_submessage->set_bb(42);
r->AddAllocatedMessage(heap_message, fd, arena2_submessage);
// Should copy object.
EXPECT_NE(arena2_submessage,
&heap_message->repeated_nested_message(i));
EXPECT_EQ(42, heap_message->repeated_nested_message(i).bb());
}
delete heap_message;
}
TEST(ArenaTest, ReleaseLastRepeatedField) {
// Release from arena-allocated repeated field and ensure that returned object
// is heap-allocated.
@ -1230,7 +1279,7 @@ TEST(ArenaTest, ArenaHooksSanity) {
EXPECT_EQ(1, ArenaHooksTestUtil::num_init);
EXPECT_EQ(0, ArenaHooksTestUtil::num_allocations);
::google::protobuf::Arena::Create<uint64>(&arena);
if (::google::protobuf::internal::has_trivial_destructor<uint64>::value) {
if (google::protobuf::internal::has_trivial_destructor<uint64>::value) {
EXPECT_EQ(1, ArenaHooksTestUtil::num_allocations);
} else {
EXPECT_EQ(2, ArenaHooksTestUtil::num_allocations);

View File

@ -33,6 +33,7 @@
#include <string>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/fastmem.h>
@ -145,7 +146,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
// Swaps internal pointers. Arena-safety semantics: this is guarded by the
// logic in Swap()/UnsafeArenaSwap() at the message level, so this method is
// 'unsafe' if called directly.
inline void Swap(ArenaStringPtr* other) GOOGLE_ATTRIBUTE_ALWAYS_INLINE {
GOOGLE_ATTRIBUTE_ALWAYS_INLINE void Swap(ArenaStringPtr* other) {
std::swap(ptr_, other->ptr_);
}
@ -283,9 +284,8 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
private:
::std::string* ptr_;
inline void CreateInstance(::google::protobuf::Arena* arena,
const ::std::string* initial_value)
GOOGLE_ATTRIBUTE_NOINLINE {
GOOGLE_ATTRIBUTE_NOINLINE void CreateInstance(::google::protobuf::Arena* arena,
const ::std::string* initial_value) {
// Assumes ptr_ is not NULL.
if (initial_value != NULL) {
ptr_ = new ::std::string(*initial_value);
@ -296,8 +296,7 @@ struct LIBPROTOBUF_EXPORT ArenaStringPtr {
arena->Own(ptr_);
}
}
inline void CreateInstanceNoArena(const ::std::string* initial_value)
GOOGLE_ATTRIBUTE_NOINLINE {
GOOGLE_ATTRIBUTE_NOINLINE void CreateInstanceNoArena(const ::std::string* initial_value) {
if (initial_value != NULL) {
ptr_ = new ::std::string(*initial_value);
} else {

View File

@ -42,6 +42,7 @@
#endif
#include <cstdlib>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <gtest/gtest.h>

View File

@ -34,6 +34,7 @@
#include <google/protobuf/compiler/code_generator.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>

View File

@ -70,6 +70,7 @@
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/stubs/map_util.h>
@ -1657,7 +1658,11 @@ bool CommandLineInterface::WriteDescriptorSet(
&already_seen, file_set.mutable_file());
}
} else {
set<const FileDescriptor*> already_seen;
for (int i = 0; i < parsed_files.size(); i++) {
if (!already_seen.insert(parsed_files[i]).second) {
continue;
}
FileDescriptorProto* file_proto = file_set.add_file();
parsed_files[i]->CopyTo(file_proto);
if (source_info_in_descriptor_set_) {

View File

@ -886,6 +886,39 @@ TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) {
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
}
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithDuplicates) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"
"message Foo {}\n");
CreateTempFile("bar.proto",
"syntax = \"proto2\";\n"
"import \"foo.proto\";\n"
"message Bar {\n"
" optional Foo foo = 1;\n"
"}\n");
CreateTempFile("baz.proto",
"syntax = \"proto2\";\n"
"import \"foo.proto\";\n"
"message Baz {\n"
" optional Foo foo = 1;\n"
"}\n");
Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set "
"--proto_path=$tmpdir bar.proto foo.proto bar.proto baz.proto");
ExpectNoErrors();
FileDescriptorSet descriptor_set;
ReadDescriptorSet("descriptor_set", &descriptor_set);
if (HasFatalFailure()) return;
EXPECT_EQ(3, descriptor_set.file_size());
EXPECT_EQ("bar.proto", descriptor_set.file(0).name());
EXPECT_EQ("foo.proto", descriptor_set.file(1).name());
EXPECT_EQ("baz.proto", descriptor_set.file(2).name());
// Descriptor set should not have source code info.
EXPECT_FALSE(descriptor_set.file(0).has_source_code_info());
}
TEST_F(CommandLineInterfaceTest, WriteDescriptorSetWithSourceInfo) {
CreateTempFile("foo.proto",
"syntax = \"proto2\";\n"

View File

@ -133,8 +133,7 @@ TEST(BootstrapTest, GeneratedDescriptorMatches) {
CppGenerator generator;
MockGeneratorContext context;
string error;
string parameter;
parameter = "dllexport_decl=LIBPROTOBUF_EXPORT";
string parameter = "dllexport_decl=LIBPROTOBUF_EXPORT";
ASSERT_TRUE(generator.Generate(proto_file, parameter,
&context, &error));
parameter = "dllexport_decl=LIBPROTOC_EXPORT";

View File

@ -32,7 +32,6 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <set>
#include <map>
#include <google/protobuf/compiler/cpp/cpp_enum.h>
@ -70,14 +69,11 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor,
EnumGenerator::~EnumGenerator() {}
void EnumGenerator::GenerateForwardDeclaration(io::Printer* printer) {
void EnumGenerator::FillForwardDeclaration(set<string>* enum_names) {
if (!options_.proto_h) {
return;
}
map<string, string> vars;
vars["classname"] = classname_;
printer->Print(vars, "enum $classname$ : int;\n");
printer->Print(vars, "bool $classname$_IsValid(int value);\n");
enum_names->insert(classname_);
}
void EnumGenerator::GenerateDefinition(io::Printer* printer) {

View File

@ -35,6 +35,7 @@
#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__
#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__
#include <set>
#include <string>
#include <google/protobuf/compiler/cpp/cpp_options.h>
#include <google/protobuf/descriptor.h>
@ -60,11 +61,11 @@ class EnumGenerator {
// Header stuff.
// Generate header code to forward-declare the enum. This is for use when
// Fills the name to use when declaring the enum. This is for use when
// generating other .proto.h files. This code should be placed within the
// enum's package namespace, but NOT within any class, even for nested
// enums.
void GenerateForwardDeclaration(io::Printer* printer);
void FillForwardDeclaration(set<string>* enum_names);
// Generate header code defining the enum. This code should be placed
// within the enum's package namespace, but NOT within any class, even for

View File

@ -35,7 +35,6 @@
#include <google/protobuf/compiler/cpp/cpp_enum_field.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {

View File

@ -47,6 +47,7 @@
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>

View File

@ -33,6 +33,7 @@
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/cpp/cpp_file.h>
#include <map>
#include <memory>
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
@ -93,22 +94,36 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
FileGenerator::~FileGenerator() {}
void FileGenerator::GenerateHeader(io::Printer* printer) {
GenerateTopHeaderGuard(printer);
void FileGenerator::GenerateProtoHeader(io::Printer* printer) {
if (!options_.proto_h) {
return;
}
string filename_identifier = FilenameIdentifier(file_->name());
GenerateTopHeaderGuard(printer, filename_identifier);
GenerateLibraryIncludes(printer);
GenerateDependencyIncludes(printer);
for (int i = 0; i < file_->public_dependency_count(); i++) {
const FileDescriptor* dep = file_->public_dependency(i);
const char* extension = ".proto.h";
string dependency = StripProto(dep->name()) + extension;
printer->Print(
"#include \"$dependency$\" // IWYU pragma: export\n",
"dependency", dependency);
}
printer->Print(
"// @@protoc_insertion_point(includes)\n");
GenerateForwardDeclarations(printer);
// Open namespace.
GenerateNamespaceOpeners(printer);
GenerateGlobalStateFunctionDeclarations(printer);
GenerateMessageForwardDeclarations(printer);
printer->Print("\n");
@ -133,6 +148,11 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
GenerateInlineFunctionDefinitions(printer);
printer->Print(
"\n"
"// @@protoc_insertion_point(namespace_scope)\n"
"\n");
// Close up namespace.
GenerateNamespaceClosers(printer);
@ -144,19 +164,89 @@ void FileGenerator::GenerateHeader(io::Printer* printer) {
"// @@protoc_insertion_point(global_scope)\n"
"\n");
GenerateBottomHeaderGuard(printer);
GenerateBottomHeaderGuard(printer, filename_identifier);
}
void FileGenerator::GeneratePBHeader(io::Printer* printer) {
string filename_identifier =
FilenameIdentifier(file_->name() + (options_.proto_h ? ".pb.h" : ""));
GenerateTopHeaderGuard(printer, filename_identifier);
if (options_.proto_h) {
printer->Print("#include \"$basename$.proto.h\" // IWYU pragma: export\n",
"basename", StripProto(file_->name()));
} else {
GenerateLibraryIncludes(printer);
}
GenerateDependencyIncludes(printer);
printer->Print(
"// @@protoc_insertion_point(includes)\n");
// Open namespace.
GenerateNamespaceOpeners(printer);
if (!options_.proto_h) {
GenerateGlobalStateFunctionDeclarations(printer);
GenerateMessageForwardDeclarations(printer);
printer->Print("\n");
GenerateEnumDefinitions(printer);
printer->Print(kThickSeparator);
printer->Print("\n");
GenerateMessageDefinitions(printer);
printer->Print("\n");
printer->Print(kThickSeparator);
printer->Print("\n");
GenerateServiceDefinitions(printer);
GenerateExtensionIdentifiers(printer);
printer->Print("\n");
printer->Print(kThickSeparator);
printer->Print("\n");
GenerateInlineFunctionDefinitions(printer);
}
printer->Print(
"\n"
"// @@protoc_insertion_point(namespace_scope)\n");
// Close up namespace.
GenerateNamespaceClosers(printer);
if (!options_.proto_h) {
// We need to specialize some templates in the ::google::protobuf namespace:
GenerateProto2NamespaceEnumSpecializations(printer);
}
printer->Print(
"\n"
"// @@protoc_insertion_point(global_scope)\n"
"\n");
GenerateBottomHeaderGuard(printer, filename_identifier);
}
void FileGenerator::GenerateSource(io::Printer* printer) {
string header =
StripProto(file_->name()) + (options_.proto_h ? ".proto.h" : ".pb.h");
printer->Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
"// source: $filename$\n"
"\n"
// The generated code calls accessors that might be deprecated. We don't
// want the compiler to warn in generated code.
"#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n"
"#include \"$basename$.pb.h\"\n"
"#include \"$header$\"\n"
"\n"
"#include <algorithm>\n" // for swap()
"\n"
@ -165,7 +255,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
"#include <google/protobuf/io/coded_stream.h>\n"
"#include <google/protobuf/wire_format_lite_inl.h>\n",
"filename", file_->name(),
"basename", StripProto(file_->name()));
"header", header);
// Unknown fields implementation in lite mode uses StringOutputStream
if (!UseUnknownFieldSet(file_) && file_->message_type_count() > 0) {
@ -181,6 +271,18 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
"#include <google/protobuf/wire_format.h>\n");
}
if (options_.proto_h) {
// Use the smaller .proto.h files.
for (int i = 0; i < file_->dependency_count(); i++) {
const FileDescriptor* dep = file_->dependency(i);
const char* extension = ".proto.h";
string dependency = StripProto(dep->name()) + extension;
printer->Print(
"#include \"$dependency$\"\n",
"dependency", dependency);
}
}
printer->Print(
"// @@protoc_insertion_point(includes)\n");
@ -276,6 +378,59 @@ void FileGenerator::GenerateSource(io::Printer* printer) {
"// @@protoc_insertion_point(global_scope)\n");
}
class FileGenerator::ForwardDeclarations {
public:
~ForwardDeclarations() {
for (map<string, ForwardDeclarations *>::iterator it = namespaces_.begin(),
end = namespaces_.end();
it != end; ++it) {
delete it->second;
}
namespaces_.clear();
}
ForwardDeclarations* AddOrGetNamespace(const string& ns_name) {
ForwardDeclarations*& ns = namespaces_[ns_name];
if (ns == NULL) {
ns = new ForwardDeclarations;
}
return ns;
}
set<string>& classes() { return classes_; }
set<string>& enums() { return enums_; }
void Print(io::Printer* printer) const {
for (set<string>::const_iterator it = enums_.begin(), end = enums_.end();
it != end; ++it) {
printer->Print("enum $enumname$ : int;\n"
"bool $enumname$_IsValid(int value);\n",
"enumname", it->c_str());
}
for (set<string>::const_iterator it = classes_.begin(),
end = classes_.end();
it != end; ++it) {
printer->Print("class $classname$;\n", "classname", it->c_str());
}
for (map<string, ForwardDeclarations *>::const_iterator
it = namespaces_.begin(),
end = namespaces_.end();
it != end; ++it) {
printer->Print("namespace $nsname$ {\n",
"nsname", it->first);
it->second->Print(printer);
printer->Print("} // namespace $nsname$\n",
"nsname", it->first);
}
}
private:
map<string, ForwardDeclarations*> namespaces_;
set<string> classes_;
set<string> enums_;
};
void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
// AddDescriptors() is a file-level procedure which adds the encoded
// FileDescriptorProto for this .proto file to the global DescriptorPool for
@ -434,12 +589,17 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
string file_data;
file_proto.SerializeToString(&file_data);
#ifdef _MSC_VER
bool breakdown_large_file = true;
#else
bool breakdown_large_file = false;
#endif
// Workaround for MSVC: "Error C1091: compiler limit: string exceeds 65535
// bytes in length". Declare a static array of characters rather than use a
// string literal.
if (file_data.size() > 65535) {
if (breakdown_large_file && file_data.size() > 65535) {
printer->Print(
"static const char descriptor[] = {\n");
"static const char descriptor[] = {\n");
printer->Indent();
// Only write 25 bytes per line.
@ -447,26 +607,25 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
for (int i = 0; i < file_data.size();) {
for (int j = 0; j < kBytesPerLine && i < file_data.size(); ++i, ++j) {
printer->Print(
"$char$, ",
"char", SimpleItoa(file_data[i]));
"$char$, ",
"char", SimpleItoa(file_data[i]));
}
printer->Print(
"\n");
"\n");
}
printer->Outdent();
printer->Print(
"};\n");
"};\n");
printer->Print(
"::google::protobuf::DescriptorPool::InternalAddGeneratedFile(descriptor, $size$);\n",
"size", SimpleItoa(file_data.size()));
"::google::protobuf::DescriptorPool::InternalAddGeneratedFile(descriptor, $size$);\n",
"size", SimpleItoa(file_data.size()));
} else {
printer->Print(
"::google::protobuf::DescriptorPool::InternalAddGeneratedFile(");
// Only write 40 bytes per line.
static const int kBytesPerLine = 40;
for (int i = 0; i < file_data.size(); i += kBytesPerLine) {
@ -474,11 +633,10 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) {
"data",
EscapeTrigraphs(
CEscape(file_data.substr(i, kBytesPerLine))));
}
printer->Print(
", $size$);\n",
}
printer->Print(
", $size$);\n",
"size", SimpleItoa(file_data.size()));
}
// Call MessageFactory::InternalRegisterGeneratedFile().
@ -548,8 +706,40 @@ void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) {
}
}
void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer) {
string filename_identifier = FilenameIdentifier(file_->name());
void FileGenerator::GenerateForwardDeclarations(io::Printer* printer) {
ForwardDeclarations decls;
for (int i = 0; i < file_->dependency_count(); i++) {
FileGenerator dependency(file_->dependency(i), options_);
dependency.FillForwardDeclarations(&decls);
}
FillForwardDeclarations(&decls);
decls.Print(printer);
}
void FileGenerator::FillForwardDeclarations(ForwardDeclarations* decls) {
for (int i = 0; i < file_->public_dependency_count(); i++) {
FileGenerator dependency(file_->public_dependency(i), options_);
dependency.FillForwardDeclarations(decls);
}
for (int i = 0; i < package_parts_.size(); i++) {
decls = decls->AddOrGetNamespace(package_parts_[i]);
}
// Generate enum definitions.
for (int i = 0; i < file_->message_type_count(); i++) {
message_generators_[i]->FillEnumForwardDeclarations(&decls->enums());
}
for (int i = 0; i < file_->enum_type_count(); i++) {
enum_generators_[i]->FillForwardDeclaration(&decls->enums());
}
// Generate forward declarations of classes.
for (int i = 0; i < file_->message_type_count(); i++) {
message_generators_[i]->FillMessageForwardDeclarations(
&decls->classes());
}
}
void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer,
const string& filename_identifier) {
// Generate top of header.
printer->Print(
"// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
@ -564,8 +754,8 @@ void FileGenerator::GenerateTopHeaderGuard(io::Printer* printer) {
"filename_identifier", filename_identifier);
}
void FileGenerator::GenerateBottomHeaderGuard(io::Printer* printer) {
string filename_identifier = FilenameIdentifier(file_->name());
void FileGenerator::GenerateBottomHeaderGuard(
io::Printer* printer, const string& filename_identifier) {
printer->Print(
"#endif // PROTOBUF_$filename_identifier$__INCLUDED\n",
"filename_identifier", filename_identifier);
@ -696,9 +886,13 @@ void FileGenerator::GenerateGlobalStateFunctionDeclarations(
}
void FileGenerator::GenerateMessageForwardDeclarations(io::Printer* printer) {
// Generate forward declarations of classes.
set<string> classes;
for (int i = 0; i < file_->message_type_count(); i++) {
message_generators_[i]->GenerateMessageForwardDeclaration(printer);
message_generators_[i]->FillMessageForwardDeclarations(&classes);
}
for (set<string>::const_iterator it = classes.begin(), end = classes.end();
it != end; ++it) {
printer->Print("class $classname$;\n", "classname", it->c_str());
}
}
@ -804,10 +998,6 @@ void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* printer) {
// Methods of the dependent base class must always be inline in the header.
message_generators_[i]->GenerateDependentInlineMethods(printer);
}
printer->Print(
"\n"
"// @@protoc_insertion_point(namespace_scope)\n");
}
void FileGenerator::GenerateProto2NamespaceEnumSpecializations(

View File

@ -69,10 +69,14 @@ class FileGenerator {
const Options& options);
~FileGenerator();
void GenerateHeader(io::Printer* printer);
void GenerateProtoHeader(io::Printer* printer);
void GeneratePBHeader(io::Printer* printer);
void GenerateSource(io::Printer* printer);
private:
// Internal type used by GenerateForwardDeclarations (defined in file.cc).
class ForwardDeclarations;
// Generate the BuildDescriptors() procedure, which builds all descriptors
// for types defined in the file.
void GenerateBuildDescriptors(io::Printer* printer);
@ -80,9 +84,19 @@ class FileGenerator {
void GenerateNamespaceOpeners(io::Printer* printer);
void GenerateNamespaceClosers(io::Printer* printer);
// For other imports, generates their forward-declarations.
void GenerateForwardDeclarations(io::Printer* printer);
// Internal helper used by GenerateForwardDeclarations: fills 'decls'
// with all necessary forward-declarations for this file and its
// transient depednencies.
void FillForwardDeclarations(ForwardDeclarations* decls);
// Generates top or bottom of a header file.
void GenerateTopHeaderGuard(io::Printer* printer);
void GenerateBottomHeaderGuard(io::Printer* printer);
void GenerateTopHeaderGuard(io::Printer* printer,
const string& filename_identifier);
void GenerateBottomHeaderGuard(io::Printer* printer,
const string& filename_identifier);
// Generates #include directives.
void GenerateLibraryIncludes(io::Printer* printer);
@ -92,10 +106,20 @@ class FileGenerator {
void GenerateGlobalStateFunctionDeclarations(io::Printer* printer);
// Generates types for classes.
void GenerateMessageForwardDeclarations(io::Printer* printer);
void GenerateMessageDefinitions(io::Printer* printer);
// Generates forward-declarations for just this file's classes. This is
// used for .pb.h headers, but not in proto_h mode.
void GenerateMessageForwardDeclarations(io::Printer* printer);
// Fills in types for forward declarations. This is used internally, and
// also by other FileGenerators to determine imports' declarations.
void FillMessageForwardDeclarations(ForwardDeclarations* decls);
void FillMessageDefinitions(ForwardDeclarations* decls);
// Generates enum definitions.
void GenerateEnumForwardDeclarations(io::Printer* printer);
void FillEnumForwardDeclarations(ForwardDeclarations* decls);
void GenerateEnumDefinitions(io::Printer* printer);
// Generates generic service definitions.

View File

@ -100,16 +100,23 @@ bool CppGenerator::Generate(const FileDescriptor* file,
string basename = StripProto(file->name());
basename.append(".pb");
FileGenerator file_generator(file, file_options);
// Generate header.
// Generate header(s).
if (file_options.proto_h) {
google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".proto.h"));
io::Printer printer(output.get(), '$');
file_generator.GenerateProtoHeader(&printer);
}
basename.append(".pb");
{
google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(basename + ".h"));
io::Printer printer(output.get(), '$');
file_generator.GenerateHeader(&printer);
file_generator.GeneratePBHeader(&printer);
}
// Generate cc file.

View File

@ -39,6 +39,7 @@
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
@ -68,7 +69,7 @@ const char* const kKeywordList[] = {
"constexpr", "const_cast", "continue", "decltype", "default", "delete", "do",
"double", "dynamic_cast", "else", "enum", "explicit", "extern", "false",
"float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
"namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator", "or",
"namespace", "new", "noexcept", "not", "not_eq", "NULL", "operator", "or",
"or_eq", "private", "protected", "public", "register", "reinterpret_cast",
"return", "short", "signed", "sizeof", "static", "static_assert",
"static_cast", "struct", "switch", "template", "this", "thread_local",
@ -174,6 +175,14 @@ string SuperClassName(const Descriptor* descriptor) {
"::google::protobuf::Message" : "::google::protobuf::MessageLite";
}
string DependentBaseDownCast() {
return "reinterpret_cast<T*>(this)->";
}
string DependentBaseConstDownCast() {
return "reinterpret_cast<const T*>(this)->";
}
string FieldName(const FieldDescriptor* field) {
string result = field->name();
LowerString(&result);
@ -208,6 +217,19 @@ string FieldConstantName(const FieldDescriptor *field) {
}
bool IsFieldDependent(const FieldDescriptor* field) {
if (field->containing_oneof() != NULL &&
field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
return true;
}
if (field->is_map()) {
const Descriptor* map_descriptor = field->message_type();
for (int i = 0; i < map_descriptor->field_count(); i++) {
if (IsFieldDependent(map_descriptor->field(i))) {
return true;
}
}
return false;
}
if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
return false;
}

View File

@ -70,8 +70,15 @@ string ClassName(const EnumDescriptor* enum_descriptor, bool qualified);
// This is a class name, like "ProtoName_InternalBase".
string DependentBaseClassTemplateName(const Descriptor* descriptor);
// Name of the base class: either the dependent base class (for use with
// proto_h) or google::protobuf::Message.
string SuperClassName(const Descriptor* descriptor);
// Returns a string that down-casts from the dependent base class to the
// derived class.
string DependentBaseDownCast();
string DependentBaseConstDownCast();
// Get the (unqualified) name that should be used for this field in C++ code.
// The name is coerced to lower-case to emulate proto1 behavior. People
// should be using lowercase-with-underscores style for proto field names

View File

@ -100,8 +100,9 @@ void SetMessageVariables(const FieldDescriptor* descriptor,
MapFieldGenerator::
MapFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: descriptor_(descriptor) {
const Options& options)
: descriptor_(descriptor),
dependent_field_(options.proto_h && IsFieldDependent(descriptor)) {
SetMessageVariables(descriptor, &variables_, options);
}
@ -152,7 +153,9 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
void MapFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
printer->Print(variables_, "$name$_.Clear();\n");
map<string, string> variables(variables_);
variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : "";
printer->Print(variables, "$this_message$$name$_.Clear();\n");
}
void MapFieldGenerator::

View File

@ -63,6 +63,7 @@ class MapFieldGenerator : public FieldGenerator {
private:
const FieldDescriptor* descriptor_;
const bool dependent_field_;
map<string, string> variables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator);

View File

@ -39,7 +39,6 @@
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <set>
#include <utility>
#include <vector>
#include <google/protobuf/compiler/cpp/cpp_message.h>
@ -415,31 +414,34 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor,
use_dependent_base_ = true;
}
}
if (options.proto_h && descriptor->oneof_decl_count() > 0) {
// Always make oneofs dependent.
use_dependent_base_ = true;
}
}
MessageGenerator::~MessageGenerator() {}
void MessageGenerator::
GenerateMessageForwardDeclaration(io::Printer* printer) {
printer->Print("class $classname$;\n",
"classname", classname_);
FillMessageForwardDeclarations(set<string>* class_names) {
class_names->insert(classname_);
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
// map entry message doesn't need forward declaration. Since map entry
// message cannot be a top level class, we just need to avoid calling
// GenerateForwardDeclaration here.
if (IsMapEntryMessage(descriptor_->nested_type(i))) continue;
nested_generators_[i]->GenerateMessageForwardDeclaration(printer);
nested_generators_[i]->FillMessageForwardDeclarations(class_names);
}
}
void MessageGenerator::
GenerateEnumForwardDeclaration(io::Printer* printer) {
FillEnumForwardDeclarations(set<string>* enum_names) {
for (int i = 0; i < descriptor_->nested_type_count(); i++) {
nested_generators_[i]->GenerateEnumForwardDeclaration(printer);
nested_generators_[i]->FillEnumForwardDeclarations(enum_names);
}
for (int i = 0; i < descriptor_->enum_type_count(); i++) {
enum_generators_[i]->GenerateForwardDeclaration(printer);
enum_generators_[i]->FillForwardDeclaration(enum_names);
}
}
@ -484,13 +486,6 @@ GenerateDependentFieldAccessorDeclarations(io::Printer* printer) {
field_generators_.get(field).GenerateDependentAccessorDeclarations(printer);
printer->Print("\n");
}
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
const OneofDescriptor* oneof = descriptor_->oneof_decl(i);
PrintFieldComment(printer, oneof);
printer->Print(
"void clear_$oneof_name$();\n",
"oneof_name", oneof->name());
}
}
void MessageGenerator::
@ -505,7 +500,9 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) {
vars["constant_name"] = FieldConstantName(field);
bool dependent_field = use_dependent_base_ && IsFieldDependent(field);
if (dependent_field) {
if (dependent_field &&
field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
!field->is_map()) {
// If this field is dependent, the dependent base class determines
// the message type from the derived class (which is a template
// parameter). This typedef is for that:
@ -594,8 +591,8 @@ GenerateDependentFieldAccessorDefinitions(io::Printer* printer) {
vars["tmpl"] = "template<class T>\n";
vars["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_) + "<T>";
vars["this_message"] = "reinterpret_cast<T*>(this)->";
vars["this_const_message"] = "reinterpret_cast<const T*>(this)->";
vars["this_message"] = DependentBaseDownCast();
vars["this_const_message"] = DependentBaseConstDownCast();
GenerateFieldClear(field, vars, printer);
}
@ -721,13 +718,15 @@ GenerateFieldClear(const FieldDescriptor* field,
printer->Print(vars,
"if ($this_message$has_$name$()) {\n");
printer->Indent();
field_generators_.get(field).GenerateClearingCode(printer);
field_generators_.get(field)
.GenerateClearingCode(printer);
printer->Print(vars,
"$this_message$clear_has_$oneof_name$();\n");
printer->Outdent();
printer->Print("}\n");
} else {
field_generators_.get(field).GenerateClearingCode(printer);
field_generators_.get(field)
.GenerateClearingCode(printer);
if (HasFieldPresence(descriptor_->file())) {
if (!field->is_repeated()) {
printer->Print(vars,
@ -752,6 +751,18 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) {
map<string, string> vars;
SetCommonFieldVariables(field, &vars, options_);
vars["inline"] = is_inline ? "inline " : "";
if (use_dependent_base_ && IsFieldDependent(field)) {
vars["tmpl"] = "template<class T>\n";
vars["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_) + "<T>";
vars["this_message"] = "reinterpret_cast<T*>(this)->";
vars["this_const_message"] = "reinterpret_cast<const T*>(this)->";
} else {
vars["tmpl"] = "";
vars["dependent_classname"] = vars["classname"];
vars["this_message"] = "";
vars["this_const_message"] = "";
}
// Generate has_$name$() or $name$_size().
if (field->is_repeated()) {
@ -775,10 +786,6 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) {
}
if (!use_dependent_base_ || !IsFieldDependent(field)) {
vars["tmpl"] = "";
vars["dependent_classname"] = vars["classname"];
vars["this_message"] = "";
vars["this_const_message"] = "";
GenerateFieldClear(field, vars, printer);
}
@ -915,15 +922,32 @@ GenerateClassDefinition(io::Printer* printer) {
"}\n"
"\n");
} else {
printer->Print(
"inline const ::std::string& unknown_fields() const {\n"
" return _unknown_fields_;\n"
"}\n"
"\n"
"inline ::std::string* mutable_unknown_fields() {\n"
" return &_unknown_fields_;\n"
"}\n"
"\n");
if (SupportsArenas(descriptor_)) {
printer->Print(
"inline const ::std::string& unknown_fields() const {\n"
" return _unknown_fields_.Get(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n"
"}\n"
"\n"
"inline ::std::string* mutable_unknown_fields() {\n"
" return _unknown_fields_.Mutable(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited(),\n"
" GetArenaNoVirtual());\n"
"}\n"
"\n");
} else {
printer->Print(
"inline const ::std::string& unknown_fields() const {\n"
" return _unknown_fields_.GetNoArena(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n"
"}\n"
"\n"
"inline ::std::string* mutable_unknown_fields() {\n"
" return _unknown_fields_.MutableNoArena(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n"
"}\n"
"\n");
}
}
}
@ -1068,6 +1092,10 @@ GenerateClassDefinition(io::Printer* printer) {
}
}
uses_string_ = false;
if (PreserveUnknownFields(descriptor_) &&
!UseUnknownFieldSet(descriptor_->file())) {
uses_string_ = true;
}
for (int i = 0; i < descriptors.size(); i++) {
const FieldDescriptor* field = descriptors[i];
if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) {
@ -1201,18 +1229,11 @@ GenerateClassDefinition(io::Printer* printer) {
// Generate oneof function declarations
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
if (use_dependent_base_) {
printer->Print(
"inline bool has_$oneof_name$() const;\n"
"inline void clear_has_$oneof_name$();\n\n",
"oneof_name", descriptor_->oneof_decl(i)->name());
} else {
printer->Print(
"inline bool has_$oneof_name$() const;\n"
"void clear_$oneof_name$();\n"
"inline void clear_has_$oneof_name$();\n\n",
"oneof_name", descriptor_->oneof_decl(i)->name());
}
printer->Print(
"inline bool has_$oneof_name$() const;\n"
"void clear_$oneof_name$();\n"
"inline void clear_has_$oneof_name$();\n\n",
"oneof_name", descriptor_->oneof_decl(i)->name());
}
if (HasGeneratedMethods(descriptor_->file()) &&
@ -1262,7 +1283,7 @@ GenerateClassDefinition(io::Printer* printer) {
"::google::protobuf::internal::InternalMetadataWithArena _internal_metadata_;\n");
} else {
printer->Print(
"::std::string _unknown_fields_;\n"
"::google::protobuf::internal::ArenaStringPtr _unknown_fields_;\n"
"::google::protobuf::Arena* _arena_ptr_;\n"
"\n");
}
@ -1919,6 +1940,13 @@ GenerateSharedConstructorCode(io::Printer* printer) {
uses_string_ ? "::google::protobuf::internal::GetEmptyString();\n" : "",
"_cached_size_ = 0;\n").c_str());
if (PreserveUnknownFields(descriptor_) &&
!UseUnknownFieldSet(descriptor_->file())) {
printer->Print(
"_unknown_fields_.UnsafeSetDefault(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n");
}
for (int i = 0; i < descriptor_->field_count(); i++) {
if (!descriptor_->field(i)->containing_oneof()) {
field_generators_.get(descriptor_->field(i))
@ -1955,6 +1983,22 @@ GenerateSharedDestructorCode(io::Printer* printer) {
"}\n"
"\n");
}
// Write the desctructor for _unknown_fields_ in lite runtime.
if (PreserveUnknownFields(descriptor_) &&
!UseUnknownFieldSet(descriptor_->file())) {
if (SupportsArenas(descriptor_)) {
printer->Print(
"_unknown_fields_.Destroy(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited(),\n"
" GetArenaNoVirtual());\n");
} else {
printer->Print(
"_unknown_fields_.DestroyNoArena(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n");
}
}
// Write the destructors for each field except oneof members.
for (int i = 0; i < descriptor_->field_count(); i++) {
if (!descriptor_->field(i)->containing_oneof()) {
@ -2463,8 +2507,16 @@ GenerateClear(io::Printer* printer) {
" mutable_unknown_fields()->Clear();\n"
"}\n");
} else {
printer->Print(
"mutable_unknown_fields()->clear();\n");
if (SupportsArenas(descriptor_)) {
printer->Print(
"_unknown_fields_.ClearToEmpty(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited(),\n"
" GetArenaNoVirtual());\n");
} else {
printer->Print(
"_unknown_fields_.ClearToEmptyNoArena(\n"
" &::google::protobuf::internal::GetEmptyStringAlreadyInited());\n");
}
}
}
@ -2481,33 +2533,22 @@ GenerateOneofClear(io::Printer* printer) {
oneof_vars["oneofname"] = descriptor_->oneof_decl(i)->name();
string message_class;
if (use_dependent_base_) {
oneof_vars["tmpl"] = "template<class T>\n";
oneof_vars["inline"] = "inline ";
oneof_vars["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_) + "<T>";
oneof_vars["this_message"] = "reinterpret_cast<T*>(this)->";
message_class = "T::";
} else {
oneof_vars["tmpl"] = "";
oneof_vars["inline"] = "";
oneof_vars["dependent_classname"] = classname_;
oneof_vars["this_message"] = "";
}
printer->Print(oneof_vars,
"$tmpl$"
"$inline$"
"void $dependent_classname$::clear_$oneofname$() {\n");
"void $classname$::clear_$oneofname$() {\n");
printer->Indent();
// In .proto.h mode, fields with a dependent type will generate
// clearing code that down casts from the dependent base class.
// However, clear_oneof() methods are always in the .cc file, and thus
// must remain in the derived base. So, to make the clearing code work,
// we add a typedef so that the down cast works (it will be a no-op).
printer->Print(oneof_vars,
"switch($this_message$$oneofname$_case()) {\n");
"typedef $classname$ T;\n"
"switch($oneofname$_case()) {\n");
printer->Indent();
for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
printer->Print(
"case $message_class$k$field_name$: {\n",
"message_class", message_class,
"case k$field_name$: {\n",
"field_name", UnderscoresToCamelCase(field->name(), true));
printer->Indent();
// We clear only allocated objects in oneofs
@ -2524,20 +2565,16 @@ GenerateOneofClear(io::Printer* printer) {
"}\n");
}
printer->Print(
"case $message_class$$cap_oneof_name$_NOT_SET: {\n"
"case $cap_oneof_name$_NOT_SET: {\n"
" break;\n"
"}\n",
"message_class", message_class,
"cap_oneof_name",
ToUpper(descriptor_->oneof_decl(i)->name()));
printer->Outdent();
printer->Print(
"}\n"
"$this_message$_oneof_case_[$oneof_index$] = "
"$message_class$$cap_oneof_name$_NOT_SET;\n",
"this_message", oneof_vars["this_message"],
"_oneof_case_[$oneof_index$] = $cap_oneof_name$_NOT_SET;\n",
"oneof_index", SimpleItoa(i),
"message_class", message_class,
"cap_oneof_name",
ToUpper(descriptor_->oneof_decl(i)->name()));
printer->Outdent();
@ -2612,7 +2649,7 @@ GenerateSwap(io::Printer* printer) {
printer->Print(
"_internal_metadata_.Swap(&other->_internal_metadata_);\n");
} else {
printer->Print("_unknown_fields_.swap(other->_unknown_fields_);\n");
printer->Print("_unknown_fields_.Swap(&other->_unknown_fields_);\n");
}
} else {
// Still swap internal_metadata as it may contain more than just

View File

@ -39,8 +39,8 @@
#ifndef _SHARED_PTR_H
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <set>
#include <string>
#include <vector>
#include <google/protobuf/compiler/cpp/cpp_field.h>
#include <google/protobuf/compiler/cpp/cpp_options.h>
@ -66,9 +66,10 @@ class MessageGenerator {
// Header stuff.
// Generate foward declarations for this class and all its nested types.
void GenerateMessageForwardDeclaration(io::Printer* printer);
void GenerateEnumForwardDeclaration(io::Printer* printer);
// Return names for foward declarations of this class and all its nested
// types.
void FillMessageForwardDeclarations(set<string>* class_names);
void FillEnumForwardDeclarations(set<string>* enum_names);
// Generate definitions of all nested enums (must come before class
// definitions because those classes use the enums definitions).

View File

@ -63,6 +63,14 @@ void SetMessageVariables(const FieldDescriptor* descriptor,
SafeFunctionName(descriptor->containing_type(),
descriptor, "release_");
(*variables)["full_name"] = descriptor->full_name();
if (options.proto_h && IsFieldDependent(descriptor)) {
(*variables)["dependent_type"] = "T::" + DependentTypeName(descriptor);
(*variables)["dependent_typename"] =
"typename T::" + DependentTypeName(descriptor);
} else {
(*variables)["dependent_type"] = FieldMessageTypeName(descriptor);
(*variables)["dependent_typename"] = FieldMessageTypeName(descriptor);
}
}
} // namespace
@ -84,8 +92,22 @@ GeneratePrivateMembers(io::Printer* printer) const {
printer->Print(variables_, "$type$* $name$_;\n");
}
void MessageFieldGenerator::
GenerateGetterDeclaration(io::Printer* printer) const {
printer->Print(variables_,
"const $type$& $name$() const$deprecation$;\n");
}
void MessageFieldGenerator::
GenerateDependentAccessorDeclarations(io::Printer* printer) const {
if (!dependent_field_) {
return;
}
// Arena manipulation code is out-of-line in the derived message class.
printer->Print(variables_,
"$type$* mutable_$name$()$deprecation$;\n"
"$type$* $release_name$()$deprecation$;\n"
"void set_allocated_$name$($type$* $name$)$deprecation$;\n");
}
void MessageFieldGenerator::
@ -103,11 +125,13 @@ GenerateAccessorDeclarations(io::Printer* printer) const {
"$type$* _slow_$release_name$()$deprecation$;\n"
"public:\n");
}
printer->Print(variables_,
"const $type$& $name$() const$deprecation$;\n"
"$type$* mutable_$name$()$deprecation$;\n"
"$type$* $release_name$()$deprecation$;\n"
"void set_allocated_$name$($type$* $name$)$deprecation$;\n");
GenerateGetterDeclaration(printer);
if (!dependent_field_) {
printer->Print(variables_,
"$type$* mutable_$name$()$deprecation$;\n"
"$type$* $release_name$()$deprecation$;\n"
"void set_allocated_$name$($type$* $name$)$deprecation$;\n");
}
if (SupportsArenas(descriptor_)) {
printer->Print(variables_,
"$type$* unsafe_arena_release_$name$()$deprecation$;\n"
@ -123,12 +147,12 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions(
"void $classname$::_slow_mutable_$name$() {\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables_,
" $name$_ = ::google::protobuf::Arena::CreateMessage< $type$ >(\n"
" GetArenaNoVirtual());\n");
" $name$_ = ::google::protobuf::Arena::CreateMessage< $type$ >(\n"
" GetArenaNoVirtual());\n");
} else {
printer->Print(variables_,
" $name$_ = ::google::protobuf::Arena::Create< $type$ >(\n"
" GetArenaNoVirtual());\n");
" $name$_ = ::google::protobuf::Arena::Create< $type$ >(\n"
" GetArenaNoVirtual());\n");
}
printer->Print(variables_,
"}\n"
@ -151,7 +175,7 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions(
if (SupportsArenas(descriptor_->message_type())) {
// NOTE: the same logic is mirrored in weak_message_field.cc. Any
// arena-related semantics changes should be made in both places.
printer->Print(variables_,
printer->Print(variables_,
"void $classname$::_slow_set_allocated_$name$(\n"
" ::google::protobuf::Arena* message_arena, $type$** $name$) {\n"
" if (message_arena != NULL && \n"
@ -189,15 +213,139 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions(
void MessageFieldGenerator::
GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const {
if (!dependent_field_) {
return;
}
map<string, string> variables(variables_);
// For the CRTP base class, all mutation methods are dependent, and so
// they must be in the header.
variables["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_->containing_type()) + "<T>";
variables["this_message"] = DependentBaseDownCast();
if (!variables["set_hasbit"].empty()) {
variables["set_hasbit"] =
variables["this_message"] + variables["set_hasbit"];
}
if (!variables["clear_hasbit"].empty()) {
variables["clear_hasbit"] =
variables["this_message"] + variables["clear_hasbit"];
}
if (SupportsArenas(descriptor_)) {
printer->Print(variables,
"template <class T>\n"
"inline $type$* $dependent_classname$::mutable_$name$() {\n"
" $set_hasbit$\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" if ($name$_ == NULL) {\n"
" $this_message$_slow_mutable_$name$();\n"
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"template <class T>\n"
"inline $type$* $dependent_classname$::$release_name$() {\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" $clear_hasbit$\n"
" if ($this_message$GetArenaNoVirtual() != NULL) {\n"
" return $this_message$_slow_$release_name$();\n"
" } else {\n"
" $dependent_typename$* temp = $name$_;\n"
" $name$_ = NULL;\n"
" return temp;\n"
" }\n"
"}\n"
"template <class T>\n"
"inline void $dependent_classname$::"
"set_allocated_$name$($type$* $name$) {\n"
" ::google::protobuf::Arena* message_arena = $this_message$GetArenaNoVirtual();\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" if (message_arena == NULL) {\n"
" delete $name$_;\n"
" }\n"
" if ($name$ != NULL) {\n");
if (SupportsArenas(descriptor_->message_type())) {
// If we're on an arena and the incoming message is not, simply Own() it
// rather than copy to the arena -- either way we need a heap dealloc,
// so we might as well defer it. Otherwise, if incoming message is on a
// different ownership domain (specific arena, or the heap) than we are,
// copy to our arena (or heap, as the case may be).
printer->Print(variables,
" $this_message$_slow_set_allocated_$name$(message_arena, "
"&$name$);\n");
} else {
printer->Print(variables,
" if (message_arena != NULL) {\n"
" message_arena->Own($name$);\n"
" }\n");
}
printer->Print(variables,
" }\n"
" $name$_ = $name$;\n"
" if ($name$) {\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n"
// TODO(dlj): move insertion points to message class.
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
} else {
printer->Print(variables,
"template <class T>\n"
"inline $type$* $dependent_classname$::mutable_$name$() {\n"
" $set_hasbit$\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" if ($name$_ == NULL) {\n"
" $name$_ = new $dependent_typename$;\n"
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"template <class T>\n"
"inline $type$* $dependent_classname$::$release_name$() {\n"
" $clear_hasbit$\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" $dependent_typename$* temp = $name$_;\n"
" $name$_ = NULL;\n"
" return temp;\n"
"}\n"
"template <class T>\n"
"inline void $dependent_classname$::"
"set_allocated_$name$($type$* $name$) {\n"
" $dependent_typename$*& $name$_ = $this_message$$name$_;\n"
" delete $name$_;\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables,
" if ($name$ != NULL && static_cast< $dependent_typename$* >($name$)"
"->GetArena() != NULL) {\n"
" $dependent_typename$* new_$name$ = new $dependent_typename$;\n"
" new_$name$->CopyFrom(*$name$);\n"
" $name$ = new_$name$;\n"
" }\n");
}
printer->Print(variables,
" $name$_ = $name$;\n"
" if ($name$) {\n"
" $set_hasbit$\n"
" } else {\n"
" $clear_hasbit$\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
}
}
void MessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer,
bool is_inline) const {
map<string, string> variables(variables_);
variables["inline"] = is_inline ? "inline" : "";
variables["inline"] = is_inline ? "inline " : "";
printer->Print(variables,
"$inline$ const $type$& $classname$::$name$() const {\n"
"$inline$const $type$& $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n");
PrintHandlingOptionalStaticInitializers(
@ -206,19 +354,25 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
" return $name$_ != NULL ? *$name$_ : *default_instance_->$name$_;\n",
// Without.
" return $name$_ != NULL ? *$name$_ : *default_instance().$name$_;\n");
printer->Print(variables, "}\n");
if (dependent_field_) {
return;
}
if (SupportsArenas(descriptor_)) {
printer->Print(variables,
"}\n"
"$inline$ $type$* $classname$::mutable_$name$() {\n"
"$inline$"
"$type$* $classname$::mutable_$name$() {\n"
" $set_hasbit$\n"
" if ($name$_ == NULL) {\n"
" _slow_mutable_$name$();"
" _slow_mutable_$name$();\n"
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"$inline$ $type$* $classname$::$release_name$() {\n"
"$inline$"
"$type$* $classname$::$release_name$() {\n"
" $clear_hasbit$\n"
" if (GetArenaNoVirtual() != NULL) {\n"
" return _slow_$release_name$();\n"
@ -228,7 +382,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
" return temp;\n"
" }\n"
"}\n"
"$inline$ void $classname$::set_allocated_$name$($type$* $name$) {\n"
"$inline$ "
"void $classname$::set_allocated_$name$($type$* $name$) {\n"
" ::google::protobuf::Arena* message_arena = GetArenaNoVirtual();\n"
" if (message_arena == NULL) {\n"
" delete $name$_;\n"
@ -260,8 +415,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
"}\n");
} else {
printer->Print(variables,
"}\n"
"$inline$ $type$* $classname$::mutable_$name$() {\n"
"$inline$"
"$type$* $classname$::mutable_$name$() {\n"
" $set_hasbit$\n"
" if ($name$_ == NULL) {\n"
" $name$_ = new $type$;\n"
@ -269,13 +424,15 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_;\n"
"}\n"
"$inline$ $type$* $classname$::$release_name$() {\n"
"$inline$"
"$type$* $classname$::$release_name$() {\n"
" $clear_hasbit$\n"
" $type$* temp = $name$_;\n"
" $name$_ = NULL;\n"
" return temp;\n"
"}\n"
"$inline$ void $classname$::set_allocated_$name$($type$* $name$) {\n"
"$inline$"
"void $classname$::set_allocated_$name$($type$* $name$) {\n"
" delete $name$_;\n");
if (SupportsArenas(descriptor_->message_type())) {
@ -301,15 +458,19 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
void MessageFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
map<string, string> variables(variables_);
variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : "";
if (!HasFieldPresence(descriptor_->file())) {
// If we don't have has-bits, message presence is indicated only by ptr !=
// NULL. Thus on clear, we need to delete the object.
printer->Print(variables_,
"if (GetArenaNoVirtual() == NULL && $name$_ != NULL) delete $name$_;\n"
"$name$_ = NULL;\n");
printer->Print(variables,
"if ($this_message$GetArenaNoVirtual() == NULL && "
"$this_message$$name$_ != NULL) delete $this_message$$name$_;\n"
"$this_message$$name$_ = NULL;\n");
} else {
printer->Print(variables_,
"if ($name$_ != NULL) $name$_->$type$::Clear();\n");
printer->Print(variables,
"if ($this_message$$name$_ != NULL) $this_message$$name$_->"
"$dependent_type$::Clear();\n");
}
}
@ -370,67 +531,180 @@ GenerateByteSize(io::Printer* printer) const {
MessageOneofFieldGenerator::
MessageOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: MessageFieldGenerator(descriptor, options) {
: MessageFieldGenerator(descriptor, options),
dependent_base_(options.proto_h) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
MessageOneofFieldGenerator::~MessageOneofFieldGenerator() {}
void MessageOneofFieldGenerator::
GenerateDependentAccessorDeclarations(io::Printer* printer) const {
// Oneof field getters must be dependent as they call default_instance().
// Otherwise, the logic is the same as MessageFields.
if (!dependent_field_) {
return;
}
printer->Print(variables_,
"const $type$& $name$() const$deprecation$;\n");
MessageFieldGenerator::GenerateDependentAccessorDeclarations(printer);
}
void MessageOneofFieldGenerator::
GenerateGetterDeclaration(io::Printer* printer) const {
// Oneof field getters must be dependent as they call default_instance().
// Unlike MessageField, this means there is no (non-dependent) getter to
// generate.
if (dependent_field_) {
return;
}
printer->Print(variables_,
"const $type$& $name$() const$deprecation$;\n");
}
void MessageOneofFieldGenerator::
GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const {
// For the CRTP base class, all mutation methods are dependent, and so
// they must be in the header.
if (!dependent_base_) {
return;
}
map<string, string> variables(variables_);
variables["inline"] = "inline ";
variables["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_->containing_type()) + "<T>";
variables["this_message"] = "reinterpret_cast<T*>(this)->";
// Const message access is needed for the dependent getter.
variables["this_const_message"] = "reinterpret_cast<const T*>(this)->";
variables["tmpl"] = "template <class T>\n";
variables["field_member"] = variables["this_message"] +
variables["oneof_prefix"] + variables["name"] +
"_";
InternalGenerateInlineAccessorDefinitions(variables, printer);
}
void MessageOneofFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer,
bool is_inline) const {
if (dependent_base_) {
return;
}
map<string, string> variables(variables_);
variables["inline"] = is_inline ? "inline" : "";
variables["inline"] = is_inline ? "inline " : "";
variables["dependent_classname"] = variables["classname"];
variables["this_message"] = "";
variables["this_const_message"] = "";
variables["tmpl"] = "";
variables["field_member"] =
variables["oneof_prefix"] + variables["name"] + "_";
variables["dependent_type"] = variables["type"];
InternalGenerateInlineAccessorDefinitions(variables, printer);
}
void MessageOneofFieldGenerator::
GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {
map<string, string> variables(variables_);
variables["field_member"] =
variables["oneof_prefix"] + variables["name"] + "_";
//printer->Print(variables,
}
void MessageOneofFieldGenerator::
InternalGenerateInlineAccessorDefinitions(const map<string, string>& variables,
io::Printer* printer) const {
printer->Print(variables,
"$tmpl$"
"$inline$ "
"const $type$& $dependent_classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $this_const_message$has_$name$()\n"
" ? *$this_const_message$$oneof_prefix$$name$_\n"
" : $dependent_type$::default_instance();\n"
"}\n");
if (SupportsArenas(descriptor_)) {
printer->Print(variables,
"$inline$ const $type$& $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return has_$name$() ? *$oneof_prefix$$name$_\n"
" : $type$::default_instance();\n"
"}\n"
"$inline$ $type$* $classname$::mutable_$name$() {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n");
"$tmpl$"
"$inline$"
"$type$* $dependent_classname$::mutable_$name$() {\n"
" if (!$this_message$has_$name$()) {\n"
" $this_message$clear_$oneof_name$();\n"
" $this_message$set_has_$name$();\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables,
" $oneof_prefix$$name$_ = \n"
" ::google::protobuf::Arena::CreateMessage< $type$ >(\n"
" GetArenaNoVirtual());\n");
" $field_member$ = \n"
" ::google::protobuf::Arena::CreateMessage< $dependent_typename$ >(\n"
" $this_message$GetArenaNoVirtual());\n");
} else {
printer->Print(variables,
" $oneof_prefix$$name$_ = \n"
" ::google::protobuf::Arena::Create< $type$ >(\n"
" GetArenaNoVirtual());\n");
" $this_message$$oneof_prefix$$name$_ = \n"
" ::google::protobuf::Arena::Create< $dependent_typename$ >(\n"
" $this_message$GetArenaNoVirtual());\n");
}
printer->Print(variables,
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $oneof_prefix$$name$_;\n"
" return $field_member$;\n"
"}\n"
"$inline$ $type$* $classname$::$release_name$() {\n"
" if (has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" if (GetArenaNoVirtual() != NULL) {\n"
"$tmpl$"
"$inline$"
"$type$* $dependent_classname$::$release_name$() {\n"
" if ($this_message$has_$name$()) {\n"
" $this_message$clear_has_$oneof_name$();\n"
" if ($this_message$GetArenaNoVirtual() != NULL) {\n"
// N.B.: safe to use the underlying field pointer here because we are sure
// that it is non-NULL (because has_$name$() returned true).
" $type$* temp = new $type$;\n"
" temp->MergeFrom(*$oneof_prefix$$name$_);\n"
" $oneof_prefix$$name$_ = NULL;\n"
" $dependent_typename$* temp = new $dependent_typename$;\n"
" temp->MergeFrom(*$field_member$);\n"
" $field_member$ = NULL;\n"
" return temp;\n"
" } else {\n"
" $type$* temp = $oneof_prefix$$name$_;\n"
" $oneof_prefix$$name$_ = NULL;\n"
" $dependent_typename$* temp = $field_member$;\n"
" $field_member$ = NULL;\n"
" return temp;\n"
" }\n"
" } else {\n"
" return NULL;\n"
" }\n"
"}\n"
"$tmpl$"
"$inline$"
"void $dependent_classname$::"
"set_allocated_$name$($type$* $name$) {\n"
" $this_message$clear_$oneof_name$();\n"
" if ($name$) {\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables,
// If incoming message is on the heap and we are on an arena, just Own()
// it (see above). If it's on a different arena than we are or one of us
// is on the heap, we make a copy to our arena/heap.
" if ($this_message$GetArenaNoVirtual() != NULL &&\n"
" ::google::protobuf::Arena::GetArena($name$) == NULL) {\n"
" $this_message$GetArenaNoVirtual()->Own($name$);\n"
" } else if ($this_message$GetArenaNoVirtual() !=\n"
" ::google::protobuf::Arena::GetArena($name$)) {\n"
" $dependent_typename$* new_$name$ = \n"
" ::google::protobuf::Arena::CreateMessage< $dependent_typename$ >(\n"
" $this_message$GetArenaNoVirtual());\n"
" new_$name$->CopyFrom(*$name$);\n"
" $name$ = new_$name$;\n"
" }\n");
} else {
printer->Print(variables,
" if ($this_message$GetArenaNoVirtual() != NULL) {\n"
" $this_message$GetArenaNoVirtual()->Own($name$);\n"
" }\n");
}
printer->Print(variables,
" $this_message$set_has_$name$();\n"
" $field_member$ = $name$;\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n"
"$inline$ $type$* $classname$::unsafe_arena_release_$name$() {\n"
" if (has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
@ -441,41 +715,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
" return NULL;\n"
" }\n"
"}\n"
"$inline$ void $classname$::set_allocated_$name$($type$* $name$) {\n"
" clear_$oneof_name$();\n"
" if ($name$) {\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables,
// If incoming message is on the heap and we are on an arena, just Own()
// it (see above). If it's on a different arena than we are or one of us
// is on the heap, we make a copy to our arena/heap.
" if (GetArenaNoVirtual() != NULL &&\n"
" ::google::protobuf::Arena::GetArena($name$) == NULL) {\n"
" GetArenaNoVirtual()->Own($name$);\n"
" } else if (GetArenaNoVirtual() !=\n"
" ::google::protobuf::Arena::GetArena($name$)) {\n"
" $type$* new_$name$ = \n"
" ::google::protobuf::Arena::CreateMessage< $type$ >(\n"
" GetArenaNoVirtual());\n"
" new_$name$->CopyFrom(*$name$);\n"
" $name$ = new_$name$;\n"
" }\n");
} else {
printer->Print(variables,
" if (GetArenaNoVirtual() != NULL) {\n"
" GetArenaNoVirtual()->Own($name$);\n"
" }\n");
}
printer->Print(variables,
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = $name$;\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n"
"$inline$ void $classname$::unsafe_arena_set_allocated_$name$("
"$type$* $name$) {\n"
"$inline$ void $classname$::unsafe_arena_set_allocated_$name$"
"($type$* $name$) {\n"
// We rely on the oneof clear method to free the earlier contents of this
// oneof. We can directly use the pointer we're given to set the new
// value.
@ -489,44 +730,47 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
"}\n");
} else {
printer->Print(variables,
"$inline$ const $type$& $classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return has_$name$() ? *$oneof_prefix$$name$_\n"
" : $type$::default_instance();\n"
"}\n"
"$inline$ $type$* $classname$::mutable_$name$() {\n"
" if (!has_$name$()) {\n"
" clear_$oneof_name$();\n"
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = new $type$;\n"
"$tmpl$"
"$inline$"
"$type$* $dependent_classname$::mutable_$name$() {\n"
" if (!$this_message$has_$name$()) {\n"
" $this_message$clear_$oneof_name$();\n"
" $this_message$set_has_$name$();\n"
" $field_member$ = new $dependent_typename$;\n"
" }\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $oneof_prefix$$name$_;\n"
" return $field_member$;\n"
"}\n"
"$inline$ $type$* $classname$::$release_name$() {\n"
" if (has_$name$()) {\n"
" clear_has_$oneof_name$();\n"
" $type$* temp = $oneof_prefix$$name$_;\n"
" $oneof_prefix$$name$_ = NULL;\n"
"$tmpl$"
"$inline$"
"$type$* $dependent_classname$::$release_name$() {\n"
" if ($this_message$has_$name$()) {\n"
" $this_message$clear_has_$oneof_name$();\n"
" $dependent_typename$* temp = $field_member$;\n"
" $field_member$ = NULL;\n"
" return temp;\n"
" } else {\n"
" return NULL;\n"
" }\n"
"}\n"
"$inline$ void $classname$::set_allocated_$name$($type$* $name$) {\n"
" clear_$oneof_name$();\n"
"$tmpl$"
"$inline$"
"void $dependent_classname$::"
"set_allocated_$name$($type$* $name$) {\n"
" $this_message$clear_$oneof_name$();\n"
" if ($name$) {\n");
if (SupportsArenas(descriptor_->message_type())) {
printer->Print(variables,
" if ($name$->GetArena() != NULL) {\n"
" $type$* new_$name$ = new $type$;\n"
" if (static_cast< $dependent_typename$*>($name$)->"
"GetArena() != NULL) {\n"
" $dependent_typename$* new_$name$ = new $dependent_typename$;\n"
" new_$name$->CopyFrom(*$name$);\n"
" $name$ = new_$name$;\n"
" }\n");
}
printer->Print(variables,
" set_has_$name$();\n"
" $oneof_prefix$$name$_ = $name$;\n"
" $this_message$set_has_$name$();\n"
" $field_member$ = $name$;\n"
" }\n"
" // @@protoc_insertion_point(field_set_allocated:$full_name$)\n"
"}\n");
@ -535,14 +779,16 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
void MessageOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
map<string, string> variables(variables_);
variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : "";
if (SupportsArenas(descriptor_)) {
printer->Print(variables_,
"if (GetArenaNoVirtual() == NULL) {\n"
" delete $oneof_prefix$$name$_;\n"
printer->Print(variables,
"if ($this_message$GetArenaNoVirtual() == NULL) {\n"
" delete $this_message$$oneof_prefix$$name$_;\n"
"}\n");
} else {
printer->Print(variables_,
"delete $oneof_prefix$$name$_;\n");
printer->Print(variables,
"delete $this_message$$oneof_prefix$$name$_;\n");
}
}
@ -562,7 +808,9 @@ GenerateConstructorCode(io::Printer* printer) const {
RepeatedMessageFieldGenerator::
RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: descriptor_(descriptor) {
: descriptor_(descriptor),
dependent_field_(options.proto_h && IsFieldDependent(descriptor)),
dependent_getter_(dependent_field_ && options.safe_boundary_check) {
SetMessageVariables(descriptor, &variables_, options);
}
@ -575,60 +823,160 @@ GeneratePrivateMembers(io::Printer* printer) const {
}
void RepeatedMessageFieldGenerator::
GenerateDependentAccessorDeclarations(io::Printer* printer) const {
}
void RepeatedMessageFieldGenerator::
GenerateAccessorDeclarations(io::Printer* printer) const {
InternalGenerateTypeDependentAccessorDeclarations(io::Printer* printer) const {
printer->Print(variables_,
"const $type$& $name$(int index) const$deprecation$;\n"
"$type$* mutable_$name$(int index)$deprecation$;\n"
"$type$* add_$name$()$deprecation$;\n");
if (dependent_getter_) {
printer->Print(variables_,
"const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
" $name$() const$deprecation$;\n");
}
printer->Print(variables_,
"const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
" $name$() const$deprecation$;\n"
"::google::protobuf::RepeatedPtrField< $type$ >*\n"
" mutable_$name$()$deprecation$;\n");
}
void RepeatedMessageFieldGenerator::
GenerateDependentAccessorDeclarations(io::Printer* printer) const {
if (dependent_getter_) {
printer->Print(variables_,
"const $type$& $name$(int index) const$deprecation$;\n");
}
if (dependent_field_) {
InternalGenerateTypeDependentAccessorDeclarations(printer);
}
}
void RepeatedMessageFieldGenerator::
GenerateAccessorDeclarations(io::Printer* printer) const {
if (!dependent_getter_) {
printer->Print(variables_,
"const $type$& $name$(int index) const$deprecation$;\n");
}
if (!dependent_field_) {
InternalGenerateTypeDependentAccessorDeclarations(printer);
}
if (!dependent_getter_) {
printer->Print(variables_,
"const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
" $name$() const$deprecation$;\n");
}
}
void RepeatedMessageFieldGenerator::
GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const {
if (!dependent_field_) {
return;
}
map<string, string> variables(variables_);
// For the CRTP base class, all mutation methods are dependent, and so
// they must be in the header.
variables["dependent_classname"] =
DependentBaseClassTemplateName(descriptor_->containing_type()) + "<T>";
variables["this_message"] = DependentBaseDownCast();
variables["this_const_message"] = DependentBaseConstDownCast();
if (dependent_getter_) {
printer->Print(variables,
"template <class T>\n"
"inline const $type$& $dependent_classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $this_const_message$$name$_.$cppget$(index);\n"
"}\n");
}
// Generate per-element accessors:
printer->Print(variables,
"template <class T>\n"
"inline $type$* $dependent_classname$::mutable_$name$(int index) {\n"
// TODO(dlj): move insertion points
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $this_message$$name$_.Mutable(index);\n"
"}\n"
"template <class T>\n"
"inline $type$* $dependent_classname$::add_$name$() {\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
" return $this_message$$name$_.Add();\n"
"}\n");
if (dependent_getter_) {
printer->Print(variables,
"template <class T>\n"
"inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
"$dependent_classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $this_const_message$$name$_;\n"
"}\n");
}
// Generate mutable access to the entire list:
printer->Print(variables,
"template <class T>\n"
"inline ::google::protobuf::RepeatedPtrField< $type$ >*\n"
"$dependent_classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$this_message$$name$_;\n"
"}\n");
}
void RepeatedMessageFieldGenerator::
GenerateInlineAccessorDefinitions(io::Printer* printer,
bool is_inline) const {
map<string, string> variables(variables_);
variables["inline"] = is_inline ? "inline" : "";
printer->Print(variables,
"$inline$ const $type$& $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.$cppget$(index);\n"
"}\n"
"$inline$ $type$* $classname$::mutable_$name$(int index) {\n"
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"$inline$ $type$* $classname$::add_$name$() {\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
" return $name$_.Add();\n"
"}\n");
printer->Print(variables,
"$inline$ const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n"
"$inline$ ::google::protobuf::RepeatedPtrField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
variables["inline"] = is_inline ? "inline " : "";
if (!dependent_getter_) {
printer->Print(variables,
"$inline$"
"const $type$& $classname$::$name$(int index) const {\n"
" // @@protoc_insertion_point(field_get:$full_name$)\n"
" return $name$_.$cppget$(index);\n"
"}\n");
}
if (!dependent_field_) {
printer->Print(variables,
"$inline$"
"$type$* $classname$::mutable_$name$(int index) {\n"
// TODO(dlj): move insertion points
" // @@protoc_insertion_point(field_mutable:$full_name$)\n"
" return $name$_.Mutable(index);\n"
"}\n"
"$inline$"
"$type$* $classname$::add_$name$() {\n"
" // @@protoc_insertion_point(field_add:$full_name$)\n"
" return $name$_.Add();\n"
"}\n");
}
if (!dependent_field_) {
printer->Print(variables,
"$inline$"
"::google::protobuf::RepeatedPtrField< $type$ >*\n"
"$classname$::mutable_$name$() {\n"
" // @@protoc_insertion_point(field_mutable_list:$full_name$)\n"
" return &$name$_;\n"
"}\n");
}
if (!dependent_getter_) {
printer->Print(variables,
"$inline$"
"const ::google::protobuf::RepeatedPtrField< $type$ >&\n"
"$classname$::$name$() const {\n"
" // @@protoc_insertion_point(field_list:$full_name$)\n"
" return $name$_;\n"
"}\n");
}
}
void RepeatedMessageFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
printer->Print(variables_, "$name$_.Clear();\n");
map<string, string> variables(variables_);
variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : "";
printer->Print(variables, "$this_message$$name$_.Clear();\n");
}
void RepeatedMessageFieldGenerator::

View File

@ -68,6 +68,11 @@ class MessageFieldGenerator : public FieldGenerator {
void GenerateByteSize(io::Printer* printer) const;
protected:
void GenerateArenaManipulationCode(const map<string, string>& variables,
io::Printer* printer) const;
virtual void GenerateGetterDeclaration(io::Printer* printer) const;
const FieldDescriptor* descriptor_;
const bool dependent_field_;
map<string, string> variables_;
@ -83,15 +88,23 @@ class MessageOneofFieldGenerator : public MessageFieldGenerator {
~MessageOneofFieldGenerator();
// implements FieldGenerator ---------------------------------------
void GenerateDependentAccessorDeclarations(io::Printer* printer) const;
void GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateInlineAccessorDefinitions(io::Printer* printer,
bool is_inline) const;
void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const {}
void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const;
void GenerateClearingCode(io::Printer* printer) const;
void GenerateSwappingCode(io::Printer* printer) const;
void GenerateConstructorCode(io::Printer* printer) const;
protected:
void GenerateGetterDeclaration(io::Printer* printer) const;
private:
void InternalGenerateInlineAccessorDefinitions(
const map<string, string>& variables, io::Printer* printer) const;
const bool dependent_base_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageOneofFieldGenerator);
};
@ -118,7 +131,12 @@ class RepeatedMessageFieldGenerator : public FieldGenerator {
void GenerateByteSize(io::Printer* printer) const;
private:
void InternalGenerateTypeDependentAccessorDeclarations(
io::Printer* printer) const;
const FieldDescriptor* descriptor_;
const bool dependent_field_;
const bool dependent_getter_;
map<string, string> variables_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator);

View File

@ -421,7 +421,8 @@ GenerateByteSize(io::Printer* printer) const {
StringOneofFieldGenerator::
StringOneofFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: StringFieldGenerator(descriptor, options) {
: StringFieldGenerator(descriptor, options),
dependent_field_(options.proto_h) {
SetCommonOneofFieldVariables(descriptor, &variables_);
}
@ -604,13 +605,29 @@ GenerateInlineAccessorDefinitions(io::Printer* printer,
void StringOneofFieldGenerator::
GenerateClearingCode(io::Printer* printer) const {
if (SupportsArenas(descriptor_)) {
printer->Print(variables_,
"$oneof_prefix$$name$_.Destroy($default_variable$,\n"
" GetArenaNoVirtual());\n");
map<string, string> variables(variables_);
if (dependent_field_) {
variables["this_message"] = DependentBaseDownCast();
// This clearing code may be in the dependent base class. If the default
// value is an empty string, then the $default_variable$ is a global
// singleton. If the default is not empty, we need to down-cast to get the
// default value's global singleton instance. See SetStringVariables() for
// possible values of default_variable.
if (!descriptor_->default_value_string().empty()) {
variables["default_variable"] =
DependentBaseDownCast() + variables["default_variable"];
}
} else {
printer->Print(variables_,
"$oneof_prefix$$name$_.DestroyNoArena($default_variable$);\n");
variables["this_message"] = "";
}
if (SupportsArenas(descriptor_)) {
printer->Print(variables,
"$this_message$$oneof_prefix$$name$_.Destroy($default_variable$,\n"
" $this_message$GetArenaNoVirtual());\n");
} else {
printer->Print(variables,
"$this_message$$oneof_prefix$$name$_."
"DestroyNoArena($default_variable$);\n");
}
}
@ -664,7 +681,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const {
RepeatedStringFieldGenerator::
RepeatedStringFieldGenerator(const FieldDescriptor* descriptor,
const Options& options)
: descriptor_(descriptor) {
: descriptor_(descriptor) {
SetStringVariables(descriptor, &variables_, options);
}

View File

@ -93,6 +93,7 @@ class StringOneofFieldGenerator : public StringFieldGenerator {
void GenerateMergeFromCodedStream(io::Printer* printer) const;
private:
const bool dependent_field_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator);
};

View File

@ -67,7 +67,9 @@
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/dynamic_message.h>
#include <google/protobuf/stubs/callback.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/strutil.h>
#include <google/protobuf/stubs/substitute.h>
#include <google/protobuf/testing/googletest.h>

View File

@ -44,6 +44,7 @@
#include <google/protobuf/io/zero_copy_stream_impl.h>
#include <google/protobuf/stubs/map_util.h>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/stubs/strutil.h>

View File

@ -181,8 +181,8 @@ void EnumGenerator::Generate(io::Printer* printer) {
" internalGetValueMap() {\n"
" return internalValueMap;\n"
"}\n"
"private static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
" internalValueMap =\n"
"private static final com.google.protobuf.Internal.EnumLiteMap<\n"
" $classname$> internalValueMap =\n"
" new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
" public $classname$ findValueByNumber(int number) {\n"
" return $classname$.valueOf(number);\n"

View File

@ -35,6 +35,7 @@
#include <map>
#include <string>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_doc_comment.h>

View File

@ -35,6 +35,7 @@
#include <map>
#include <string>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_doc_comment.h>

View File

@ -0,0 +1,226 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <map>
#include <string>
#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum_lite.h>
#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/descriptor.pb.h>
#include <google/protobuf/stubs/strutil.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
namespace {
bool EnumHasCustomOptions(const EnumDescriptor* descriptor) {
if (descriptor->options().unknown_fields().field_count() > 0) return true;
for (int i = 0; i < descriptor->value_count(); ++i) {
const EnumValueDescriptor* value = descriptor->value(i);
if (value->options().unknown_fields().field_count() > 0) return true;
}
return false;
}
} // namespace
EnumLiteGenerator::EnumLiteGenerator(const EnumDescriptor* descriptor,
bool immutable_api,
Context* context)
: descriptor_(descriptor), immutable_api_(immutable_api),
name_resolver_(context->GetNameResolver()) {
for (int i = 0; i < descriptor_->value_count(); i++) {
const EnumValueDescriptor* value = descriptor_->value(i);
const EnumValueDescriptor* canonical_value =
descriptor_->FindValueByNumber(value->number());
if (value == canonical_value) {
canonical_values_.push_back(value);
} else {
Alias alias;
alias.value = value;
alias.canonical_value = canonical_value;
aliases_.push_back(alias);
}
}
}
EnumLiteGenerator::~EnumLiteGenerator() {}
void EnumLiteGenerator::Generate(io::Printer* printer) {
WriteEnumDocComment(printer, descriptor_);
if (HasDescriptorMethods(descriptor_)) {
printer->Print(
"public enum $classname$\n"
" implements com.google.protobuf.ProtocolMessageEnum {\n",
"classname", descriptor_->name());
} else {
printer->Print(
"public enum $classname$\n"
" implements com.google.protobuf.Internal.EnumLite {\n",
"classname", descriptor_->name());
}
printer->Indent();
for (int i = 0; i < canonical_values_.size(); i++) {
map<string, string> vars;
vars["name"] = canonical_values_[i]->name();
vars["index"] = SimpleItoa(canonical_values_[i]->index());
vars["number"] = SimpleItoa(canonical_values_[i]->number());
WriteEnumValueDocComment(printer, canonical_values_[i]);
if (canonical_values_[i]->options().deprecated()) {
printer->Print("@java.lang.Deprecated\n");
}
printer->Print(vars,
"$name$($index$, $number$),\n");
}
if (SupportUnknownEnumValue(descriptor_->file())) {
printer->Print("UNRECOGNIZED(-1, -1),\n");
}
printer->Print(
";\n"
"\n");
// -----------------------------------------------------------------
for (int i = 0; i < aliases_.size(); i++) {
map<string, string> vars;
vars["classname"] = descriptor_->name();
vars["name"] = aliases_[i].value->name();
vars["canonical_name"] = aliases_[i].canonical_value->name();
WriteEnumValueDocComment(printer, aliases_[i].value);
printer->Print(vars,
"public static final $classname$ $name$ = $canonical_name$;\n");
}
for (int i = 0; i < descriptor_->value_count(); i++) {
map<string, string> vars;
vars["name"] = descriptor_->value(i)->name();
vars["number"] = SimpleItoa(descriptor_->value(i)->number());
WriteEnumValueDocComment(printer, descriptor_->value(i));
printer->Print(vars,
"public static final int $name$_VALUE = $number$;\n");
}
printer->Print("\n");
// -----------------------------------------------------------------
printer->Print(
"\n"
"public final int getNumber() {\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
printer->Print(
" if (index == -1) {\n"
" throw new java.lang.IllegalArgumentException(\n"
" \"Can't get the number of an unknown enum value.\");\n"
" }\n");
}
printer->Print(
" return value;\n"
"}\n"
"\n"
"public static $classname$ valueOf(int value) {\n"
" switch (value) {\n",
"classname", descriptor_->name());
printer->Indent();
printer->Indent();
for (int i = 0; i < canonical_values_.size(); i++) {
printer->Print(
"case $number$: return $name$;\n",
"name", canonical_values_[i]->name(),
"number", SimpleItoa(canonical_values_[i]->number()));
}
printer->Outdent();
printer->Outdent();
printer->Print(
" default: return null;\n"
" }\n"
"}\n"
"\n"
"public static com.google.protobuf.Internal.EnumLiteMap<$classname$>\n"
" internalGetValueMap() {\n"
" return internalValueMap;\n"
"}\n"
"private static final com.google.protobuf.Internal.EnumLiteMap<\n"
" $classname$> internalValueMap =\n"
" new com.google.protobuf.Internal.EnumLiteMap<$classname$>() {\n"
" public $classname$ findValueByNumber(int number) {\n"
" return $classname$.valueOf(number);\n"
" }\n"
" };\n"
"\n",
"classname", descriptor_->name());
printer->Print(
"private final int value;\n\n"
"private $classname$(int index, int value) {\n",
"classname", descriptor_->name());
printer->Print(
" this.value = value;\n"
"}\n");
printer->Print(
"\n"
"// @@protoc_insertion_point(enum_scope:$full_name$)\n",
"full_name", descriptor_->full_name());
printer->Outdent();
printer->Print("}\n\n");
}
bool EnumLiteGenerator::CanUseEnumValues() {
if (canonical_values_.size() != descriptor_->value_count()) {
return false;
}
for (int i = 0; i < descriptor_->value_count(); i++) {
if (descriptor_->value(i)->name() != canonical_values_[i]->name()) {
return false;
}
}
return true;
}
} // namespace java
} // namespace compiler
} // namespace protobuf
} // namespace google

View File

@ -0,0 +1,99 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_LITE_H__
#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_LITE_H__
#include <string>
#include <vector>
#include <google/protobuf/descriptor.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
class Context; // context.h
class ClassNameResolver; // name_resolver.h
}
}
namespace io {
class Printer; // printer.h
}
}
namespace protobuf {
namespace compiler {
namespace java {
class EnumLiteGenerator {
public:
explicit EnumLiteGenerator(const EnumDescriptor* descriptor,
bool immutable_api,
Context* context);
~EnumLiteGenerator();
void Generate(io::Printer* printer);
private:
const EnumDescriptor* descriptor_;
// The proto language allows multiple enum constants to have the same numeric
// value. Java, however, does not allow multiple enum constants to be
// considered equivalent. We treat the first defined constant for any
// given numeric value as "canonical" and the rest as aliases of that
// canonical value.
vector<const EnumValueDescriptor*> canonical_values_;
struct Alias {
const EnumValueDescriptor* value;
const EnumValueDescriptor* canonical_value;
};
vector<Alias> aliases_;
bool immutable_api_;
Context* context_;
ClassNameResolver* name_resolver_;
bool CanUseEnumValues();
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumLiteGenerator);
};
} // namespace java
} // namespace compiler
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_LITE_H__

View File

@ -39,6 +39,7 @@
#include <google/protobuf/stubs/shared_ptr.h>
#endif
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/compiler/java/java_context.h>
#include <google/protobuf/compiler/java/java_enum_field.h>

View File

@ -44,6 +44,7 @@
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/descriptor.h>
#include <google/protobuf/stubs/logging.h>
namespace google {
namespace protobuf {

View File

@ -332,6 +332,10 @@ inline bool PreserveUnknownFields(const Descriptor* descriptor) {
return descriptor->file()->syntax() != FileDescriptor::SYNTAX_PROTO3;
}
inline bool IsAnyMessage(const Descriptor* descriptor) {
return descriptor->full_name() == "google.protobuf.Any";
}
} // namespace java
} // namespace compiler
} // namespace protobuf

View File

@ -314,6 +314,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
" internalGetMutable$capitalized_name$().getMutableMap(),\n"
" $name$ValueConverter);\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"$deprecation$public Builder putAll$capitalized_name$(\n"
" java.util.Map<$boxed_key_type$, $value_enum_type$> values) {\n"
" getMutable$capitalized_name$().putAll(values);\n"
" return this;\n"
"}\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
WriteFieldDocComment(printer, descriptor_);
printer->Print(
@ -331,6 +339,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
"getMutable$capitalized_name$Value() {\n"
" return internalGetMutable$capitalized_name$().getMutableMap();\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"$deprecation$public Builder putAll$capitalized_name$Value(\n"
" java.util.Map<$boxed_key_type$, $boxed_value_type$> values) {\n"
" getMutable$capitalized_name$Value().putAll(values);\n"
" return this;\n"
"}\n");
}
} else {
WriteFieldDocComment(printer, descriptor_);
@ -346,6 +362,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
"getMutable$capitalized_name$() {\n"
" return internalGetMutable$capitalized_name$().getMutableMap();\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"$deprecation$public Builder putAll$capitalized_name$(\n"
" java.util.Map<$type_parameters$> values) {\n"
" getMutable$capitalized_name$().putAll(values);\n"
" return this;\n"
"}\n");
}
}

View File

@ -303,6 +303,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
" copyOnWrite();\n"
" return instance.getMutable$capitalized_name$();\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"$deprecation$public Builder putAll$capitalized_name$(\n"
" java.util.Map<$boxed_key_type$, $value_enum_type$> values) {\n"
" getMutable$capitalized_name$().putAll(values);\n"
" return this;\n"
"}\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
WriteFieldDocComment(printer, descriptor_);
printer->Print(
@ -321,6 +329,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
" copyOnWrite();\n"
" return instance.getMutable$capitalized_name$Value();\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"$deprecation$public Builder putAll$capitalized_name$Value(\n"
" java.util.Map<$boxed_key_type$, $boxed_value_type$> values) {\n"
" getMutable$capitalized_name$Value().putAll(values);\n"
" return this;\n"
"}\n");
}
} else {
WriteFieldDocComment(printer, descriptor_);
@ -337,6 +353,14 @@ GenerateBuilderMembers(io::Printer* printer) const {
" copyOnWrite();\n"
" return instance.getMutable$capitalized_name$();\n"
"}\n");
WriteFieldDocComment(printer, descriptor_);
printer->Print(
variables_,
"public Builder putAll$capitalized_name$(\n"
" java.util.Map<$type_parameters$> values) {\n"
" getMutable$capitalized_name$().putAll(values);\n"
" return this;\n"
"}\n");
}
}

View File

@ -255,6 +255,18 @@ void ImmutableMessageGenerator::GenerateInterface(io::Printer* printer) {
field_generators_.get(descriptor_->field(i))
.GenerateInterfaceMembers(printer);
}
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
printer->Print(
"\n"
"public $classname$.$oneof_capitalized_name$Case "
"get$oneof_capitalized_name$Case();\n",
"oneof_capitalized_name",
context_->GetOneofGeneratorInfo(
descriptor_->oneof_decl(i))->capitalized_name,
"classname",
context_->GetNameResolver()->GetImmutableClassName(
descriptor_));
}
printer->Outdent();
printer->Print("}\n");
@ -292,8 +304,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) {
" com.google.protobuf.GeneratedMessage implements\n"
" $extra_interfaces$\n"
" $classname$OrBuilder {\n");
builder_type = "com.google.protobuf.GeneratedMessage.Builder";
builder_type = "com.google.protobuf.GeneratedMessage.Builder<?>";
}
printer->Indent();
// Using builder_type, instead of Builder, prevents the Builder class from
@ -435,6 +446,10 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) {
"\n");
}
if (IsAnyMessage(descriptor_)) {
GenerateAnyMethods(printer);
}
// Fields
for (int i = 0; i < descriptor_->field_count(); i++) {
printer->Print("public static final int $constant_name$ = $number$;\n",
@ -578,9 +593,8 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
printer->Print(
"}\n"
"\n"
"private int memoizedSerializedSize = -1;\n"
"public int getSerializedSize() {\n"
" int size = memoizedSerializedSize;\n"
" int size = memoizedSize;\n"
" if (size != -1) return size;\n"
"\n"
" size = 0;\n");
@ -612,7 +626,7 @@ GenerateMessageSerializationMethods(io::Printer* printer) {
printer->Outdent();
printer->Print(
" memoizedSerializedSize = size;\n"
" memoizedSize = size;\n"
" return size;\n"
"}\n"
"\n");
@ -948,22 +962,58 @@ GenerateEqualsAndHashCode(io::Printer* printer) {
printer->Print("boolean result = true;\n");
for (int i = 0; i < descriptor_->field_count(); i++) {
const FieldDescriptor* field = descriptor_->field(i);
const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field);
if (check_has_bits) {
printer->Print(
"result = result && (has$name$() == other.has$name$());\n"
"if (has$name$()) {\n",
"name", info->capitalized_name);
printer->Indent();
}
field_generators_.get(field).GenerateEqualsCode(printer);
if (check_has_bits) {
printer->Outdent();
printer->Print(
"}\n");
if (field->containing_oneof() == NULL) {
const FieldGeneratorInfo* info = context_->GetFieldGeneratorInfo(field);
bool check_has_bits = CheckHasBitsForEqualsAndHashCode(field);
if (check_has_bits) {
printer->Print(
"result = result && (has$name$() == other.has$name$());\n"
"if (has$name$()) {\n",
"name", info->capitalized_name);
printer->Indent();
}
field_generators_.get(field).GenerateEqualsCode(printer);
if (check_has_bits) {
printer->Outdent();
printer->Print(
"}\n");
}
}
}
// Compare oneofs.
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
printer->Print(
"result = result && get$oneof_capitalized_name$Case().equals(\n"
" other.get$oneof_capitalized_name$Case());\n",
"oneof_capitalized_name",
context_->GetOneofGeneratorInfo(
descriptor_->oneof_decl(i))->capitalized_name);
printer->Print(
"if (!result) return false;\n"
"switch ($oneof_name$Case_) {\n",
"oneof_name",
context_->GetOneofGeneratorInfo(
descriptor_->oneof_decl(i))->name);
printer->Indent();
for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) {
const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j);
printer->Print(
"case $field_number$:\n",
"field_number",
SimpleItoa(field->number()));
printer->Indent();
field_generators_.get(field).GenerateEqualsCode(printer);
printer->Print("break;\n");
printer->Outdent();
}
printer->Print(
"case 0:\n"
"default:\n");
printer->Outdent();
printer->Print("}\n");
}
if (PreserveUnknownFields(descriptor_)) {
// Always consider unknown fields for equality. This will sometimes return
// false for non-canonical ordering when running in LITE_RUNTIME but it's
@ -1198,7 +1248,7 @@ GenerateParsingConstructor(io::Printer* printer) {
// ===================================================================
void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) {
printer->Print(
"public static final com.google.protobuf.Parser<$classname$> PARSER =\n"
"private static final com.google.protobuf.Parser<$classname$> PARSER =\n"
" new com.google.protobuf.AbstractParser<$classname$>() {\n",
"classname", descriptor_->name());
printer->Indent();
@ -1250,6 +1300,10 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) {
"\n");
printer->Print(
"public static com.google.protobuf.Parser<$classname$> parser() {\n"
" return PARSER;\n"
"}\n"
"\n"
"@java.lang.Override\n"
"public com.google.protobuf.Parser<$classname$> getParserForType() {\n"
" return PARSER;\n"
@ -1269,6 +1323,50 @@ void ImmutableMessageGenerator::GenerateInitializers(io::Printer* printer) {
}
void ImmutableMessageGenerator::GenerateAnyMethods(io::Printer* printer) {
printer->Print(
"private static String getTypeUrl(\n"
" com.google.protobuf.Descriptors.Descriptor descriptor) {\n"
" return \"type.googleapis.com/\" + descriptor.getFullName();\n"
"}\n"
"\n"
"public static <T extends com.google.protobuf.Message> Any pack(\n"
" T message) {\n"
" return Any.newBuilder()\n"
" .setTypeUrl(getTypeUrl(message.getDescriptorForType()))\n"
" .setValue(message.toByteString())\n"
" .build();\n"
"}\n"
"\n"
"public <T extends com.google.protobuf.Message> boolean is(\n"
" java.lang.Class<T> clazz) {\n"
" T defaultInstance =\n"
" com.google.protobuf.Internal.getDefaultInstance(clazz);\n"
" return getTypeUrl().equals(\n"
" getTypeUrl(defaultInstance.getDescriptorForType()));\n"
"}\n"
"\n"
"private volatile com.google.protobuf.Message cachedUnpackValue;\n"
"\n"
"public <T extends com.google.protobuf.Message> T unpack(\n"
" java.lang.Class<T> clazz)\n"
" throws com.google.protobuf.InvalidProtocolBufferException {\n"
" if (!is(clazz)) {\n"
" throw new com.google.protobuf.InvalidProtocolBufferException(\n"
" \"Type of the Any messsage does not match the given class.\");\n"
" }\n"
" if (cachedUnpackValue != null) {\n"
" return (T) cachedUnpackValue;\n"
" }\n"
" T defaultInstance =\n"
" com.google.protobuf.Internal.getDefaultInstance(clazz);\n"
" T result = (T) defaultInstance.getParserForType()\n"
" .parseFrom(getValue());\n"
" cachedUnpackValue = result;\n"
" return result;\n"
"}\n");
}
} // namespace java
} // namespace compiler
} // namespace protobuf

Some files were not shown because too many files have changed in this diff Show More