diff --git a/cmake/install.cmake b/cmake/install.cmake index e14ef1d27f..dbb4265d45 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -1,103 +1,103 @@ -include(GNUInstallDirs) - -foreach(_library - libprotobuf-lite - libprotobuf - libprotoc) - set_property(TARGET ${_library} - PROPERTY INTERFACE_INCLUDE_DIRECTORIES - $) - 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(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) diff --git a/cmake/protobuf-config-version.cmake.in b/cmake/protobuf-config-version.cmake.in index 07ab40b990..1f171c6649 100644 --- a/cmake/protobuf-config-version.cmake.in +++ b/cmake/protobuf-config-version.cmake.in @@ -1 +1 @@ -set(PACKAGE_VERSION @protobuf_VERSION@) +set(PACKAGE_VERSION @protobuf_VERSION@) diff --git a/cmake/protobuf-config.cmake.in b/cmake/protobuf-config.cmake.in index 51d715cfa4..bb0997b88b 100644 --- a/cmake/protobuf-config.cmake.in +++ b/cmake/protobuf-config.cmake.in @@ -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) diff --git a/cmake/tests.cmake b/cmake/tests.cmake index 9215108737..ef4dfe0221 100644 --- a/cmake/tests.cmake +++ b/cmake/tests.cmake @@ -99,6 +99,12 @@ set(common_test_files ${protobuf_source_dir}/src/google/protobuf/testing/googletest.cc ) +set(common_lite_test_files + ${protobuf_source_dir}/src/google/protobuf/arena_test_util.cc + ${protobuf_source_dir}/src/google/protobuf/map_lite_test_util.cc + ${protobuf_source_dir}/src/google/protobuf/test_util_lite.cc +) + set(tests_files ${protobuf_source_dir}/src/google/protobuf/any_test.cc ${protobuf_source_dir}/src/google/protobuf/arena_unittest.cc @@ -179,10 +185,13 @@ add_executable(test_plugin ${test_plugin_files}) target_link_libraries(test_plugin libprotoc libprotobuf gmock) set(lite_test_files - ${protobuf_source_dir}/src/google/protobuf/arena_test_util.cc ${protobuf_source_dir}/src/google/protobuf/lite_unittest.cc - ${protobuf_source_dir}/src/google/protobuf/map_lite_test_util.cc - ${protobuf_source_dir}/src/google/protobuf/test_util_lite.cc ) -add_executable(lite-test ${lite_test_files} ${lite_test_proto_files}) +add_executable(lite-test ${lite_test_files} ${common_lite_test_files} ${lite_test_proto_files}) target_link_libraries(lite-test libprotobuf-lite) + +set(lite_arena_test_files + ${protobuf_source_dir}/src/google/protobuf/lite_arena_unittest.cc +) +add_executable(lite-arena-test ${lite_arena_test_files} ${common_lite_test_files} ${lite_test_proto_files}) +target_link_libraries(lite-arena-test libprotobuf-lite gmock_main) diff --git a/java/pom.xml b/java/pom.xml index fb7e4168e9..49099b4ae8 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -142,6 +142,7 @@ + diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java index 1d5d4e8a5e..0bd1750d3f 100644 --- a/java/src/main/java/com/google/protobuf/ByteString.java +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -294,10 +294,10 @@ public abstract class ByteString implements Iterable, Serializable { * Performance notes: 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. + * + *

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, Serializable { * * Performance notes: 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. + * + *

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

* The methods in the returned {@link InputStream} might not be * thread safe. diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java index 954fde0861..291bd20abb 100644 --- a/java/src/main/java/com/google/protobuf/CodedOutputStream.java +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -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); } } diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java index 3658410cd6..7cfc47f78f 100644 --- a/java/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -31,6 +31,7 @@ package com.google.protobuf; import com.google.protobuf.DescriptorProtos.*; +import com.google.protobuf.Descriptors.FileDescriptor.Syntax; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -912,7 +913,17 @@ public final class Descriptors { /** For internal use only. */ public boolean needsUtf8Check() { - return (type == Type.STRING) && (getFile().getOptions().getJavaStringCheckUtf8()); + if (type != Type.STRING) { + return false; + } + if (getContainingType().getOptions().getMapEntry()) { + // Always enforce strict UTF-8 checking for map fields. + return true; + } + if (getFile().getSyntax() == Syntax.PROTO3) { + return true; + } + return getFile().getOptions().getJavaStringCheckUtf8(); } public boolean isMapField() { @@ -1118,9 +1129,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."); } } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java index 9457d99961..d84fa75c51 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessage.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -121,22 +121,44 @@ public abstract class GeneratedMessage extends AbstractMessage final TreeMap result = new TreeMap(); 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 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 result = new TreeMap(); 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 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) 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); + } + } } diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java index bd6bc463dd..a535b71833 100644 --- a/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java +++ b/java/src/main/java/com/google/protobuf/GeneratedMessageLite.java @@ -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> 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, PrototypeHolder> PROTOTYPE_MAP = - new ConcurrentHashMap, PrototypeHolder>(); - - // For use by generated code only. - protected static < - MessageType extends GeneratedMessageLite, - BuilderType extends GeneratedMessageLite.Builder< - MessageType, BuilderType>> void onLoad(Class clazz, - PrototypeHolder 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 getParserForType() { - return (Parser) PROTOTYPE_MAP - .get(getClass()).getParserForType(); + return (Parser) 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< *

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

    - *
  • {@link #getDefaultInstanceForType()} - *
  • {@link #getParserForType()} - *
  • {@link #newBuilderForType()} - *
- * This allows us to trade three generated methods for a static Map. - */ - protected static class PrototypeHolder< - MessageType extends GeneratedMessageLite, - BuilderType extends GeneratedMessageLite.Builder< - MessageType, BuilderType>> { - - private final MessageType defaultInstance; - private final Parser parser; - - public PrototypeHolder( - MessageType defaultInstance, Parser parser) { - this.defaultInstance = defaultInstance; - this.parser = parser; - } - - public MessageType getDefaultInstanceForType() { - return defaultInstance; - } - - public Parser 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. diff --git a/java/src/main/java/com/google/protobuf/Internal.java b/java/src/main/java/com/google/protobuf/Internal.java index 20054b7917..fefda90406 100644 --- a/java/src/main/java/com/google/protobuf/Internal.java +++ b/java/src/main/java/com/google/protobuf/Internal.java @@ -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 getDefaultInstance(Class 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. */ diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java index 367fa23ba3..0a76105278 100644 --- a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java +++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java @@ -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."); } diff --git a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java index a2997e1cb6..c3be3cca8f 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringArrayList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringArrayList.java @@ -215,6 +215,11 @@ public class LazyStringArrayList extends AbstractProtobufList modCount++; } + @Override + public Object getRaw(int index) { + return list.get(index); + } + // @Override public ByteString getByteString(int index) { Object o = list.get(index); diff --git a/java/src/main/java/com/google/protobuf/LazyStringList.java b/java/src/main/java/com/google/protobuf/LazyStringList.java index 235126b685..3eeedca1d3 100644 --- a/java/src/main/java/com/google/protobuf/LazyStringList.java +++ b/java/src/main/java/com/google/protobuf/LazyStringList.java @@ -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[]. * diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java index fa0265e2e3..9516d71f29 100644 --- a/java/src/main/java/com/google/protobuf/Message.java +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -121,6 +121,9 @@ public interface Message extends MessageLite, MessageOrBuilder { * using the same merging rules.
* * 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++. */ diff --git a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java index be737b1a32..f91cdbced6 100644 --- a/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java +++ b/java/src/main/java/com/google/protobuf/RepeatedFieldBuilder.java @@ -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 messages; // Whether messages is an mutable array that can be modified. diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java index 7ea8402211..45d5fc357f 100644 --- a/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSetLite.java @@ -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 internal representation of the unknown fields. + * The boxed values of the elements in the set. */ - private final ByteString byteString; + private Object[] objects; + + /** + * The lazily computed serialized size of the set. + */ + private int memoizedSerializedSize = -1; /** - * Constructs the {@code UnknownFieldSetLite} as a thin wrapper around {@link ByteString}. + * Constructs the {@code UnknownFieldSetLite}. */ - 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 { *

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

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; + } + + 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 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 { *

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); } } } diff --git a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java index 5cc005df88..5257c5a235 100644 --- a/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java +++ b/java/src/main/java/com/google/protobuf/UnmodifiableLazyStringList.java @@ -57,6 +57,11 @@ public class UnmodifiableLazyStringList extends AbstractList public String get(int index) { return list.get(index); } + + @Override + public Object getRaw(int index) { + return list.getRaw(index); + } @Override public int size() { diff --git a/java/src/main/java/com/google/protobuf/Utf8.java b/java/src/main/java/com/google/protobuf/Utf8.java index 4271b41bc9..0699778f73 100644 --- a/java/src/main/java/com/google/protobuf/Utf8.java +++ b/java/src/main/java/com/google/protobuf/Utf8.java @@ -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. } diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java index ba83b6665a..8dbe1ae33c 100644 --- a/java/src/main/java/com/google/protobuf/WireFormat.java +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -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; } diff --git a/java/src/test/java/com/google/protobuf/AnyTest.java b/java/src/test/java/com/google/protobuf/AnyTest.java new file mode 100644 index 0000000000..e169f69d24 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/AnyTest.java @@ -0,0 +1,92 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf; + +import any_test.AnyTestProto.TestAny; +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +/** + * Unit tests for Any message. + */ +public class AnyTest extends TestCase { + public void testAnyGeneratedApi() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + TestAny container = TestAny.newBuilder() + .setValue(Any.pack(message)).build(); + + assertTrue(container.getValue().is(TestAllTypes.class)); + assertFalse(container.getValue().is(TestAny.class)); + + TestAllTypes result = container.getValue().unpack(TestAllTypes.class); + TestUtil.assertAllFieldsSet(result); + + + // Unpacking to a wrong type will throw an exception. + try { + TestAny wrongMessage = container.getValue().unpack(TestAny.class); + fail("Exception is expected."); + } catch (InvalidProtocolBufferException e) { + // expected. + } + + // Test that unpacking throws an exception if parsing fails. + TestAny.Builder containerBuilder = container.toBuilder(); + containerBuilder.getValueBuilder().setValue( + ByteString.copyFrom(new byte[]{0x11})); + container = containerBuilder.build(); + try { + TestAllTypes parsingFailed = container.getValue().unpack(TestAllTypes.class); + fail("Exception is expected."); + } catch (InvalidProtocolBufferException e) { + // expected. + } + } + + public void testCachedUnpackResult() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + TestAny container = TestAny.newBuilder() + .setValue(Any.pack(message)).build(); + + assertTrue(container.getValue().is(TestAllTypes.class)); + + TestAllTypes result1 = container.getValue().unpack(TestAllTypes.class); + TestAllTypes result2 = container.getValue().unpack(TestAllTypes.class); + assertTrue(result1 == result2); + } +} diff --git a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java index 1562a1a604..447e6ef31f 100644 --- a/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/BoundedByteStringTest.java @@ -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); diff --git a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java b/java/src/test/java/com/google/protobuf/CheckUtf8Test.java index 6470e9ca03..3d6381c9b1 100644 --- a/java/src/test/java/com/google/protobuf/CheckUtf8Test.java +++ b/java/src/test/java/com/google/protobuf/CheckUtf8Test.java @@ -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()); diff --git a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java index 365789c043..360e759e01 100644 --- a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java +++ b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java @@ -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); + } } diff --git a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java index acf2b023f0..eaeec0b8ae 100644 --- a/java/src/test/java/com/google/protobuf/FieldPresenceTest.java +++ b/java/src/test/java/com/google/protobuf/FieldPresenceTest.java @@ -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. diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java index 2bd8d1a9de..70812b953f 100644 --- a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -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()); diff --git a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java index acd180036a..0ef414aac6 100644 --- a/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java +++ b/java/src/test/java/com/google/protobuf/LazyStringEndToEndTest.java @@ -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 { diff --git a/java/src/test/java/com/google/protobuf/LiteTest.java b/java/src/test/java/com/google/protobuf/LiteTest.java index 8c3b5e5c72..b1f298ff40 100644 --- a/java/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/src/test/java/com/google/protobuf/LiteTest.java @@ -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(); diff --git a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java index 958b6a7ecd..7dfda2ae05 100644 --- a/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/LiteralByteStringTest.java @@ -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. + *

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."); diff --git a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java index 6cff689ffa..3d8c9bc4bf 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2LiteTest.java @@ -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); } diff --git a/java/src/test/java/com/google/protobuf/MapForProto2Test.java b/java/src/test/java/com/google/protobuf/MapForProto2Test.java index 7e984040f6..1fa3cbdbae 100644 --- a/java/src/test/java/com/google/protobuf/MapForProto2Test.java +++ b/java/src/test/java/com/google/protobuf/MapForProto2Test.java @@ -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); } diff --git a/java/src/test/java/com/google/protobuf/MapTest.java b/java/src/test/java/com/google/protobuf/MapTest.java index 0509be152b..0e5c12840c 100644 --- a/java/src/test/java/com/google/protobuf/MapTest.java +++ b/java/src/test/java/com/google/protobuf/MapTest.java @@ -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); } diff --git a/java/src/test/java/com/google/protobuf/ParserTest.java b/java/src/test/java/com/google/protobuf/ParserTest.java index b11d8cb96b..5a92bacfbf 100644 --- a/java/src/test/java/com/google/protobuf/ParserTest.java +++ b/java/src/test/java/com/google/protobuf/ParserTest.java @@ -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 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()); diff --git a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java index bd0d15e341..4ec3a40988 100644 --- a/java/src/test/java/com/google/protobuf/RopeByteStringTest.java +++ b/java/src/test/java/com/google/protobuf/RopeByteStringTest.java @@ -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); diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java index 19a96d0e47..792e866580 100644 --- a/java/src/test/java/com/google/protobuf/TestUtil.java +++ b/java/src/test/java/com/google/protobuf/TestUtil.java @@ -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()); diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java index 5d8466465b..8294b8655d 100644 --- a/java/src/test/java/com/google/protobuf/TextFormatTest.java +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -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; diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java index 93a5ee22ad..8c9dcafeed 100644 --- a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java +++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java @@ -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()); diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java index 6858524eac..0175005d2c 100644 --- a/java/src/test/java/com/google/protobuf/WireFormatTest.java +++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java @@ -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; diff --git a/java/src/test/java/com/google/protobuf/any_test.proto b/java/src/test/java/com/google/protobuf/any_test.proto new file mode 100644 index 0000000000..80173d8a0e --- /dev/null +++ b/java/src/test/java/com/google/protobuf/any_test.proto @@ -0,0 +1,42 @@ +// 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 any_test; + +option java_package = "any_test"; +option java_outer_classname = "AnyTestProto"; + +import "google/protobuf/any.proto"; + +message TestAny { + google.protobuf.Any value = 1; +} diff --git a/java/util/pom.xml b/java/util/pom.xml new file mode 100644 index 0000000000..9416f380d3 --- /dev/null +++ b/java/util/pom.xml @@ -0,0 +1,202 @@ + + + 4.0.0 + + com.google + google + 1 + + com.google.protobuf + protobuf-java-util + 3.0.0-alpha-4-pre + bundle + Protocol Buffer Java API + + Protocol Buffers are a way of encoding structured data in an efficient yet + extensible format. + + 2008 + https://developers.google.com/protocol-buffers/ + + + New BSD license + http://www.opensource.org/licenses/bsd-license.php + repo + + + + https://github.com/google/protobuf + + scm:git:https://github.com/google/protobuf.git + + + + + com.google.protobuf + protobuf-java + 3.0.0-alpha-4-pre + compile + + + com.google.guava + guava + 18.0 + compile + + + com.google.code.gson + gson + 2.3 + compile + + + junit + junit + 4.4 + test + + + org.easymock + easymock + 2.2 + test + + + org.easymock + easymockclassextension + 2.2.1 + test + + + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + maven-surefire-plugin + + + **/*Test.java + ../src/main/java/com/google/protobuf/TestUtil.java + + + + + maven-antrun-plugin + + + generate-test-sources + generate-test-sources + + + + + + + + + + + + + + target/generated-test-sources + + + run + + + + + + org.apache.felix + maven-bundle-plugin + true + + + https://developers.google.com/protocol-buffers/ + com.google.protobuf.util + com.google.protobuf.util;version=3.0.0-alpha-3 + + + + + + + + release + + + sonatype-nexus-staging + https://oss.sonatype.org/content/repositories/snapshots + + + sonatype-nexus-staging + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.3 + true + + sonatype-nexus-staging + https://oss.sonatype.org/ + false + + + + + + + diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java new file mode 100644 index 0000000000..dc2f4b841e --- /dev/null +++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java @@ -0,0 +1,259 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.FieldMask; +import com.google.protobuf.Message; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.logging.Logger; + +/** + * A tree representation of a FieldMask. Each leaf node in this tree represent + * a field path in the FieldMask. + * + *

For example, FieldMask "foo.bar,foo.baz,bar.baz" as a tree will be: + *

+ *   [root] -+- foo -+- bar
+ *           |       |
+ *           |       +- baz
+ *           |
+ *           +- bar --- baz
+ * 
+ * + *

By representing FieldMasks with this tree structure we can easily convert + * a FieldMask to a canonical form, merge two FieldMasks, calculate the + * intersection to two FieldMasks and traverse all fields specified by the + * FieldMask in a message tree. + */ +class FieldMaskTree { + private static final Logger logger = + Logger.getLogger(FieldMaskTree.class.getName()); + + private static final String FIELD_PATH_SEPARATOR_REGEX = "\\."; + + private static class Node { + public TreeMap children = new TreeMap(); + } + + private final Node root = new Node(); + + /** Creates an empty FieldMaskTree. */ + public FieldMaskTree() {} + + /** Creates a FieldMaskTree for a given FieldMask. */ + public FieldMaskTree(FieldMask mask) { + mergeFromFieldMask(mask); + } + + @Override + public String toString() { + return FieldMaskUtil.toString(toFieldMask()); + } + + /** + * Adds a field path to the tree. In a FieldMask, every field path matches the + * specified field as well as all its sub-fields. For example, a field path + * "foo.bar" matches field "foo.bar" and also "foo.bar.baz", etc. When adding + * a field path to the tree, redundant sub-paths will be removed. That is, + * after adding "foo.bar" to the tree, "foo.bar.baz" will be removed if it + * exists, which will turn the tree node for "foo.bar" to a leaf node. + * Likewise, if the field path to add is a sub-path of an existing leaf node, + * nothing will be changed in the tree. + */ + public FieldMaskTree addFieldPath(String path) { + String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX); + if (parts.length == 0) { + return this; + } + Node node = root; + boolean createNewBranch = false; + // Find the matching node in the tree. + for (String part : parts) { + // Check whether the path matches an existing leaf node. + if (!createNewBranch && node != root && node.children.isEmpty()) { + // The path to add is a sub-path of an existing leaf node. + return this; + } + if (node.children.containsKey(part)) { + node = node.children.get(part); + } else { + createNewBranch = true; + Node tmp = new Node(); + node.children.put(part, tmp); + node = tmp; + } + } + // Turn the matching node into a leaf node (i.e., remove sub-paths). + node.children.clear(); + return this; + } + + /** + * Merges all field paths in a FieldMask into this tree. + */ + public FieldMaskTree mergeFromFieldMask(FieldMask mask) { + for (String path : mask.getPathsList()) { + addFieldPath(path); + } + return this; + } + + /** Converts this tree to a FieldMask. */ + public FieldMask toFieldMask() { + if (root.children.isEmpty()) { + return FieldMask.getDefaultInstance(); + } + List paths = new ArrayList(); + getFieldPaths(root, "", paths); + return FieldMask.newBuilder().addAllPaths(paths).build(); + } + + /** Gathers all field paths in a sub-tree. */ + private void getFieldPaths(Node node, String path, List paths) { + if (node.children.isEmpty()) { + paths.add(path); + return; + } + for (Entry entry : node.children.entrySet()) { + String childPath = path.isEmpty() + ? entry.getKey() : path + "." + entry.getKey(); + getFieldPaths(entry.getValue(), childPath, paths); + } + } + + /** + * Adds the intersection of this tree with the given {@code path} to + * {@code output}. + */ + public void intersectFieldPath(String path, FieldMaskTree output) { + if (root.children.isEmpty()) { + return; + } + String[] parts = path.split(FIELD_PATH_SEPARATOR_REGEX); + if (parts.length == 0) { + return; + } + Node node = root; + for (String part : parts) { + if (node != root && node.children.isEmpty()) { + // The given path is a sub-path of an existing leaf node in the tree. + output.addFieldPath(path); + return; + } + if (node.children.containsKey(part)) { + node = node.children.get(part); + } else { + return; + } + } + // We found a matching node for the path. All leaf children of this matching + // node is in the intersection. + List paths = new ArrayList(); + getFieldPaths(node, path, paths); + for (String value : paths) { + output.addFieldPath(value); + } + } + + /** + * Merges all fields specified by this FieldMaskTree from {@code source} to + * {@code destination}. + */ + public void merge(Message source, Message.Builder destination, + FieldMaskUtil.MergeOptions options) { + if (source.getDescriptorForType() != destination.getDescriptorForType()) { + throw new IllegalArgumentException( + "Cannot merge messages of different types."); + } + if (root.children.isEmpty()) { + return; + } + merge(root, "", source, destination, options); + } + + /** Merges all fields specified by a sub-tree from {@code source} to + * {@code destination}. + */ + private void merge(Node node, String path, Message source, + Message.Builder destination, FieldMaskUtil.MergeOptions options) { + assert source.getDescriptorForType() == destination.getDescriptorForType(); + + Descriptor descriptor = source.getDescriptorForType(); + for (Entry entry : node.children.entrySet()) { + FieldDescriptor field = + descriptor.findFieldByName(entry.getKey()); + if (field == null) { + logger.warning("Cannot find field \"" + entry.getKey() + + "\" in message type " + descriptor.getFullName()); + continue; + } + if (!entry.getValue().children.isEmpty()) { + if (field.isRepeated() + || field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + logger.warning("Field \"" + field.getFullName() + "\" is not a " + + "singluar message field and cannot have sub-fields."); + continue; + } + String childPath = path.isEmpty() + ? entry.getKey() : path + "." + entry.getKey(); + merge(entry.getValue(), childPath, (Message) source.getField(field), + destination.getFieldBuilder(field), options); + continue; + } + if (field.isRepeated()) { + if (options.replaceRepeatedFields()) { + destination.setField(field, source.getField(field)); + } else { + for (Object element : (List) source.getField(field)) { + destination.addRepeatedField(field, element); + } + } + } else { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (options.replaceMessageFields()) { + destination.setField(field, source.getField(field)); + } else { + destination.getFieldBuilder(field).mergeFrom( + (Message) source.getField(field)); + } + } else { + destination.setField(field, source.getField(field)); + } + } + } + } +} diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java new file mode 100644 index 0000000000..7bf8785856 --- /dev/null +++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskUtil.java @@ -0,0 +1,222 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.FieldMask; +import com.google.protobuf.Internal; +import com.google.protobuf.Message; + +import java.util.Arrays; +import java.util.List; + +/** + * Utility helper functions to work with {@link com.google.protobuf.FieldMask}. + */ +public class FieldMaskUtil { + private static final String FIELD_PATH_SEPARATOR = ","; + private static final String FIELD_PATH_SEPARATOR_REGEX = ","; + private static final String FIELD_SEPARATOR_REGEX = "\\."; + + private FieldMaskUtil() {} + + /** + * Converts a FieldMask to a string. + */ + public static String toString(FieldMask fieldMask) { + StringBuilder result = new StringBuilder(); + boolean first = true; + for (String value : fieldMask.getPathsList()) { + if (value.isEmpty()) { + // Ignore empty paths. + continue; + } + if (first) { + first = false; + } else { + result.append(FIELD_PATH_SEPARATOR); + } + result.append(value); + } + return result.toString(); + } + + /** + * Parses from a string to a FieldMask. + */ + public static FieldMask fromString(String value) { + return fromStringList( + null, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX))); + } + + /** + * Parses from a string to a FieldMask and validates all field paths. + * + * @throws IllegalArgumentException if any of the field path is invalid. + */ + public static FieldMask fromString(Class type, String value) + throws IllegalArgumentException { + return fromStringList( + type, Arrays.asList(value.split(FIELD_PATH_SEPARATOR_REGEX))); + } + + /** + * Constructs a FieldMask for a list of field paths in a certain type. + * + * @throws IllegalArgumentException if any of the field path is not valid. + */ + public static FieldMask fromStringList( + Class type, List paths) + throws IllegalArgumentException { + FieldMask.Builder builder = FieldMask.newBuilder(); + for (String path : paths) { + if (path.isEmpty()) { + // Ignore empty field paths. + continue; + } + if (type != null && !isValid(type, path)) { + throw new IllegalArgumentException( + path + " is not a valid path for " + type); + } + builder.addPaths(path); + } + return builder.build(); + } + + /** + * Checks whether a given field path is valid. + */ + public static boolean isValid(Class type, String path) { + String[] parts = path.split(FIELD_SEPARATOR_REGEX); + if (parts.length == 0) { + return false; + } + Descriptor descriptor = + Internal.getDefaultInstance(type).getDescriptorForType(); + for (String name : parts) { + if (descriptor == null) { + return false; + } + FieldDescriptor field = descriptor.findFieldByName(name); + if (field == null) { + return false; + } + if (!field.isRepeated() + && field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + descriptor = field.getMessageType(); + } else { + descriptor = null; + } + } + return true; + } + + /** + * Converts a FieldMask to its canonical form. In the canonical form of a + * FieldMask, all field paths are sorted alphabetically and redundant field + * paths are moved. + */ + public static FieldMask normalize(FieldMask mask) { + return new FieldMaskTree(mask).toFieldMask(); + } + + /** + * Creates an union of two FieldMasks. + */ + public static FieldMask union(FieldMask mask1, FieldMask mask2) { + return new FieldMaskTree(mask1).mergeFromFieldMask(mask2).toFieldMask(); + } + + /** + * Calculates the intersection of two FieldMasks. + */ + public static FieldMask intersection(FieldMask mask1, FieldMask mask2) { + FieldMaskTree tree = new FieldMaskTree(mask1); + FieldMaskTree result = new FieldMaskTree(); + for (String path : mask2.getPathsList()) { + tree.intersectFieldPath(path, result); + } + return result.toFieldMask(); + } + + /** + * Options to customize merging behavior. + */ + public static class MergeOptions { + private boolean replaceMessageFields = false; + private boolean replaceRepeatedFields = false; + + /** + * Whether to replace message fields (i.e., discard existing content in + * destination message fields) when merging. + * Default behavior is to merge the source message field into the + * destination message field. + */ + public boolean replaceMessageFields() { + return replaceMessageFields; + } + + /** + * Whether to replace repeated fields (i.e., discard existing content in + * destination repeated fields) when merging. + * Default behavior is to append elements from source repeated field to the + * destination repeated field. + */ + public boolean replaceRepeatedFields() { + return replaceRepeatedFields; + } + + public void setReplaceMessageFields(boolean value) { + replaceMessageFields = value; + } + + public void setReplaceRepeatedFields(boolean value) { + replaceRepeatedFields = value; + } + } + + /** + * Merges fields specified by a FieldMask from one message to another. + */ + public static void merge(FieldMask mask, Message source, + Message.Builder destination, MergeOptions options) { + new FieldMaskTree(mask).merge(source, destination, options); + } + + /** + * Merges fields specified by a FieldMask from one message to another. + */ + public static void merge(FieldMask mask, Message source, + Message.Builder destination) { + merge(mask, source, destination, new MergeOptions()); + } +} diff --git a/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java new file mode 100644 index 0000000000..c9a3915368 --- /dev/null +++ b/java/util/src/main/java/com/google/protobuf/util/JsonFormat.java @@ -0,0 +1,1571 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.common.io.BaseEncoding; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonNull; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.google.gson.JsonPrimitive; +import com.google.gson.stream.JsonReader; +import com.google.protobuf.Any; +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.DoubleValue; +import com.google.protobuf.Duration; +import com.google.protobuf.DynamicMessage; +import com.google.protobuf.FieldMask; +import com.google.protobuf.FloatValue; +import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ListValue; +import com.google.protobuf.Message; +import com.google.protobuf.MessageOrBuilder; +import com.google.protobuf.StringValue; +import com.google.protobuf.Struct; +import com.google.protobuf.Timestamp; +import com.google.protobuf.UInt32Value; +import com.google.protobuf.UInt64Value; +import com.google.protobuf.Value; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ParseException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + +/** + * Utility classes to convert protobuf messages to/from JSON format. The JSON + * format follows Proto3 JSON specification and only proto3 features are + * supported. Proto2 only features (e.g., extensions and unknown fields) will + * be discarded in the conversion. That is, when converting proto2 messages + * to JSON format, extensions and unknown fields will be treated as if they + * do not exist. This applies to proto2 messages embedded in proto3 messages + * as well. + */ +public class JsonFormat { + private static final Logger logger = + Logger.getLogger(JsonFormat.class.getName()); + + private JsonFormat() {} + + /** + * Creates a {@link Printer} with default configurations. + */ + public static Printer printer() { + return new Printer(TypeRegistry.getEmptyTypeRegistry()); + } + + /** + * A Printer converts protobuf message to JSON format. + */ + public static class Printer { + private final TypeRegistry registry; + + private Printer(TypeRegistry registry) { + this.registry = registry; + } + + /** + * Creates a new {@link Printer} using the given registry. The new Printer + * clones all other configurations from the current {@link Printer}. + * + * @throws IllegalArgumentException if a registry is already set. + */ + public Printer usingTypeRegistry(TypeRegistry registry) { + if (this.registry != TypeRegistry.getEmptyTypeRegistry()) { + throw new IllegalArgumentException("Only one registry is allowed."); + } + return new Printer(registry); + } + + /** + * Converts a protobuf message to JSON format. + * + * @throws InvalidProtocolBufferException if the message contains Any types + * that can't be resolved. + * @throws IOException if writing to the output fails. + */ + public void appendTo(MessageOrBuilder message, Appendable output) + throws IOException { + // TODO(xiaofeng): Investigate the allocation overhead and optimize for + // mobile. + new PrinterImpl(registry, output).print(message); + } + + /** + * Converts a protobuf message to JSON format. Throws exceptions if there + * are unknown Any types in the message. + */ + public String print(MessageOrBuilder message) + throws InvalidProtocolBufferException { + try { + StringBuilder builder = new StringBuilder(); + appendTo(message, builder); + return builder.toString(); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + // Unexpected IOException. + throw new IllegalStateException(e); + } + } + } + + /** + * Creates a {@link Parser} with default configuration. + */ + public static Parser parser() { + return new Parser(TypeRegistry.getEmptyTypeRegistry()); + } + + /** + * A Parser parses JSON to protobuf message. + */ + public static class Parser { + private final TypeRegistry registry; + + private Parser(TypeRegistry registry) { + this.registry = registry; + } + + /** + * Creates a new {@link Parser} using the given registry. The new Parser + * clones all other configurations from this Parser. + * + * @throws IllegalArgumentException if a registry is already set. + */ + public Parser usingTypeRegistry(TypeRegistry registry) { + if (this.registry != TypeRegistry.getEmptyTypeRegistry()) { + throw new IllegalArgumentException("Only one registry is allowed."); + } + return new Parser(registry); + } + + /** + * Parses from JSON into a protobuf message. + * + * @throws InvalidProtocolBufferException if the input is not valid JSON + * format or there are unknown fields in the input. + */ + public void merge(String json, Message.Builder builder) + throws InvalidProtocolBufferException { + // TODO(xiaofeng): Investigate the allocation overhead and optimize for + // mobile. + new ParserImpl(registry).merge(json, builder); + } + + /** + * Parses from JSON into a protobuf message. + * + * @throws InvalidProtocolBufferException if the input is not valid JSON + * format or there are unknown fields in the input. + * @throws IOException if reading from the input throws. + */ + public void merge(Reader json, Message.Builder builder) + throws IOException { + // TODO(xiaofeng): Investigate the allocation overhead and optimize for + // mobile. + new ParserImpl(registry).merge(json, builder); + } + } + + /** + * A TypeRegistry is used to resolve Any messages in the JSON conversion. + * You must provide a TypeRegistry containing all message types used in + * Any message fields, or the JSON conversion will fail because data + * in Any message fields is unrecognizable. You don't need to supply a + * TypeRegistry if you don't use Any message fields. + */ + public static class TypeRegistry { + private static class EmptyTypeRegistryHolder { + private static final TypeRegistry EMPTY = new TypeRegistry( + Collections.emptyMap()); + } + + public static TypeRegistry getEmptyTypeRegistry() { + return EmptyTypeRegistryHolder.EMPTY; + } + + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Find a type by its full name. Returns null if it cannot be found in + * this {@link TypeRegistry}. + */ + public Descriptor find(String name) { + return types.get(name); + } + + private final Map types; + + private TypeRegistry(Map types) { + this.types = types; + } + + /** + * A Builder is used to build {@link TypeRegistry}. + */ + public static class Builder { + private Builder() {} + + /** + * Adds a message type and all types defined in the same .proto file as + * well as all transitively imported .proto files to this {@link Builder}. + */ + public Builder add(Descriptor messageType) { + if (types == null) { + throw new IllegalStateException( + "A TypeRegistry.Builer can only be used once."); + } + addFile(messageType.getFile()); + return this; + } + + /** + * Adds message types and all types defined in the same .proto file as + * well as all transitively imported .proto files to this {@link Builder}. + */ + public Builder add(Iterable messageTypes) { + if (types == null) { + throw new IllegalStateException( + "A TypeRegistry.Builer can only be used once."); + } + for (Descriptor type : messageTypes) { + addFile(type.getFile()); + } + return this; + } + + /** + * Builds a {@link TypeRegistry}. This method can only be called once for + * one Builder. + */ + public TypeRegistry build() { + TypeRegistry result = new TypeRegistry(types); + // Make sure the built {@link TypeRegistry} is immutable. + types = null; + return result; + } + + private void addFile(FileDescriptor file) { + // Skip the file if it's already added. + if (files.contains(file.getName())) { + return; + } + for (FileDescriptor dependency : file.getDependencies()) { + addFile(dependency); + } + for (Descriptor message : file.getMessageTypes()) { + addMessage(message); + } + } + + private void addMessage(Descriptor message) { + for (Descriptor nestedType : message.getNestedTypes()) { + addMessage(nestedType); + } + + if (types.containsKey(message.getFullName())) { + logger.warning("Type " + message.getFullName() + + " is added multiple times."); + return; + } + + types.put(message.getFullName(), message); + } + + private final Set files = new HashSet(); + private Map types = + new HashMap(); + } + } + + /** + * A TextGenerator adds indentation when writing formatted text. + */ + private static final class TextGenerator { + private final Appendable output; + private final StringBuilder indent = new StringBuilder(); + private boolean atStartOfLine = true; + + private TextGenerator(final Appendable output) { + this.output = output; + } + + /** + * Indent text by two spaces. After calling Indent(), two spaces will be + * inserted at the beginning of each line of text. Indent() may be called + * multiple times to produce deeper indents. + */ + public void indent() { + indent.append(" "); + } + + /** + * Reduces the current indent level by two spaces, or crashes if the indent + * level is zero. + */ + public void outdent() { + final int length = indent.length(); + if (length < 2) { + throw new IllegalArgumentException( + " Outdent() without matching Indent()."); + } + indent.delete(length - 2, length); + } + + /** + * Print text to the output stream. + */ + public void print(final CharSequence text) throws IOException { + final int size = text.length(); + int pos = 0; + + for (int i = 0; i < size; i++) { + if (text.charAt(i) == '\n') { + write(text.subSequence(pos, i + 1)); + pos = i + 1; + atStartOfLine = true; + } + } + write(text.subSequence(pos, size)); + } + + private void write(final CharSequence data) throws IOException { + if (data.length() == 0) { + return; + } + if (atStartOfLine) { + atStartOfLine = false; + output.append(indent); + } + output.append(data); + } + } + + /** + * A Printer converts protobuf messages to JSON format. + */ + private static final class PrinterImpl { + private final TypeRegistry registry; + private final TextGenerator generator; + // We use Gson to help handle string escapes. + private final Gson gson; + + private static class GsonHolder { + private static final Gson DEFAULT_GSON = new Gson(); + } + + PrinterImpl(TypeRegistry registry, Appendable jsonOutput) { + this.registry = registry; + this.generator = new TextGenerator(jsonOutput); + this.gson = GsonHolder.DEFAULT_GSON; + } + + void print(MessageOrBuilder message) throws IOException { + WellKnownTypePrinter specialPrinter = wellKnownTypePrinters.get( + message.getDescriptorForType().getFullName()); + if (specialPrinter != null) { + specialPrinter.print(this, message); + return; + } + print(message, null); + } + + private interface WellKnownTypePrinter { + void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException; + } + + private static final Map + wellKnownTypePrinters = buildWellKnownTypePrinters(); + + private static Map + buildWellKnownTypePrinters() { + Map printers = + new HashMap(); + // Special-case Any. + printers.put(Any.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printAny(message); + } + }); + // Special-case wrapper types. + WellKnownTypePrinter wrappersPrinter = new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printWrapper(message); + + } + }; + printers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter); + printers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter); + printers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter); + printers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter); + printers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter); + printers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter); + printers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter); + printers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter); + printers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter); + // Special-case Timestamp. + printers.put(Timestamp.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printTimestamp(message); + } + }); + // Special-case Duration. + printers.put(Duration.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printDuration(message); + } + }); + // Special-case FieldMask. + printers.put(FieldMask.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printFieldMask(message); + } + }); + // Special-case Struct. + printers.put(Struct.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printStruct(message); + } + }); + // Special-case Value. + printers.put(Value.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printValue(message); + } + }); + // Special-case ListValue. + printers.put(ListValue.getDescriptor().getFullName(), + new WellKnownTypePrinter() { + @Override + public void print(PrinterImpl printer, MessageOrBuilder message) + throws IOException { + printer.printListValue(message); + } + }); + return printers; + } + + /** Prints google.protobuf.Any */ + private void printAny(MessageOrBuilder message) throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url"); + FieldDescriptor valueField = descriptor.findFieldByName("value"); + // Validates type of the message. Note that we can't just cast the message + // to com.google.protobuf.Any because it might be a DynamicMessage. + if (typeUrlField == null || valueField == null + || typeUrlField.getType() != FieldDescriptor.Type.STRING + || valueField.getType() != FieldDescriptor.Type.BYTES) { + throw new InvalidProtocolBufferException("Invalid Any type."); + } + String typeUrl = (String) message.getField(typeUrlField); + String typeName = getTypeName(typeUrl); + Descriptor type = registry.find(typeName); + if (type == null) { + throw new InvalidProtocolBufferException( + "Cannot find type for url: " + typeUrl); + } + ByteString content = (ByteString) message.getField(valueField); + Message contentMessage = DynamicMessage.getDefaultInstance(type) + .getParserForType().parseFrom(content); + WellKnownTypePrinter printer = wellKnownTypePrinters.get(typeName); + if (printer != null) { + // If the type is one of the well-known types, we use a special + // formatting. + generator.print("{\n"); + generator.indent(); + generator.print("\"@type\": " + gson.toJson(typeUrl) + ",\n"); + generator.print("\"value\": "); + printer.print(this, contentMessage); + generator.print("\n"); + generator.outdent(); + generator.print("}"); + } else { + // Print the content message instead (with a "@type" field added). + print(contentMessage, typeUrl); + } + } + + /** Prints wrapper types (e.g., google.protobuf.Int32Value) */ + private void printWrapper(MessageOrBuilder message) throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + FieldDescriptor valueField = descriptor.findFieldByName("value"); + if (valueField == null) { + throw new InvalidProtocolBufferException("Invalid Wrapper type."); + } + // When formatting wrapper types, we just print its value field instead of + // the whole message. + printSingleFieldValue(valueField, message.getField(valueField)); + } + + private ByteString toByteString(MessageOrBuilder message) { + if (message instanceof Message) { + return ((Message) message).toByteString(); + } else { + return ((Message.Builder) message).build().toByteString(); + } + } + + /** Prints google.protobuf.Timestamp */ + private void printTimestamp(MessageOrBuilder message) throws IOException { + Timestamp value = Timestamp.parseFrom(toByteString(message)); + generator.print("\"" + TimeUtil.toString(value) + "\""); + } + + /** Prints google.protobuf.Duration */ + private void printDuration(MessageOrBuilder message) throws IOException { + Duration value = Duration.parseFrom(toByteString(message)); + generator.print("\"" + TimeUtil.toString(value) + "\""); + + } + + /** Prints google.protobuf.FieldMask */ + private void printFieldMask(MessageOrBuilder message) throws IOException { + FieldMask value = FieldMask.parseFrom(toByteString(message)); + generator.print("\"" + FieldMaskUtil.toString(value) + "\""); + } + + /** Prints google.protobuf.Struct */ + private void printStruct(MessageOrBuilder message) throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + FieldDescriptor field = descriptor.findFieldByName("fields"); + if (field == null) { + throw new InvalidProtocolBufferException("Invalid Struct type."); + } + // Struct is formatted as a map object. + printMapFieldValue(field, message.getField(field)); + } + + /** Prints google.protobuf.Value */ + private void printValue(MessageOrBuilder message) throws IOException { + // For a Value message, only the value of the field is formatted. + Map fields = message.getAllFields(); + if (fields.isEmpty()) { + // No value set. + generator.print("null"); + return; + } + // A Value message can only have at most one field set (it only contains + // an oneof). + if (fields.size() != 1) { + throw new InvalidProtocolBufferException("Invalid Value type."); + } + for (Map.Entry entry : fields.entrySet()) { + printSingleFieldValue(entry.getKey(), entry.getValue()); + } + } + + /** Prints google.protobuf.ListValue */ + private void printListValue(MessageOrBuilder message) throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + FieldDescriptor field = descriptor.findFieldByName("values"); + if (field == null) { + throw new InvalidProtocolBufferException("Invalid ListValue type."); + } + printRepeatedFieldValue(field, message.getField(field)); + } + + /** Prints a regular message with an optional type URL. */ + private void print(MessageOrBuilder message, String typeUrl) + throws IOException { + generator.print("{\n"); + generator.indent(); + + boolean printedField = false; + if (typeUrl != null) { + generator.print("\"@type\": " + gson.toJson(typeUrl)); + printedField = true; + } + for (Map.Entry field + : message.getAllFields().entrySet()) { + // Skip unknown enum fields. + if (field.getValue() instanceof EnumValueDescriptor + && ((EnumValueDescriptor) field.getValue()).getIndex() == -1) { + continue; + } + if (printedField) { + // Add line-endings for the previous field. + generator.print(",\n"); + } else { + printedField = true; + } + printField(field.getKey(), field.getValue()); + } + + // Add line-endings for the last field. + if (printedField) { + generator.print("\n"); + } + generator.outdent(); + generator.print("}"); + } + + private void printField(FieldDescriptor field, Object value) + throws IOException { + generator.print("\"" + fieldNameToCamelName(field.getName()) + "\": "); + if (field.isMapField()) { + printMapFieldValue(field, value); + } else if (field.isRepeated()) { + printRepeatedFieldValue(field, value); + } else { + printSingleFieldValue(field, value); + } + } + + @SuppressWarnings("rawtypes") + private void printRepeatedFieldValue(FieldDescriptor field, Object value) + throws IOException { + generator.print("["); + boolean printedElement = false; + for (Object element : (List) value) { + // Skip unknown enum entries. + if (element instanceof EnumValueDescriptor + && ((EnumValueDescriptor) element).getIndex() == -1) { + continue; + } + if (printedElement) { + generator.print(", "); + } else { + printedElement = true; + } + printSingleFieldValue(field, element); + } + generator.print("]"); + } + + @SuppressWarnings("rawtypes") + private void printMapFieldValue(FieldDescriptor field, Object value) + throws IOException { + Descriptor type = field.getMessageType(); + FieldDescriptor keyField = type.findFieldByName("key"); + FieldDescriptor valueField = type.findFieldByName("value"); + if (keyField == null || valueField == null) { + throw new InvalidProtocolBufferException("Invalid map field."); + } + generator.print("{\n"); + generator.indent(); + boolean printedElement = false; + for (Object element : (List) value) { + Message entry = (Message) element; + Object entryKey = entry.getField(keyField); + Object entryValue = entry.getField(valueField); + // Skip unknown enum entries. + if (entryValue instanceof EnumValueDescriptor + && ((EnumValueDescriptor) entryValue).getIndex() == -1) { + continue; + } + if (printedElement) { + generator.print(",\n"); + } else { + printedElement = true; + } + // Key fields are always double-quoted. + printSingleFieldValue(keyField, entryKey, true); + generator.print(": "); + printSingleFieldValue(valueField, entryValue); + } + if (printedElement) { + generator.print("\n"); + } + generator.outdent(); + generator.print("}"); + } + + private void printSingleFieldValue(FieldDescriptor field, Object value) + throws IOException { + printSingleFieldValue(field, value, false); + } + + /** + * Prints a field's value in JSON format. + * + * @param alwaysWithQuotes whether to always add double-quotes to primitive + * types. + */ + private void printSingleFieldValue( + final FieldDescriptor field, final Object value, + boolean alwaysWithQuotes) throws IOException { + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + if (alwaysWithQuotes) { + generator.print("\""); + } + generator.print(((Integer) value).toString()); + if (alwaysWithQuotes) { + generator.print("\""); + } + break; + + case INT64: + case SINT64: + case SFIXED64: + generator.print("\"" + ((Long) value).toString() + "\""); + break; + + case BOOL: + if (alwaysWithQuotes) { + generator.print("\""); + } + if (((Boolean) value).booleanValue()) { + generator.print("true"); + } else { + generator.print("false"); + } + if (alwaysWithQuotes) { + generator.print("\""); + } + break; + + case FLOAT: + Float floatValue = (Float) value; + if (floatValue.isNaN()) { + generator.print("\"NaN\""); + } else if (floatValue.isInfinite()) { + if (floatValue < 0) { + generator.print("\"-Infinity\""); + } else { + generator.print("\"Infinity\""); + } + } else { + if (alwaysWithQuotes) { + generator.print("\""); + } + generator.print(floatValue.toString()); + if (alwaysWithQuotes) { + generator.print("\""); + } + } + break; + + case DOUBLE: + Double doubleValue = (Double) value; + if (doubleValue.isNaN()) { + generator.print("\"NaN\""); + } else if (doubleValue.isInfinite()) { + if (doubleValue < 0) { + generator.print("\"-Infinity\""); + } else { + generator.print("\"Infinity\""); + } + } else { + if (alwaysWithQuotes) { + generator.print("\""); + } + generator.print(doubleValue.toString()); + if (alwaysWithQuotes) { + generator.print("\""); + } + } + break; + + case UINT32: + case FIXED32: + if (alwaysWithQuotes) { + generator.print("\""); + } + generator.print(unsignedToString((Integer) value)); + if (alwaysWithQuotes) { + generator.print("\""); + } + break; + + case UINT64: + case FIXED64: + generator.print("\"" + unsignedToString((Long) value) + "\""); + break; + + case STRING: + generator.print(gson.toJson(value)); + break; + + case BYTES: + generator.print("\""); + generator.print( + BaseEncoding.base64().encode(((ByteString) value).toByteArray())); + generator.print("\""); + break; + + case ENUM: + // Special-case google.protobuf.NullValue (it's an Enum). + if (field.getEnumType().getFullName().equals( + "google.protobuf.NullValue")) { + // No matter what value it contains, we always print it as "null". + if (alwaysWithQuotes) { + generator.print("\""); + } + generator.print("null"); + if (alwaysWithQuotes) { + generator.print("\""); + } + } else { + generator.print( + "\"" + ((EnumValueDescriptor) value).getName() + "\""); + } + break; + + case MESSAGE: + case GROUP: + print((Message) value); + break; + } + } + } + + /** Convert an unsigned 32-bit integer to a string. */ + private static String unsignedToString(final int value) { + if (value >= 0) { + return Integer.toString(value); + } else { + return Long.toString(value & 0x00000000FFFFFFFFL); + } + } + + /** Convert an unsigned 64-bit integer to a string. */ + private static String unsignedToString(final long value) { + if (value >= 0) { + return Long.toString(value); + } else { + // Pull off the most-significant bit so that BigInteger doesn't think + // the number is negative, then set it again using setBit(). + return BigInteger.valueOf(value & Long.MAX_VALUE) + .setBit(Long.SIZE - 1).toString(); + } + } + + private static final String TYPE_URL_PREFIX = "type.googleapis.com"; + + private static String getTypeName(String typeUrl) + throws InvalidProtocolBufferException { + String[] parts = typeUrl.split("/"); + if (parts.length != 2 || !parts[0].equals(TYPE_URL_PREFIX)) { + throw new InvalidProtocolBufferException( + "Invalid type url found: " + typeUrl); + } + return parts[1]; + } + + private static String fieldNameToCamelName(String name) { + StringBuilder result = new StringBuilder(name.length()); + boolean isNextUpperCase = false; + for (int i = 0; i < name.length(); i++) { + Character ch = name.charAt(i); + if (Character.isLowerCase(ch)) { + if (isNextUpperCase) { + result.append(Character.toUpperCase(ch)); + } else { + result.append(ch); + } + isNextUpperCase = false; + } else if (Character.isUpperCase(ch)) { + if (i == 0 && !isNextUpperCase) { + // Force first letter to lower-case unless explicitly told to + // capitalize it. + result.append(Character.toLowerCase(ch)); + } else { + // Capital letters after the first are left as-is. + result.append(ch); + } + isNextUpperCase = false; + } else if (Character.isDigit(ch)) { + result.append(ch); + isNextUpperCase = true; + } else { + isNextUpperCase = true; + } + } + return result.toString(); + } + + private static class ParserImpl { + private final TypeRegistry registry; + private final JsonParser jsonParser; + + ParserImpl(TypeRegistry registry) { + this.registry = registry; + this.jsonParser = new JsonParser(); + } + + void merge(Reader json, Message.Builder builder) + throws IOException { + JsonReader reader = new JsonReader(json); + reader.setLenient(false); + merge(jsonParser.parse(reader), builder); + } + + void merge(String json, Message.Builder builder) + throws InvalidProtocolBufferException { + try { + JsonReader reader = new JsonReader(new StringReader(json)); + reader.setLenient(false); + merge(jsonParser.parse(reader), builder); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (Exception e) { + // We convert all exceptions from JSON parsing to our own exceptions. + throw new InvalidProtocolBufferException(e.getMessage()); + } + } + + private interface WellKnownTypeParser { + void merge(ParserImpl parser, JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException; + } + + private static final Map wellKnownTypeParsers = + buildWellKnownTypeParsers(); + + private static Map + buildWellKnownTypeParsers() { + Map parsers = + new HashMap(); + // Special-case Any. + parsers.put(Any.getDescriptor().getFullName(), new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeAny(json, builder); + } + }); + // Special-case wrapper types. + WellKnownTypeParser wrappersPrinter = new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeWrapper(json, builder); + } + }; + parsers.put(BoolValue.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(Int32Value.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(UInt32Value.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(Int64Value.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(UInt64Value.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(StringValue.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(BytesValue.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(FloatValue.getDescriptor().getFullName(), wrappersPrinter); + parsers.put(DoubleValue.getDescriptor().getFullName(), wrappersPrinter); + // Special-case Timestamp. + parsers.put(Timestamp.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeTimestamp(json, builder); + } + }); + // Special-case Duration. + parsers.put(Duration.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeDuration(json, builder); + } + }); + // Special-case FieldMask. + parsers.put(FieldMask.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeFieldMask(json, builder); + } + }); + // Special-case Struct. + parsers.put(Struct.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeStruct(json, builder); + } + }); + // Special-case Value. + parsers.put(Value.getDescriptor().getFullName(), + new WellKnownTypeParser() { + @Override + public void merge(ParserImpl parser, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + parser.mergeValue(json, builder); + } + }); + return parsers; + } + + private void merge(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + WellKnownTypeParser specialParser = wellKnownTypeParsers.get( + builder.getDescriptorForType().getFullName()); + if (specialParser != null) { + specialParser.merge(this, json, builder); + return; + } + mergeMessage(json, builder, false); + } + + // Maps from camel-case field names to FieldDescriptor. + private final Map> fieldNameMaps = + new HashMap>(); + + private Map getFieldNameMap( + Descriptor descriptor) { + if (!fieldNameMaps.containsKey(descriptor)) { + Map fieldNameMap = + new HashMap(); + for (FieldDescriptor field : descriptor.getFields()) { + fieldNameMap.put(fieldNameToCamelName(field.getName()), field); + } + fieldNameMaps.put(descriptor, fieldNameMap); + return fieldNameMap; + } + return fieldNameMaps.get(descriptor); + } + + private void mergeMessage(JsonElement json, Message.Builder builder, + boolean skipTypeUrl) throws InvalidProtocolBufferException { + if (!(json instanceof JsonObject)) { + throw new InvalidProtocolBufferException( + "Expect message object but got: " + json); + } + JsonObject object = (JsonObject) json; + Map fieldNameMap = + getFieldNameMap(builder.getDescriptorForType()); + for (Map.Entry entry : object.entrySet()) { + if (skipTypeUrl && entry.getKey().equals("@type")) { + continue; + } + FieldDescriptor field = fieldNameMap.get(entry.getKey()); + if (field == null) { + throw new InvalidProtocolBufferException( + "Cannot find field: " + entry.getKey() + " in message " + + builder.getDescriptorForType().getFullName()); + } + mergeField(field, entry.getValue(), builder); + } + } + + private void mergeAny(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + Descriptor descriptor = builder.getDescriptorForType(); + FieldDescriptor typeUrlField = descriptor.findFieldByName("type_url"); + FieldDescriptor valueField = descriptor.findFieldByName("value"); + // Validates type of the message. Note that we can't just cast the message + // to com.google.protobuf.Any because it might be a DynamicMessage. + if (typeUrlField == null || valueField == null + || typeUrlField.getType() != FieldDescriptor.Type.STRING + || valueField.getType() != FieldDescriptor.Type.BYTES) { + throw new InvalidProtocolBufferException("Invalid Any type."); + } + + if (!(json instanceof JsonObject)) { + throw new InvalidProtocolBufferException( + "Expect message object but got: " + json); + } + JsonObject object = (JsonObject) json; + JsonElement typeUrlElement = object.get("@type"); + if (typeUrlElement == null) { + throw new InvalidProtocolBufferException( + "Missing type url when parsing: " + json); + } + String typeUrl = typeUrlElement.getAsString(); + Descriptor contentType = registry.find(getTypeName(typeUrl)); + if (contentType == null) { + throw new InvalidProtocolBufferException( + "Cannot resolve type: " + typeUrl); + } + builder.setField(typeUrlField, typeUrl); + Message.Builder contentBuilder = + DynamicMessage.getDefaultInstance(contentType).newBuilderForType(); + WellKnownTypeParser specialParser = + wellKnownTypeParsers.get(contentType.getFullName()); + if (specialParser != null) { + JsonElement value = object.get("value"); + if (value != null) { + specialParser.merge(this, value, contentBuilder); + } + } else { + mergeMessage(json, contentBuilder, true); + } + builder.setField(valueField, contentBuilder.build().toByteString()); + } + + private void mergeFieldMask(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + FieldMask value = FieldMaskUtil.fromString(json.getAsString()); + builder.mergeFrom(value.toByteString()); + } + + private void mergeTimestamp(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + try { + Timestamp value = TimeUtil.parseTimestamp(json.getAsString()); + builder.mergeFrom(value.toByteString()); + } catch (ParseException e) { + throw new InvalidProtocolBufferException( + "Failed to parse timestamp: " + json); + } + } + + private void mergeDuration(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + try { + Duration value = TimeUtil.parseDuration(json.getAsString()); + builder.mergeFrom(value.toByteString()); + } catch (ParseException e) { + throw new InvalidProtocolBufferException( + "Failed to parse duration: " + json); + } + } + + private void mergeStruct(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + Descriptor descriptor = builder.getDescriptorForType(); + FieldDescriptor field = descriptor.findFieldByName("fields"); + if (field == null) { + throw new InvalidProtocolBufferException("Invalid Struct type."); + } + mergeMapField(field, json, builder); + } + + private void mergeValue(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + Descriptor type = builder.getDescriptorForType(); + if (json instanceof JsonPrimitive) { + JsonPrimitive primitive = (JsonPrimitive) json; + if (primitive.isBoolean()) { + builder.setField(type.findFieldByName("bool_value"), + primitive.getAsBoolean()); + } else if (primitive.isNumber()) { + builder.setField(type.findFieldByName("number_value"), + primitive.getAsDouble()); + } else { + builder.setField(type.findFieldByName("string_value"), + primitive.getAsString()); + } + } else if (json instanceof JsonObject) { + FieldDescriptor field = type.findFieldByName("struct_value"); + Message.Builder structBuilder = builder.newBuilderForField(field); + merge(json, structBuilder); + builder.setField(field, structBuilder.build()); + } else if (json instanceof JsonArray) { + FieldDescriptor field = type.findFieldByName("list_value"); + Message.Builder listBuilder = builder.newBuilderForField(field); + FieldDescriptor listField = + listBuilder.getDescriptorForType().findFieldByName("values"); + mergeRepeatedField(listField, json, listBuilder); + builder.setField(field, listBuilder.build()); + } else { + throw new IllegalStateException("Unexpected json data: " + json); + } + } + + private void mergeWrapper(JsonElement json, Message.Builder builder) + throws InvalidProtocolBufferException { + Descriptor type = builder.getDescriptorForType(); + FieldDescriptor field = type.findFieldByName("value"); + if (field == null) { + throw new InvalidProtocolBufferException( + "Invalid wrapper type: " + type.getFullName()); + } + builder.setField(field, parseFieldValue(field, json, builder)); + } + + private void mergeField(FieldDescriptor field, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + if (json instanceof JsonNull) { + // We allow "null" as value for all field types and treat it as if the + // field is not present. + return; + } + if (field.isMapField()) { + mergeMapField(field, json, builder); + } else if (field.isRepeated()) { + mergeRepeatedField(field, json, builder); + } else { + Object value = parseFieldValue(field, json, builder); + if (value != null) { + builder.setField(field, value); + } + } + } + + private void mergeMapField(FieldDescriptor field, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + if (!(json instanceof JsonObject)) { + throw new InvalidProtocolBufferException( + "Expect a map object but found: " + json); + } + Descriptor type = field.getMessageType(); + FieldDescriptor keyField = type.findFieldByName("key"); + FieldDescriptor valueField = type.findFieldByName("value"); + if (keyField == null || valueField == null) { + throw new InvalidProtocolBufferException( + "Invalid map field: " + field.getFullName()); + } + JsonObject object = (JsonObject) json; + for (Map.Entry entry : object.entrySet()) { + Message.Builder entryBuilder = builder.newBuilderForField(field); + Object key = parseFieldValue( + keyField, new JsonPrimitive(entry.getKey()), entryBuilder); + Object value = parseFieldValue( + valueField, entry.getValue(), entryBuilder); + if (value == null) { + value = getDefaultValue(valueField, entryBuilder); + } + entryBuilder.setField(keyField, key); + entryBuilder.setField(valueField, value); + builder.addRepeatedField(field, entryBuilder.build()); + } + } + + /** + * Gets the default value for a field type. Note that we use proto3 + * language defaults and ignore any default values set through the + * proto "default" option. + */ + private Object getDefaultValue(FieldDescriptor field, + Message.Builder builder) { + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + case UINT32: + case FIXED32: + return 0; + case INT64: + case SINT64: + case SFIXED64: + case UINT64: + case FIXED64: + return 0L; + case FLOAT: + return 0.0f; + case DOUBLE: + return 0.0; + case BOOL: + return false; + case STRING: + return ""; + case BYTES: + return ByteString.EMPTY; + case ENUM: + return field.getEnumType().getValues().get(0); + case MESSAGE: + case GROUP: + return builder.newBuilderForField(field).getDefaultInstanceForType(); + default: + throw new IllegalStateException( + "Invalid field type: " + field.getType()); + } + } + + private void mergeRepeatedField(FieldDescriptor field, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + if (!(json instanceof JsonArray)) { + throw new InvalidProtocolBufferException( + "Expect an array but found: " + json); + } + JsonArray array = (JsonArray) json; + for (int i = 0; i < array.size(); ++i) { + Object value = parseFieldValue(field, array.get(i), builder); + if (value == null) { + value = getDefaultValue(field, builder); + } + builder.addRepeatedField(field, value); + } + } + + private int parseInt32(JsonElement json) + throws InvalidProtocolBufferException { + try { + return Integer.parseInt(json.getAsString()); + } catch (Exception e) { + throw new InvalidProtocolBufferException("Not an int32 value: " + json); + } + } + + private long parseInt64(JsonElement json) + throws InvalidProtocolBufferException { + try { + return Long.parseLong(json.getAsString()); + } catch (Exception e) { + throw new InvalidProtocolBufferException("Not an int64 value: " + json); + } + } + + private int parseUint32(JsonElement json) + throws InvalidProtocolBufferException { + try { + long result = Long.parseLong(json.getAsString()); + if (result < 0 || result > 0xFFFFFFFFL) { + throw new InvalidProtocolBufferException( + "Out of range uint32 value: " + json); + } + return (int) result; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (Exception e) { + throw new InvalidProtocolBufferException( + "Not an uint32 value: " + json); + } + } + + private static final BigInteger MAX_UINT64 = + new BigInteger("FFFFFFFFFFFFFFFF", 16); + + private long parseUint64(JsonElement json) + throws InvalidProtocolBufferException { + try { + BigInteger value = new BigInteger(json.getAsString()); + if (value.compareTo(BigInteger.ZERO) < 0 + || value.compareTo(MAX_UINT64) > 0) { + throw new InvalidProtocolBufferException( + "Out of range uint64 value: " + json); + } + return value.longValue(); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (Exception e) { + throw new InvalidProtocolBufferException( + "Not an uint64 value: " + json); + } + } + + private boolean parseBool(JsonElement json) + throws InvalidProtocolBufferException { + if (json.getAsString().equals("true")) { + return true; + } + if (json.getAsString().equals("false")) { + return false; + } + throw new InvalidProtocolBufferException("Invalid bool value: " + json); + } + + private static final double EPSILON = 1e-6; + + private float parseFloat(JsonElement json) + throws InvalidProtocolBufferException { + if (json.getAsString().equals("NaN")) { + return Float.NaN; + } else if (json.getAsString().equals("Infinity")) { + return Float.POSITIVE_INFINITY; + } else if (json.getAsString().equals("-Infinity")) { + return Float.NEGATIVE_INFINITY; + } + try { + // We don't use Float.parseFloat() here because that function simply + // accepts all double values. Here we parse the value into a Double + // and do explicit range check on it. + double value = Double.parseDouble(json.getAsString()); + // When a float value is printed, the printed value might be a little + // larger or smaller due to precision loss. Here we need to add a bit + // of tolerance when checking whether the float value is in range. + if (value > Float.MAX_VALUE * (1.0 + EPSILON) + || value < -Float.MAX_VALUE * (1.0 + EPSILON)) { + throw new InvalidProtocolBufferException( + "Out of range float value: " + json); + } + return (float) value; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (Exception e) { + throw new InvalidProtocolBufferException("Not a float value: " + json); + } + } + + private static final BigDecimal MORE_THAN_ONE = new BigDecimal( + String.valueOf(1.0 + EPSILON)); + // When a float value is printed, the printed value might be a little + // larger or smaller due to precision loss. Here we need to add a bit + // of tolerance when checking whether the float value is in range. + private static final BigDecimal MAX_DOUBLE = new BigDecimal( + String.valueOf(Double.MAX_VALUE)).multiply(MORE_THAN_ONE); + private static final BigDecimal MIN_DOUBLE = new BigDecimal( + String.valueOf(-Double.MAX_VALUE)).multiply(MORE_THAN_ONE); + + private double parseDouble(JsonElement json) + throws InvalidProtocolBufferException { + if (json.getAsString().equals("NaN")) { + return Double.NaN; + } else if (json.getAsString().equals("Infinity")) { + return Double.POSITIVE_INFINITY; + } else if (json.getAsString().equals("-Infinity")) { + return Double.NEGATIVE_INFINITY; + } + try { + // We don't use Double.parseDouble() here because that function simply + // accepts all values. Here we parse the value into a BigDecimal and do + // explicit range check on it. + BigDecimal value = new BigDecimal(json.getAsString()); + if (value.compareTo(MAX_DOUBLE) > 0 + || value.compareTo(MIN_DOUBLE) < 0) { + throw new InvalidProtocolBufferException( + "Out of range double value: " + json); + } + return value.doubleValue(); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (Exception e) { + throw new InvalidProtocolBufferException( + "Not an double value: " + json); + } + } + + private String parseString(JsonElement json) { + return json.getAsString(); + } + + private ByteString parseBytes(JsonElement json) { + return ByteString.copyFrom( + BaseEncoding.base64().decode(json.getAsString())); + } + + private EnumValueDescriptor parseEnum(EnumDescriptor enumDescriptor, + JsonElement json) throws InvalidProtocolBufferException { + String value = json.getAsString(); + EnumValueDescriptor result = enumDescriptor.findValueByName(value); + if (result == null) { + throw new InvalidProtocolBufferException( + "Invalid enum value: " + value + " for enum type: " + + enumDescriptor.getFullName()); + } + return result; + } + + private Object parseFieldValue(FieldDescriptor field, JsonElement json, + Message.Builder builder) throws InvalidProtocolBufferException { + if (json instanceof JsonNull) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE + && field.getMessageType().getFullName().equals( + Value.getDescriptor().getFullName())) { + // For every other type, "null" means absence, but for the special + // Value message, it means the "null_value" field has been set. + Value value = Value.newBuilder().setNullValueValue(0).build(); + return builder.newBuilderForField(field).mergeFrom( + value.toByteString()).build(); + } + return null; + } + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + return parseInt32(json); + + case INT64: + case SINT64: + case SFIXED64: + return parseInt64(json); + + case BOOL: + return parseBool(json); + + case FLOAT: + return parseFloat(json); + + case DOUBLE: + return parseDouble(json); + + case UINT32: + case FIXED32: + return parseUint32(json); + + case UINT64: + case FIXED64: + return parseUint64(json); + + case STRING: + return parseString(json); + + case BYTES: + return parseBytes(json); + + case ENUM: + return parseEnum(field.getEnumType(), json); + + case MESSAGE: + case GROUP: + Message.Builder subBuilder = builder.newBuilderForField(field); + merge(json, subBuilder); + return subBuilder.build(); + + default: + throw new InvalidProtocolBufferException( + "Invalid field type: " + field.getType()); + } + } + } +} diff --git a/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java new file mode 100644 index 0000000000..6e4b7c03aa --- /dev/null +++ b/java/util/src/main/java/com/google/protobuf/util/TimeUtil.java @@ -0,0 +1,545 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.Duration; +import com.google.protobuf.Timestamp; + +import java.math.BigInteger; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.TimeZone; + +/** + * Utilities to help create/manipulate Timestamp/Duration + */ +public class TimeUtil { + // Timestamp for "0001-01-01T00:00:00Z" + public static final long TIMESTAMP_SECONDS_MIN = -62135596800L; + + // Timestamp for "9999-12-31T23:59:59Z" + public static final long TIMESTAMP_SECONDS_MAX = 253402300799L; + public static final long DURATION_SECONDS_MIN = -315576000000L; + public static final long DURATION_SECONDS_MAX = 315576000000L; + + private static final long NANOS_PER_SECOND = 1000000000; + private static final long NANOS_PER_MILLISECOND = 1000000; + private static final long NANOS_PER_MICROSECOND = 1000; + private static final long MILLIS_PER_SECOND = 1000; + private static final long MICROS_PER_SECOND = 1000000; + + private static final SimpleDateFormat timestampFormat = + createTimestampFormat(); + + private static SimpleDateFormat createTimestampFormat() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + GregorianCalendar calendar = + new GregorianCalendar(TimeZone.getTimeZone("UTC")); + // We use Proleptic Gregorian Calendar (i.e., Gregorian calendar extends + // backwards to year one) for timestamp formating. + calendar.setGregorianChange(new Date(Long.MIN_VALUE)); + sdf.setCalendar(calendar); + return sdf; + } + + private TimeUtil() {} + + /** + * Convert Timestamp to RFC 3339 date string format. The output will always + * be Z-normalized and uses 3, 6 or 9 fractional digits as required to + * represent the exact value. Note that Timestamp can only represent time + * from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. See + * https://www.ietf.org/rfc/rfc3339.txt + * + *

Example of generated format: "1972-01-01T10:00:20.021Z" + * + * @return The string representation of the given timestamp. + * @throws IllegalArgumentException if the given timestamp is not in the + * valid range. + */ + public static String toString(Timestamp timestamp) + throws IllegalArgumentException { + StringBuilder result = new StringBuilder(); + // Format the seconds part. + if (timestamp.getSeconds() < TIMESTAMP_SECONDS_MIN + || timestamp.getSeconds() > TIMESTAMP_SECONDS_MAX) { + throw new IllegalArgumentException("Timestamp is out of range."); + } + Date date = new Date(timestamp.getSeconds() * MILLIS_PER_SECOND); + result.append(timestampFormat.format(date)); + // Format the nanos part. + if (timestamp.getNanos() < 0 || timestamp.getNanos() >= NANOS_PER_SECOND) { + throw new IllegalArgumentException("Timestamp has invalid nanos value."); + } + if (timestamp.getNanos() != 0) { + result.append("."); + result.append(formatNanos(timestamp.getNanos())); + } + result.append("Z"); + return result.toString(); + } + + /** + * Parse from RFC 3339 date string to Timestamp. This method accepts all + * outputs of {@link #toString(Timestamp)} and it also accepts any fractional + * digits (or none) and any offset as long as they fit into nano-seconds + * precision. + * + *

Example of accepted format: "1972-01-01T10:00:20.021-05:00" + * + * @return A Timestamp parsed from the string. + * @throws ParseException if parsing fails. + */ + + public static Timestamp parseTimestamp(String value) throws ParseException { + int dayOffset = value.indexOf('T'); + if (dayOffset == -1) { + throw new ParseException( + "Failed to parse timestamp: invalid timestamp \"" + value + "\"", 0); + } + int timezoneOffsetPosition = value.indexOf('Z', dayOffset); + if (timezoneOffsetPosition == -1) { + timezoneOffsetPosition = value.indexOf('+', dayOffset); + } + if (timezoneOffsetPosition == -1) { + timezoneOffsetPosition = value.indexOf('-', dayOffset); + } + if (timezoneOffsetPosition == -1) { + throw new ParseException( + "Failed to parse timestamp: missing valid timezone offset.", 0); + } + // Parse seconds and nanos. + String timeValue = value.substring(0, timezoneOffsetPosition); + String secondValue = timeValue; + String nanoValue = ""; + int pointPosition = timeValue.indexOf('.'); + if (pointPosition != -1) { + secondValue = timeValue.substring(0, pointPosition); + nanoValue = timeValue.substring(pointPosition + 1); + } + Date date = timestampFormat.parse(secondValue); + long seconds = date.getTime() / MILLIS_PER_SECOND; + int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue); + // Parse timezone offsets. + if (value.charAt(timezoneOffsetPosition) == 'Z') { + if (value.length() != timezoneOffsetPosition + 1) { + throw new ParseException( + "Failed to parse timestamp: invalid trailing data \"" + + value.substring(timezoneOffsetPosition) + "\"", 0); + } + } else { + String offsetValue = value.substring(timezoneOffsetPosition + 1); + long offset = parseTimezoneOffset(offsetValue); + if (value.charAt(timezoneOffsetPosition) == '+') { + seconds -= offset; + } else { + seconds += offset; + } + } + try { + return normalizedTimestamp(seconds, nanos); + } catch (IllegalArgumentException e) { + throw new ParseException( + "Failed to parse timestmap: timestamp is out of range.", 0); + } + } + + /** + * Convert Duration to string format. The string format will contains 3, 6, + * or 9 fractional digits depending on the precision required to represent + * the exact Duration value. For example: "1s", "1.010s", "1.000000100s", + * "-3.100s" The range that can be represented by Duration is from + * -315,576,000,000 to +315,576,000,000 inclusive (in seconds). + * + * @return The string representation of the given duration. + * @throws IllegalArgumentException if the given duration is not in the valid + * range. + */ + public static String toString(Duration duration) + throws IllegalArgumentException { + if (duration.getSeconds() < DURATION_SECONDS_MIN + || duration.getSeconds() > DURATION_SECONDS_MAX) { + throw new IllegalArgumentException("Duration is out of valid range."); + } + StringBuilder result = new StringBuilder(); + long seconds = duration.getSeconds(); + int nanos = duration.getNanos(); + if (seconds < 0 || nanos < 0) { + if (seconds > 0 || nanos > 0) { + throw new IllegalArgumentException( + "Invalid duration: seconds value and nanos value must have the same" + + "sign."); + } + result.append("-"); + seconds = -seconds; + nanos = -nanos; + } + result.append(seconds); + if (nanos != 0) { + result.append("."); + result.append(formatNanos(nanos)); + } + result.append("s"); + return result.toString(); + } + + /** + * Parse from a string to produce a duration. + * + * @return A Duration parsed from the string. + * @throws ParseException if parsing fails. + */ + public static Duration parseDuration(String value) throws ParseException { + // Must ended with "s". + if (value.isEmpty() || value.charAt(value.length() - 1) != 's') { + throw new ParseException("Invalid duration string: " + value, 0); + } + boolean negative = false; + if (value.charAt(0) == '-') { + negative = true; + value = value.substring(1); + } + String secondValue = value.substring(0, value.length() - 1); + String nanoValue = ""; + int pointPosition = secondValue.indexOf('.'); + if (pointPosition != -1) { + nanoValue = secondValue.substring(pointPosition + 1); + secondValue = secondValue.substring(0, pointPosition); + } + long seconds = Long.parseLong(secondValue); + int nanos = nanoValue.isEmpty() ? 0 : parseNanos(nanoValue); + if (seconds < 0) { + throw new ParseException("Invalid duration string: " + value, 0); + } + if (negative) { + seconds = -seconds; + nanos = -nanos; + } + try { + return normalizedDuration(seconds, nanos); + } catch (IllegalArgumentException e) { + throw new ParseException("Duration value is out of range.", 0); + } + } + + /** + * Create a Timestamp from the number of milliseconds elapsed from the epoch. + */ + public static Timestamp createTimestampFromMillis(long milliseconds) { + return normalizedTimestamp(milliseconds / MILLIS_PER_SECOND, + (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + } + + /** + * Create a Duration from the number of milliseconds. + */ + public static Duration createDurationFromMillis(long milliseconds) { + return normalizedDuration(milliseconds / MILLIS_PER_SECOND, + (int) (milliseconds % MILLIS_PER_SECOND * NANOS_PER_MILLISECOND)); + } + + /** + * Convert a Timestamp to the number of milliseconds elapsed from the epoch. + * + *

The result will be rounded down to the nearest millisecond. E.g., if the + * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded + * to -1 millisecond. + */ + public static long toMillis(Timestamp timestamp) { + return timestamp.getSeconds() * MILLIS_PER_SECOND + timestamp.getNanos() + / NANOS_PER_MILLISECOND; + } + + /** + * Convert a Duration to the number of milliseconds.The result will be + * rounded towards 0 to the nearest millisecond. E.g., if the duration + * represents -1 nanosecond, it will be rounded to 0. + */ + public static long toMillis(Duration duration) { + return duration.getSeconds() * MILLIS_PER_SECOND + duration.getNanos() + / NANOS_PER_MILLISECOND; + } + + /** + * Create a Timestamp from the number of microseconds elapsed from the epoch. + */ + public static Timestamp createTimestampFromMicros(long microseconds) { + return normalizedTimestamp(microseconds / MICROS_PER_SECOND, + (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND)); + } + + /** + * Create a Duration from the number of microseconds. + */ + public static Duration createDurationFromMicros(long microseconds) { + return normalizedDuration(microseconds / MICROS_PER_SECOND, + (int) (microseconds % MICROS_PER_SECOND * NANOS_PER_MICROSECOND)); + } + + /** + * Convert a Timestamp to the number of microseconds elapsed from the epoch. + * + *

The result will be rounded down to the nearest microsecond. E.g., if the + * timestamp represents "1969-12-31T23:59:59.999999999Z", it will be rounded + * to -1 millisecond. + */ + public static long toMicros(Timestamp timestamp) { + return timestamp.getSeconds() * MICROS_PER_SECOND + timestamp.getNanos() + / NANOS_PER_MICROSECOND; + } + + /** + * Convert a Duration to the number of microseconds.The result will be + * rounded towards 0 to the nearest microseconds. E.g., if the duration + * represents -1 nanosecond, it will be rounded to 0. + */ + public static long toMicros(Duration duration) { + return duration.getSeconds() * MICROS_PER_SECOND + duration.getNanos() + / NANOS_PER_MICROSECOND; + } + + /** + * Create a Timestamp from the number of nanoseconds elapsed from the epoch. + */ + public static Timestamp createTimestampFromNanos(long nanoseconds) { + return normalizedTimestamp(nanoseconds / NANOS_PER_SECOND, + (int) (nanoseconds % NANOS_PER_SECOND)); + } + + /** + * Create a Duration from the number of nanoseconds. + */ + public static Duration createDurationFromNanos(long nanoseconds) { + return normalizedDuration(nanoseconds / NANOS_PER_SECOND, + (int) (nanoseconds % NANOS_PER_SECOND)); + } + + /** + * Convert a Timestamp to the number of nanoseconds elapsed from the epoch. + */ + public static long toNanos(Timestamp timestamp) { + return timestamp.getSeconds() * NANOS_PER_SECOND + timestamp.getNanos(); + } + + /** + * Convert a Duration to the number of nanoseconds. + */ + public static long toNanos(Duration duration) { + return duration.getSeconds() * NANOS_PER_SECOND + duration.getNanos(); + } + + /** + * Get the current time. + */ + public static Timestamp getCurrentTime() { + return createTimestampFromMillis(System.currentTimeMillis()); + } + + /** + * Get the epoch. + */ + public static Timestamp getEpoch() { + return Timestamp.getDefaultInstance(); + } + + /** + * Calculate the difference between two timestamps. + */ + public static Duration distance(Timestamp from, Timestamp to) { + return normalizedDuration(to.getSeconds() - from.getSeconds(), + to.getNanos() - from.getNanos()); + } + + /** + * Add a duration to a timestamp. + */ + public static Timestamp add(Timestamp start, Duration length) { + return normalizedTimestamp(start.getSeconds() + length.getSeconds(), + start.getNanos() + length.getNanos()); + } + + /** + * Subtract a duration from a timestamp. + */ + public static Timestamp subtract(Timestamp start, Duration length) { + return normalizedTimestamp(start.getSeconds() - length.getSeconds(), + start.getNanos() - length.getNanos()); + } + + /** + * Add two durations. + */ + public static Duration add(Duration d1, Duration d2) { + return normalizedDuration(d1.getSeconds() + d2.getSeconds(), + d1.getNanos() + d2.getNanos()); + } + + /** + * Subtract a duration from another. + */ + public static Duration subtract(Duration d1, Duration d2) { + return normalizedDuration(d1.getSeconds() - d2.getSeconds(), + d1.getNanos() - d2.getNanos()); + } + + // Multiplications and divisions. + + public static Duration multiply(Duration duration, double times) { + double result = duration.getSeconds() * times + duration.getNanos() * times + / 1000000000.0; + if (result < Long.MIN_VALUE || result > Long.MAX_VALUE) { + throw new IllegalArgumentException("Result is out of valid range."); + } + long seconds = (long) result; + int nanos = (int) ((result - seconds) * 1000000000); + return normalizedDuration(seconds, nanos); + } + + public static Duration divide(Duration duration, double value) { + return multiply(duration, 1.0 / value); + } + + public static Duration multiply(Duration duration, long times) { + return createDurationFromBigInteger( + toBigInteger(duration).multiply(toBigInteger(times))); + } + + public static Duration divide(Duration duration, long times) { + return createDurationFromBigInteger( + toBigInteger(duration).divide(toBigInteger(times))); + } + + public static long divide(Duration d1, Duration d2) { + return toBigInteger(d1).divide(toBigInteger(d2)).longValue(); + } + + public static Duration remainder(Duration d1, Duration d2) { + return createDurationFromBigInteger( + toBigInteger(d1).remainder(toBigInteger(d2))); + } + + private static final BigInteger NANOS_PER_SECOND_BIG_INTEGER = + new BigInteger(String.valueOf(NANOS_PER_SECOND)); + + private static BigInteger toBigInteger(Duration duration) { + return toBigInteger(duration.getSeconds()) + .multiply(NANOS_PER_SECOND_BIG_INTEGER) + .add(toBigInteger(duration.getNanos())); + } + + private static BigInteger toBigInteger(long value) { + return new BigInteger(String.valueOf(value)); + } + + private static Duration createDurationFromBigInteger(BigInteger value) { + long seconds = value.divide( + new BigInteger(String.valueOf(NANOS_PER_SECOND))).longValue(); + int nanos = value.remainder( + new BigInteger(String.valueOf(NANOS_PER_SECOND))).intValue(); + return normalizedDuration(seconds, nanos); + + } + + private static Duration normalizedDuration(long seconds, int nanos) { + if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { + seconds += nanos / NANOS_PER_SECOND; + nanos %= NANOS_PER_SECOND; + } + if (seconds > 0 && nanos < 0) { + nanos += NANOS_PER_SECOND; + seconds -= 1; + } + if (seconds < 0 && nanos > 0) { + nanos -= NANOS_PER_SECOND; + seconds += 1; + } + if (seconds < DURATION_SECONDS_MIN || seconds > DURATION_SECONDS_MAX) { + throw new IllegalArgumentException("Duration is out of valid range."); + } + return Duration.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + } + + private static Timestamp normalizedTimestamp(long seconds, int nanos) { + if (nanos <= -NANOS_PER_SECOND || nanos >= NANOS_PER_SECOND) { + seconds += nanos / NANOS_PER_SECOND; + nanos %= NANOS_PER_SECOND; + } + if (nanos < 0) { + nanos += NANOS_PER_SECOND; + seconds -= 1; + } + if (seconds < TIMESTAMP_SECONDS_MIN || seconds > TIMESTAMP_SECONDS_MAX) { + throw new IllegalArgumentException("Timestamp is out of valid range."); + } + return Timestamp.newBuilder().setSeconds(seconds).setNanos(nanos).build(); + } + + /** + * Format the nano part of a timestamp or a duration. + */ + private static String formatNanos(int nanos) { + assert nanos >= 1 && nanos <= 999999999; + // Determine whether to use 3, 6, or 9 digits for the nano part. + if (nanos % NANOS_PER_MILLISECOND == 0) { + return String.format("%1$03d", nanos / NANOS_PER_MILLISECOND); + } else if (nanos % NANOS_PER_MICROSECOND == 0) { + return String.format("%1$06d", nanos / NANOS_PER_MICROSECOND); + } else { + return String.format("%1$09d", nanos); + } + } + + private static int parseNanos(String value) throws ParseException { + int result = 0; + for (int i = 0; i < 9; ++i) { + result = result * 10; + if (i < value.length()) { + if (value.charAt(i) < '0' || value.charAt(i) > '9') { + throw new ParseException("Invalid nanosecnds.", 0); + } + result += value.charAt(i) - '0'; + } + } + return result; + } + + private static long parseTimezoneOffset(String value) throws ParseException { + int pos = value.indexOf(':'); + if (pos == -1) { + throw new ParseException("Invalid offset value: " + value, 0); + } + String hours = value.substring(0, pos); + String minutes = value.substring(pos + 1); + return (Long.parseLong(hours) * 60 + Long.parseLong(minutes)) * 60; + } +} diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java new file mode 100644 index 0000000000..3391f239fe --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskTreeTest.java @@ -0,0 +1,229 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import protobuf_unittest.UnittestProto.NestedTestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypes.NestedMessage; + +import junit.framework.TestCase; + +public class FieldMaskTreeTest extends TestCase { + public void testAddFieldPath() throws Exception { + FieldMaskTree tree = new FieldMaskTree(); + assertEquals("", tree.toString()); + tree.addFieldPath(""); + assertEquals("", tree.toString()); + // New branch. + tree.addFieldPath("foo"); + assertEquals("foo", tree.toString()); + // Redundant path. + tree.addFieldPath("foo"); + assertEquals("foo", tree.toString()); + // New branch. + tree.addFieldPath("bar.baz"); + assertEquals("bar.baz,foo", tree.toString()); + // Redundant sub-path. + tree.addFieldPath("foo.bar"); + assertEquals("bar.baz,foo", tree.toString()); + // New branch from a non-root node. + tree.addFieldPath("bar.quz"); + assertEquals("bar.baz,bar.quz,foo", tree.toString()); + // A path that matches several existing sub-paths. + tree.addFieldPath("bar"); + assertEquals("bar,foo", tree.toString()); + } + + public void testMergeFromFieldMask() throws Exception { + FieldMaskTree tree = new FieldMaskTree( + FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); + assertEquals("bar.baz,bar.quz,foo", tree.toString()); + tree.mergeFromFieldMask( + FieldMaskUtil.fromString("foo.bar,bar")); + assertEquals("bar,foo", tree.toString()); + } + + public void testIntersectFieldPath() throws Exception { + FieldMaskTree tree = new FieldMaskTree( + FieldMaskUtil.fromString("foo,bar.baz,bar.quz")); + FieldMaskTree result = new FieldMaskTree(); + // Empty path. + tree.intersectFieldPath("", result); + assertEquals("", result.toString()); + // Non-exist path. + tree.intersectFieldPath("quz", result); + assertEquals("", result.toString()); + // Sub-path of an existing leaf. + tree.intersectFieldPath("foo.bar", result); + assertEquals("foo.bar", result.toString()); + // Match an existing leaf node. + tree.intersectFieldPath("foo", result); + assertEquals("foo", result.toString()); + // Non-exist path. + tree.intersectFieldPath("bar.foo", result); + assertEquals("foo", result.toString()); + // Match a non-leaf node. + tree.intersectFieldPath("bar", result); + assertEquals("bar.baz,bar.quz,foo", result.toString()); + } + + public void testMerge() throws Exception { + TestAllTypes value = TestAllTypes.newBuilder() + .setOptionalInt32(1234) + .setOptionalNestedMessage(NestedMessage.newBuilder().setBb(5678)) + .addRepeatedInt32(4321) + .addRepeatedNestedMessage(NestedMessage.newBuilder().setBb(8765)) + .build(); + NestedTestAllTypes source = NestedTestAllTypes.newBuilder() + .setPayload(value) + .setChild(NestedTestAllTypes.newBuilder().setPayload(value)) + .build(); + // Now we have a message source with the following structure: + // [root] -+- payload -+- optional_int32 + // | +- optional_nested_message + // | +- repeated_int32 + // | +- repeated_nested_message + // | + // +- child --- payload -+- optional_int32 + // +- optional_nested_message + // +- repeated_int32 + // +- repeated_nested_message + + FieldMaskUtil.MergeOptions options = new FieldMaskUtil.MergeOptions(); + + // Test merging each individual field. + NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("payload.optional_int32") + .merge(source, builder, options); + NestedTestAllTypes.Builder expected = NestedTestAllTypes.newBuilder(); + expected.getPayloadBuilder().setOptionalInt32(1234); + assertEquals(expected.build(), builder.build()); + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("payload.optional_nested_message") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getPayloadBuilder().setOptionalNestedMessage( + NestedMessage.newBuilder().setBb(5678)); + assertEquals(expected.build(), builder.build()); + + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("payload.repeated_int32") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getPayloadBuilder().addRepeatedInt32(4321); + assertEquals(expected.build(), builder.build()); + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("payload.repeated_nested_message") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getPayloadBuilder().addRepeatedNestedMessage( + NestedMessage.newBuilder().setBb(8765)); + assertEquals(expected.build(), builder.build()); + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("child.payload.optional_int32") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getChildBuilder().getPayloadBuilder().setOptionalInt32(1234); + assertEquals(expected.build(), builder.build()); + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("child.payload.optional_nested_message") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getChildBuilder().getPayloadBuilder().setOptionalNestedMessage( + NestedMessage.newBuilder().setBb(5678)); + assertEquals(expected.build(), builder.build()); + + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("child.payload.repeated_int32") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getChildBuilder().getPayloadBuilder().addRepeatedInt32(4321); + assertEquals(expected.build(), builder.build()); + + + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("child.payload.repeated_nested_message") + .merge(source, builder, options); + expected = NestedTestAllTypes.newBuilder(); + expected.getChildBuilder().getPayloadBuilder().addRepeatedNestedMessage( + NestedMessage.newBuilder().setBb(8765)); + assertEquals(expected.build(), builder.build()); + + // Test merging all fields. + builder = NestedTestAllTypes.newBuilder(); + new FieldMaskTree().addFieldPath("child").addFieldPath("payload") + .merge(source, builder, options); + assertEquals(source, builder.build()); + + // Test repeated options. + builder = NestedTestAllTypes.newBuilder(); + builder.getPayloadBuilder().addRepeatedInt32(1000); + new FieldMaskTree().addFieldPath("payload.repeated_int32") + .merge(source, builder, options); + // Default behavior is to append repeated fields. + assertEquals(2, builder.getPayload().getRepeatedInt32Count()); + assertEquals(1000, builder.getPayload().getRepeatedInt32(0)); + assertEquals(4321, builder.getPayload().getRepeatedInt32(1)); + // Change to replace repeated fields. + options.setReplaceRepeatedFields(true); + new FieldMaskTree().addFieldPath("payload.repeated_int32") + .merge(source, builder, options); + assertEquals(1, builder.getPayload().getRepeatedInt32Count()); + assertEquals(4321, builder.getPayload().getRepeatedInt32(0)); + + // Test message options. + builder = NestedTestAllTypes.newBuilder(); + builder.getPayloadBuilder().setOptionalInt32(1000); + builder.getPayloadBuilder().setOptionalUint32(2000); + new FieldMaskTree().addFieldPath("payload") + .merge(source, builder, options); + // Default behavior is to merge message fields. + assertEquals(1234, builder.getPayload().getOptionalInt32()); + assertEquals(2000, builder.getPayload().getOptionalUint32()); + + // Change to replace message fields. + options.setReplaceMessageFields(true); + builder = NestedTestAllTypes.newBuilder(); + builder.getPayloadBuilder().setOptionalInt32(1000); + builder.getPayloadBuilder().setOptionalUint32(2000); + new FieldMaskTree().addFieldPath("payload") + .merge(source, builder, options); + assertEquals(1234, builder.getPayload().getOptionalInt32()); + assertEquals(0, builder.getPayload().getOptionalUint32()); + } +} + diff --git a/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java new file mode 100644 index 0000000000..67fbe0b1ac --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/FieldMaskUtilTest.java @@ -0,0 +1,135 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.FieldMask; +import protobuf_unittest.UnittestProto.NestedTestAllTypes; +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +/** Unit tests for {@link FieldMaskUtil}. */ +public class FieldMaskUtilTest extends TestCase { + public void testIsValid() throws Exception { + assertTrue(FieldMaskUtil.isValid(NestedTestAllTypes.class, "payload")); + assertFalse(FieldMaskUtil.isValid(NestedTestAllTypes.class, "nonexist")); + assertTrue(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.optional_int32")); + assertTrue(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.repeated_int32")); + assertTrue(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.optional_nested_message")); + assertTrue(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.repeated_nested_message")); + assertFalse(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.nonexist")); + + assertTrue(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.optional_nested_message.bb")); + // Repeated fields cannot have sub-paths. + assertFalse(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.repeated_nested_message.bb")); + // Non-message fields cannot have sub-paths. + assertFalse(FieldMaskUtil.isValid( + NestedTestAllTypes.class, "payload.optional_int32.bb")); + } + + public void testToString() throws Exception { + assertEquals("", FieldMaskUtil.toString(FieldMask.getDefaultInstance())); + FieldMask mask = FieldMask.newBuilder().addPaths("foo").build(); + assertEquals("foo", FieldMaskUtil.toString(mask)); + mask = FieldMask.newBuilder().addPaths("foo").addPaths("bar").build(); + assertEquals("foo,bar", FieldMaskUtil.toString(mask)); + + // Empty field paths are ignored. + mask = FieldMask.newBuilder().addPaths("").addPaths("foo").addPaths(""). + addPaths("bar").addPaths("").build(); + assertEquals("foo,bar", FieldMaskUtil.toString(mask)); + } + + public void testFromString() throws Exception { + FieldMask mask = FieldMaskUtil.fromString(""); + assertEquals(0, mask.getPathsCount()); + mask = FieldMaskUtil.fromString("foo"); + assertEquals(1, mask.getPathsCount()); + assertEquals("foo", mask.getPaths(0)); + mask = FieldMaskUtil.fromString("foo,bar.baz"); + assertEquals(2, mask.getPathsCount()); + assertEquals("foo", mask.getPaths(0)); + assertEquals("bar.baz", mask.getPaths(1)); + + // Empty field paths are ignore. + mask = FieldMaskUtil.fromString(",foo,,bar,"); + assertEquals(2, mask.getPathsCount()); + assertEquals("foo", mask.getPaths(0)); + assertEquals("bar", mask.getPaths(1)); + + // Check whether the field paths are valid if a class parameter is provided. + mask = FieldMaskUtil.fromString(NestedTestAllTypes.class, ",payload"); + + try { + mask = FieldMaskUtil.fromString( + NestedTestAllTypes.class, "payload,nonexist"); + fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + } + + public void testUnion() throws Exception { + // Only test a simple case here and expect + // {@link FieldMaskTreeTest#testAddFieldPath} to cover all scenarios. + FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz"); + FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar"); + FieldMask result = FieldMaskUtil.union(mask1, mask2); + assertEquals("bar,foo", FieldMaskUtil.toString(result)); + } + + public void testIntersection() throws Exception { + // Only test a simple case here and expect + // {@link FieldMaskTreeTest#testIntersectFieldPath} to cover all scenarios. + FieldMask mask1 = FieldMaskUtil.fromString("foo,bar.baz,bar.quz"); + FieldMask mask2 = FieldMaskUtil.fromString("foo.bar,bar"); + FieldMask result = FieldMaskUtil.intersection(mask1, mask2); + assertEquals("bar.baz,bar.quz,foo.bar", FieldMaskUtil.toString(result)); + } + + public void testMerge() throws Exception { + // Only test a simple case here and expect + // {@link FieldMaskTreeTest#testMerge} to cover all scenarios. + NestedTestAllTypes source = NestedTestAllTypes.newBuilder() + .setPayload(TestAllTypes.newBuilder().setOptionalInt32(1234)) + .build(); + NestedTestAllTypes.Builder builder = NestedTestAllTypes.newBuilder(); + FieldMaskUtil.merge(FieldMaskUtil.fromString("payload"), source, builder); + assertEquals(1234, builder.getPayload().getOptionalInt32()); + } +} diff --git a/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java new file mode 100644 index 0000000000..ddf5ad2a4f --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/JsonFormatTest.java @@ -0,0 +1,976 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.Any; +import com.google.protobuf.BoolValue; +import com.google.protobuf.ByteString; +import com.google.protobuf.BytesValue; +import com.google.protobuf.DoubleValue; +import com.google.protobuf.FloatValue; +import com.google.protobuf.Int32Value; +import com.google.protobuf.Int64Value; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.ListValue; +import com.google.protobuf.Message; +import com.google.protobuf.StringValue; +import com.google.protobuf.Struct; +import com.google.protobuf.UInt32Value; +import com.google.protobuf.UInt64Value; +import com.google.protobuf.Value; +import com.google.protobuf.util.JsonFormat.TypeRegistry; +import com.google.protobuf.util.JsonTestProto.TestAllTypes; +import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedEnum; +import com.google.protobuf.util.JsonTestProto.TestAllTypes.NestedMessage; +import com.google.protobuf.util.JsonTestProto.TestAny; +import com.google.protobuf.util.JsonTestProto.TestDuration; +import com.google.protobuf.util.JsonTestProto.TestFieldMask; +import com.google.protobuf.util.JsonTestProto.TestMap; +import com.google.protobuf.util.JsonTestProto.TestStruct; +import com.google.protobuf.util.JsonTestProto.TestTimestamp; +import com.google.protobuf.util.JsonTestProto.TestWrappers; + +import junit.framework.TestCase; + +import java.io.IOException; +import java.math.BigDecimal; +import java.math.BigInteger; + +public class JsonFormatTest extends TestCase { + private void setAllFields(TestAllTypes.Builder builder) { + builder.setOptionalInt32(1234); + builder.setOptionalInt64(1234567890123456789L); + builder.setOptionalUint32(5678); + builder.setOptionalUint64(2345678901234567890L); + builder.setOptionalSint32(9012); + builder.setOptionalSint64(3456789012345678901L); + builder.setOptionalFixed32(3456); + builder.setOptionalFixed64(4567890123456789012L); + builder.setOptionalSfixed32(7890); + builder.setOptionalSfixed64(5678901234567890123L); + builder.setOptionalFloat(1.5f); + builder.setOptionalDouble(1.25); + builder.setOptionalBool(true); + builder.setOptionalString("Hello world!"); + builder.setOptionalBytes(ByteString.copyFrom(new byte[]{0, 1, 2})); + builder.setOptionalNestedEnum(NestedEnum.BAR); + builder.getOptionalNestedMessageBuilder().setValue(100); + + builder.addRepeatedInt32(1234); + builder.addRepeatedInt64(1234567890123456789L); + builder.addRepeatedUint32(5678); + builder.addRepeatedUint64(2345678901234567890L); + builder.addRepeatedSint32(9012); + builder.addRepeatedSint64(3456789012345678901L); + builder.addRepeatedFixed32(3456); + builder.addRepeatedFixed64(4567890123456789012L); + builder.addRepeatedSfixed32(7890); + builder.addRepeatedSfixed64(5678901234567890123L); + builder.addRepeatedFloat(1.5f); + builder.addRepeatedDouble(1.25); + builder.addRepeatedBool(true); + builder.addRepeatedString("Hello world!"); + builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{0, 1, 2})); + builder.addRepeatedNestedEnum(NestedEnum.BAR); + builder.addRepeatedNestedMessageBuilder().setValue(100); + + builder.addRepeatedInt32(234); + builder.addRepeatedInt64(234567890123456789L); + builder.addRepeatedUint32(678); + builder.addRepeatedUint64(345678901234567890L); + builder.addRepeatedSint32(012); + builder.addRepeatedSint64(456789012345678901L); + builder.addRepeatedFixed32(456); + builder.addRepeatedFixed64(567890123456789012L); + builder.addRepeatedSfixed32(890); + builder.addRepeatedSfixed64(678901234567890123L); + builder.addRepeatedFloat(11.5f); + builder.addRepeatedDouble(11.25); + builder.addRepeatedBool(true); + builder.addRepeatedString("ello world!"); + builder.addRepeatedBytes(ByteString.copyFrom(new byte[]{1, 2})); + builder.addRepeatedNestedEnum(NestedEnum.BAZ); + builder.addRepeatedNestedMessageBuilder().setValue(200); + } + + private void assertRoundTripEquals(Message message) throws Exception { + assertRoundTripEquals(message, TypeRegistry.getEmptyTypeRegistry()); + } + + private void assertRoundTripEquals(Message message, TypeRegistry registry) throws Exception { + JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); + JsonFormat.Parser parser = JsonFormat.parser().usingTypeRegistry(registry); + Message.Builder builder = message.newBuilderForType(); + parser.merge(printer.print(message), builder); + Message parsedMessage = builder.build(); + assertEquals(message.toString(), parsedMessage.toString()); + } + + private String toJsonString(Message message) throws IOException { + return JsonFormat.printer().print(message); + } + + private void mergeFromJson(String json, Message.Builder builder) throws IOException { + JsonFormat.parser().merge(json, builder); + } + + public void testAllFields() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + setAllFields(builder); + TestAllTypes message = builder.build(); + + assertEquals( + "{\n" + + " \"optionalInt32\": 1234,\n" + + " \"optionalInt64\": \"1234567890123456789\",\n" + + " \"optionalUint32\": 5678,\n" + + " \"optionalUint64\": \"2345678901234567890\",\n" + + " \"optionalSint32\": 9012,\n" + + " \"optionalSint64\": \"3456789012345678901\",\n" + + " \"optionalFixed32\": 3456,\n" + + " \"optionalFixed64\": \"4567890123456789012\",\n" + + " \"optionalSfixed32\": 7890,\n" + + " \"optionalSfixed64\": \"5678901234567890123\",\n" + + " \"optionalFloat\": 1.5,\n" + + " \"optionalDouble\": 1.25,\n" + + " \"optionalBool\": true,\n" + + " \"optionalString\": \"Hello world!\",\n" + + " \"optionalBytes\": \"AAEC\",\n" + + " \"optionalNestedMessage\": {\n" + + " \"value\": 100\n" + + " },\n" + + " \"optionalNestedEnum\": \"BAR\",\n" + + " \"repeatedInt32\": [1234, 234],\n" + + " \"repeatedInt64\": [\"1234567890123456789\", \"234567890123456789\"],\n" + + " \"repeatedUint32\": [5678, 678],\n" + + " \"repeatedUint64\": [\"2345678901234567890\", \"345678901234567890\"],\n" + + " \"repeatedSint32\": [9012, 10],\n" + + " \"repeatedSint64\": [\"3456789012345678901\", \"456789012345678901\"],\n" + + " \"repeatedFixed32\": [3456, 456],\n" + + " \"repeatedFixed64\": [\"4567890123456789012\", \"567890123456789012\"],\n" + + " \"repeatedSfixed32\": [7890, 890],\n" + + " \"repeatedSfixed64\": [\"5678901234567890123\", \"678901234567890123\"],\n" + + " \"repeatedFloat\": [1.5, 11.5],\n" + + " \"repeatedDouble\": [1.25, 11.25],\n" + + " \"repeatedBool\": [true, true],\n" + + " \"repeatedString\": [\"Hello world!\", \"ello world!\"],\n" + + " \"repeatedBytes\": [\"AAEC\", \"AQI=\"],\n" + + " \"repeatedNestedMessage\": [{\n" + + " \"value\": 100\n" + + " }, {\n" + + " \"value\": 200\n" + + " }],\n" + + " \"repeatedNestedEnum\": [\"BAR\", \"BAZ\"]\n" + + "}", + toJsonString(message)); + + assertRoundTripEquals(message); + } + + public void testUnknownEnumValues() throws Exception { + // Unknown enum values will be dropped. + // TODO(xiaofeng): We may want to revisit this (whether we should omit + // unknown enum values). + TestAllTypes message = TestAllTypes.newBuilder() + .setOptionalNestedEnumValue(12345) + .addRepeatedNestedEnumValue(12345) + .addRepeatedNestedEnumValue(0) + .build(); + assertEquals( + "{\n" + + " \"repeatedNestedEnum\": [\"FOO\"]\n" + + "}", toJsonString(message)); + + TestMap.Builder mapBuilder = TestMap.newBuilder(); + mapBuilder.getMutableInt32ToEnumMapValue().put(1, 0); + mapBuilder.getMutableInt32ToEnumMapValue().put(2, 12345); + TestMap mapMessage = mapBuilder.build(); + assertEquals( + "{\n" + + " \"int32ToEnumMap\": {\n" + + " \"1\": \"FOO\"\n" + + " }\n" + + "}", toJsonString(mapMessage)); + } + + public void testSpecialFloatValues() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder() + .addRepeatedFloat(Float.NaN) + .addRepeatedFloat(Float.POSITIVE_INFINITY) + .addRepeatedFloat(Float.NEGATIVE_INFINITY) + .addRepeatedDouble(Double.NaN) + .addRepeatedDouble(Double.POSITIVE_INFINITY) + .addRepeatedDouble(Double.NEGATIVE_INFINITY) + .build(); + assertEquals( + "{\n" + + " \"repeatedFloat\": [\"NaN\", \"Infinity\", \"-Infinity\"],\n" + + " \"repeatedDouble\": [\"NaN\", \"Infinity\", \"-Infinity\"]\n" + + "}", toJsonString(message)); + + assertRoundTripEquals(message); + } + + public void testParserAcceptStringForNumbericField() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalInt32\": \"1234\",\n" + + " \"optionalUint32\": \"5678\",\n" + + " \"optionalSint32\": \"9012\",\n" + + " \"optionalFixed32\": \"3456\",\n" + + " \"optionalSfixed32\": \"7890\",\n" + + " \"optionalFloat\": \"1.5\",\n" + + " \"optionalDouble\": \"1.25\",\n" + + " \"optionalBool\": \"true\"\n" + + "}", builder); + TestAllTypes message = builder.build(); + assertEquals(1234, message.getOptionalInt32()); + assertEquals(5678, message.getOptionalUint32()); + assertEquals(9012, message.getOptionalSint32()); + assertEquals(3456, message.getOptionalFixed32()); + assertEquals(7890, message.getOptionalSfixed32()); + assertEquals(1.5f, message.getOptionalFloat()); + assertEquals(1.25, message.getOptionalDouble()); + assertEquals(true, message.getOptionalBool()); + } + + private void assertRejects(String name, String value) { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + try { + // Numeric form is rejected. + mergeFromJson("{\"" + name + "\":" + value + "}", builder); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + try { + // String form is also rejected. + mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + } + + private void assertAccepts(String name, String value) throws IOException { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + // Both numeric form and string form are accepted. + mergeFromJson("{\"" + name + "\":" + value + "}", builder); + mergeFromJson("{\"" + name + "\":\"" + value + "\"}", builder); + } + + public void testParserRejectOutOfRangeNumericValues() throws Exception { + assertAccepts("optionalInt32", String.valueOf(Integer.MAX_VALUE)); + assertAccepts("optionalInt32", String.valueOf(Integer.MIN_VALUE)); + assertRejects("optionalInt32", String.valueOf(Integer.MAX_VALUE + 1L)); + assertRejects("optionalInt32", String.valueOf(Integer.MIN_VALUE - 1L)); + + assertAccepts("optionalUint32", String.valueOf(Integer.MAX_VALUE + 1L)); + assertRejects("optionalUint32", "123456789012345"); + assertRejects("optionalUint32", "-1"); + + BigInteger one = new BigInteger("1"); + BigInteger maxLong = new BigInteger(String.valueOf(Long.MAX_VALUE)); + BigInteger minLong = new BigInteger(String.valueOf(Long.MIN_VALUE)); + assertAccepts("optionalInt64", maxLong.toString()); + assertAccepts("optionalInt64", minLong.toString()); + assertRejects("optionalInt64", maxLong.add(one).toString()); + assertRejects("optionalInt64", minLong.subtract(one).toString()); + + assertAccepts("optionalUint64", maxLong.add(one).toString()); + assertRejects("optionalUint64", "1234567890123456789012345"); + assertRejects("optionalUint64", "-1"); + + assertAccepts("optionalBool", "true"); + assertRejects("optionalBool", "1"); + assertRejects("optionalBool", "0"); + + assertAccepts("optionalFloat", String.valueOf(Float.MAX_VALUE)); + assertAccepts("optionalFloat", String.valueOf(-Float.MAX_VALUE)); + assertRejects("optionalFloat", String.valueOf(Double.MAX_VALUE)); + assertRejects("optionalFloat", String.valueOf(-Double.MAX_VALUE)); + + BigDecimal moreThanOne = new BigDecimal("1.000001"); + BigDecimal maxDouble = new BigDecimal(Double.MAX_VALUE); + BigDecimal minDouble = new BigDecimal(-Double.MAX_VALUE); + assertAccepts("optionalDouble", maxDouble.toString()); + assertAccepts("optionalDouble", minDouble.toString()); + assertRejects("optionalDouble", maxDouble.multiply(moreThanOne).toString()); + assertRejects("optionalDouble", minDouble.multiply(moreThanOne).toString()); + } + + public void testParserAcceptNull() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalInt32\": null,\n" + + " \"optionalInt64\": null,\n" + + " \"optionalUint32\": null,\n" + + " \"optionalUint64\": null,\n" + + " \"optionalSint32\": null,\n" + + " \"optionalSint64\": null,\n" + + " \"optionalFixed32\": null,\n" + + " \"optionalFixed64\": null,\n" + + " \"optionalSfixed32\": null,\n" + + " \"optionalSfixed64\": null,\n" + + " \"optionalFloat\": null,\n" + + " \"optionalDouble\": null,\n" + + " \"optionalBool\": null,\n" + + " \"optionalString\": null,\n" + + " \"optionalBytes\": null,\n" + + " \"optionalNestedMessage\": null,\n" + + " \"optionalNestedEnum\": null,\n" + + " \"repeatedInt32\": null,\n" + + " \"repeatedInt64\": null,\n" + + " \"repeatedUint32\": null,\n" + + " \"repeatedUint64\": null,\n" + + " \"repeatedSint32\": null,\n" + + " \"repeatedSint64\": null,\n" + + " \"repeatedFixed32\": null,\n" + + " \"repeatedFixed64\": null,\n" + + " \"repeatedSfixed32\": null,\n" + + " \"repeatedSfixed64\": null,\n" + + " \"repeatedFloat\": null,\n" + + " \"repeatedDouble\": null,\n" + + " \"repeatedBool\": null,\n" + + " \"repeatedString\": null,\n" + + " \"repeatedBytes\": null,\n" + + " \"repeatedNestedMessage\": null,\n" + + " \"repeatedNestedEnum\": null\n" + + "}", builder); + TestAllTypes message = builder.build(); + assertEquals(TestAllTypes.getDefaultInstance(), message); + + // Repeated field elements can also be null. + builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"repeatedInt32\": [null, null],\n" + + " \"repeatedInt64\": [null, null],\n" + + " \"repeatedUint32\": [null, null],\n" + + " \"repeatedUint64\": [null, null],\n" + + " \"repeatedSint32\": [null, null],\n" + + " \"repeatedSint64\": [null, null],\n" + + " \"repeatedFixed32\": [null, null],\n" + + " \"repeatedFixed64\": [null, null],\n" + + " \"repeatedSfixed32\": [null, null],\n" + + " \"repeatedSfixed64\": [null, null],\n" + + " \"repeatedFloat\": [null, null],\n" + + " \"repeatedDouble\": [null, null],\n" + + " \"repeatedBool\": [null, null],\n" + + " \"repeatedString\": [null, null],\n" + + " \"repeatedBytes\": [null, null],\n" + + " \"repeatedNestedMessage\": [null, null],\n" + + " \"repeatedNestedEnum\": [null, null]\n" + + "}", builder); + message = builder.build(); + // "null" elements will be parsed to default values. + assertEquals(2, message.getRepeatedInt32Count()); + assertEquals(0, message.getRepeatedInt32(0)); + assertEquals(0, message.getRepeatedInt32(1)); + assertEquals(2, message.getRepeatedInt32Count()); + assertEquals(0, message.getRepeatedInt32(0)); + assertEquals(0, message.getRepeatedInt32(1)); + assertEquals(2, message.getRepeatedInt64Count()); + assertEquals(0, message.getRepeatedInt64(0)); + assertEquals(0, message.getRepeatedInt64(1)); + assertEquals(2, message.getRepeatedUint32Count()); + assertEquals(0, message.getRepeatedUint32(0)); + assertEquals(0, message.getRepeatedUint32(1)); + assertEquals(2, message.getRepeatedUint64Count()); + assertEquals(0, message.getRepeatedUint64(0)); + assertEquals(0, message.getRepeatedUint64(1)); + assertEquals(2, message.getRepeatedSint32Count()); + assertEquals(0, message.getRepeatedSint32(0)); + assertEquals(0, message.getRepeatedSint32(1)); + assertEquals(2, message.getRepeatedSint64Count()); + assertEquals(0, message.getRepeatedSint64(0)); + assertEquals(0, message.getRepeatedSint64(1)); + assertEquals(2, message.getRepeatedFixed32Count()); + assertEquals(0, message.getRepeatedFixed32(0)); + assertEquals(0, message.getRepeatedFixed32(1)); + assertEquals(2, message.getRepeatedFixed64Count()); + assertEquals(0, message.getRepeatedFixed64(0)); + assertEquals(0, message.getRepeatedFixed64(1)); + assertEquals(2, message.getRepeatedSfixed32Count()); + assertEquals(0, message.getRepeatedSfixed32(0)); + assertEquals(0, message.getRepeatedSfixed32(1)); + assertEquals(2, message.getRepeatedSfixed64Count()); + assertEquals(0, message.getRepeatedSfixed64(0)); + assertEquals(0, message.getRepeatedSfixed64(1)); + assertEquals(2, message.getRepeatedFloatCount()); + assertEquals(0f, message.getRepeatedFloat(0)); + assertEquals(0f, message.getRepeatedFloat(1)); + assertEquals(2, message.getRepeatedDoubleCount()); + assertEquals(0.0, message.getRepeatedDouble(0)); + assertEquals(0.0, message.getRepeatedDouble(1)); + assertEquals(2, message.getRepeatedBoolCount()); + assertFalse(message.getRepeatedBool(0)); + assertFalse(message.getRepeatedBool(1)); + assertEquals(2, message.getRepeatedStringCount()); + assertTrue(message.getRepeatedString(0).isEmpty()); + assertTrue(message.getRepeatedString(1).isEmpty()); + assertEquals(2, message.getRepeatedBytesCount()); + assertTrue(message.getRepeatedBytes(0).isEmpty()); + assertTrue(message.getRepeatedBytes(1).isEmpty()); + assertEquals(2, message.getRepeatedNestedMessageCount()); + assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(0)); + assertEquals(NestedMessage.getDefaultInstance(), message.getRepeatedNestedMessage(1)); + assertEquals(2, message.getRepeatedNestedEnumCount()); + assertEquals(0, message.getRepeatedNestedEnumValue(0)); + assertEquals(0, message.getRepeatedNestedEnumValue(1)); + } + + public void testMapFields() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + builder.getMutableInt32ToInt32Map().put(1, 10); + builder.getMutableInt64ToInt32Map().put(1234567890123456789L, 10); + builder.getMutableUint32ToInt32Map().put(2, 20); + builder.getMutableUint64ToInt32Map().put(2234567890123456789L, 20); + builder.getMutableSint32ToInt32Map().put(3, 30); + builder.getMutableSint64ToInt32Map().put(3234567890123456789L, 30); + builder.getMutableFixed32ToInt32Map().put(4, 40); + builder.getMutableFixed64ToInt32Map().put(4234567890123456789L, 40); + builder.getMutableSfixed32ToInt32Map().put(5, 50); + builder.getMutableSfixed64ToInt32Map().put(5234567890123456789L, 50); + builder.getMutableBoolToInt32Map().put(false, 6); + builder.getMutableStringToInt32Map().put("Hello", 10); + + builder.getMutableInt32ToInt64Map().put(1, 1234567890123456789L); + builder.getMutableInt32ToUint32Map().put(2, 20); + builder.getMutableInt32ToUint64Map().put(2, 2234567890123456789L); + builder.getMutableInt32ToSint32Map().put(3, 30); + builder.getMutableInt32ToSint64Map().put(3, 3234567890123456789L); + builder.getMutableInt32ToFixed32Map().put(4, 40); + builder.getMutableInt32ToFixed64Map().put(4, 4234567890123456789L); + builder.getMutableInt32ToSfixed32Map().put(5, 50); + builder.getMutableInt32ToSfixed64Map().put(5, 5234567890123456789L); + builder.getMutableInt32ToFloatMap().put(6, 1.5f); + builder.getMutableInt32ToDoubleMap().put(6, 1.25); + builder.getMutableInt32ToBoolMap().put(7, false); + builder.getMutableInt32ToStringMap().put(7, "World"); + builder.getMutableInt32ToBytesMap().put( + 8, ByteString.copyFrom(new byte[]{1, 2, 3})); + builder.getMutableInt32ToMessageMap().put( + 8, NestedMessage.newBuilder().setValue(1234).build()); + builder.getMutableInt32ToEnumMap().put(9, NestedEnum.BAR); + TestMap message = builder.build(); + + assertEquals( + "{\n" + + " \"int32ToInt32Map\": {\n" + + " \"1\": 10\n" + + " },\n" + + " \"int64ToInt32Map\": {\n" + + " \"1234567890123456789\": 10\n" + + " },\n" + + " \"uint32ToInt32Map\": {\n" + + " \"2\": 20\n" + + " },\n" + + " \"uint64ToInt32Map\": {\n" + + " \"2234567890123456789\": 20\n" + + " },\n" + + " \"sint32ToInt32Map\": {\n" + + " \"3\": 30\n" + + " },\n" + + " \"sint64ToInt32Map\": {\n" + + " \"3234567890123456789\": 30\n" + + " },\n" + + " \"fixed32ToInt32Map\": {\n" + + " \"4\": 40\n" + + " },\n" + + " \"fixed64ToInt32Map\": {\n" + + " \"4234567890123456789\": 40\n" + + " },\n" + + " \"sfixed32ToInt32Map\": {\n" + + " \"5\": 50\n" + + " },\n" + + " \"sfixed64ToInt32Map\": {\n" + + " \"5234567890123456789\": 50\n" + + " },\n" + + " \"boolToInt32Map\": {\n" + + " \"false\": 6\n" + + " },\n" + + " \"stringToInt32Map\": {\n" + + " \"Hello\": 10\n" + + " },\n" + + " \"int32ToInt64Map\": {\n" + + " \"1\": \"1234567890123456789\"\n" + + " },\n" + + " \"int32ToUint32Map\": {\n" + + " \"2\": 20\n" + + " },\n" + + " \"int32ToUint64Map\": {\n" + + " \"2\": \"2234567890123456789\"\n" + + " },\n" + + " \"int32ToSint32Map\": {\n" + + " \"3\": 30\n" + + " },\n" + + " \"int32ToSint64Map\": {\n" + + " \"3\": \"3234567890123456789\"\n" + + " },\n" + + " \"int32ToFixed32Map\": {\n" + + " \"4\": 40\n" + + " },\n" + + " \"int32ToFixed64Map\": {\n" + + " \"4\": \"4234567890123456789\"\n" + + " },\n" + + " \"int32ToSfixed32Map\": {\n" + + " \"5\": 50\n" + + " },\n" + + " \"int32ToSfixed64Map\": {\n" + + " \"5\": \"5234567890123456789\"\n" + + " },\n" + + " \"int32ToFloatMap\": {\n" + + " \"6\": 1.5\n" + + " },\n" + + " \"int32ToDoubleMap\": {\n" + + " \"6\": 1.25\n" + + " },\n" + + " \"int32ToBoolMap\": {\n" + + " \"7\": false\n" + + " },\n" + + " \"int32ToStringMap\": {\n" + + " \"7\": \"World\"\n" + + " },\n" + + " \"int32ToBytesMap\": {\n" + + " \"8\": \"AQID\"\n" + + " },\n" + + " \"int32ToMessageMap\": {\n" + + " \"8\": {\n" + + " \"value\": 1234\n" + + " }\n" + + " },\n" + + " \"int32ToEnumMap\": {\n" + + " \"9\": \"BAR\"\n" + + " }\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + + // Test multiple entries. + builder = TestMap.newBuilder(); + builder.getMutableInt32ToInt32Map().put(1, 2); + builder.getMutableInt32ToInt32Map().put(3, 4); + message = builder.build(); + + assertEquals( + "{\n" + + " \"int32ToInt32Map\": {\n" + + " \"1\": 2,\n" + + " \"3\": 4\n" + + " }\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testMapNullValueIsDefault() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + mergeFromJson( + "{\n" + + " \"int32ToInt32Map\": {\"1\": null},\n" + + " \"int32ToMessageMap\": {\"2\": null}\n" + + "}", builder); + TestMap message = builder.build(); + assertTrue(message.getInt32ToInt32Map().containsKey(1)); + assertEquals(0, message.getInt32ToInt32Map().get(1).intValue()); + assertTrue(message.getInt32ToMessageMap().containsKey(2)); + assertEquals(0, message.getInt32ToMessageMap().get(2).getValue()); + } + + public void testParserAcceptNonQuotedObjectKey() throws Exception { + TestMap.Builder builder = TestMap.newBuilder(); + mergeFromJson( + "{\n" + + " int32ToInt32Map: {1: 2},\n" + + " stringToInt32Map: {hello: 3}\n" + + "}", builder); + TestMap message = builder.build(); + assertEquals(2, message.getInt32ToInt32Map().get(1).intValue()); + assertEquals(3, message.getStringToInt32Map().get("hello").intValue()); + } + + public void testWrappers() throws Exception { + TestWrappers.Builder builder = TestWrappers.newBuilder(); + builder.getBoolValueBuilder().setValue(false); + builder.getInt32ValueBuilder().setValue(0); + builder.getInt64ValueBuilder().setValue(0); + builder.getUint32ValueBuilder().setValue(0); + builder.getUint64ValueBuilder().setValue(0); + builder.getFloatValueBuilder().setValue(0.0f); + builder.getDoubleValueBuilder().setValue(0.0); + builder.getStringValueBuilder().setValue(""); + builder.getBytesValueBuilder().setValue(ByteString.EMPTY); + TestWrappers message = builder.build(); + + assertEquals( + "{\n" + + " \"int32Value\": 0,\n" + + " \"uint32Value\": 0,\n" + + " \"int64Value\": \"0\",\n" + + " \"uint64Value\": \"0\",\n" + + " \"floatValue\": 0.0,\n" + + " \"doubleValue\": 0.0,\n" + + " \"boolValue\": false,\n" + + " \"stringValue\": \"\",\n" + + " \"bytesValue\": \"\"\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + + builder = TestWrappers.newBuilder(); + builder.getBoolValueBuilder().setValue(true); + builder.getInt32ValueBuilder().setValue(1); + builder.getInt64ValueBuilder().setValue(2); + builder.getUint32ValueBuilder().setValue(3); + builder.getUint64ValueBuilder().setValue(4); + builder.getFloatValueBuilder().setValue(5.0f); + builder.getDoubleValueBuilder().setValue(6.0); + builder.getStringValueBuilder().setValue("7"); + builder.getBytesValueBuilder().setValue(ByteString.copyFrom(new byte[]{8})); + message = builder.build(); + + assertEquals( + "{\n" + + " \"int32Value\": 1,\n" + + " \"uint32Value\": 3,\n" + + " \"int64Value\": \"2\",\n" + + " \"uint64Value\": \"4\",\n" + + " \"floatValue\": 5.0,\n" + + " \"doubleValue\": 6.0,\n" + + " \"boolValue\": true,\n" + + " \"stringValue\": \"7\",\n" + + " \"bytesValue\": \"CA==\"\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testTimestamp() throws Exception { + TestTimestamp message = TestTimestamp.newBuilder() + .setTimestampValue(TimeUtil.parseTimestamp("1970-01-01T00:00:00Z")) + .build(); + + assertEquals( + "{\n" + + " \"timestampValue\": \"1970-01-01T00:00:00Z\"\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testDuration() throws Exception { + TestDuration message = TestDuration.newBuilder() + .setDurationValue(TimeUtil.parseDuration("12345s")) + .build(); + + assertEquals( + "{\n" + + " \"durationValue\": \"12345s\"\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testFieldMask() throws Exception { + TestFieldMask message = TestFieldMask.newBuilder() + .setFieldMaskValue(FieldMaskUtil.fromString("foo.bar,baz")) + .build(); + + assertEquals( + "{\n" + + " \"fieldMaskValue\": \"foo.bar,baz\"\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testStruct() throws Exception { + // Build a struct with all possible values. + TestStruct.Builder builder = TestStruct.newBuilder(); + Struct.Builder structBuilder = builder.getStructValueBuilder(); + structBuilder.getMutableFields().put( + "null_value", Value.newBuilder().setNullValueValue(0).build()); + structBuilder.getMutableFields().put( + "number_value", Value.newBuilder().setNumberValue(1.25).build()); + structBuilder.getMutableFields().put( + "string_value", Value.newBuilder().setStringValue("hello").build()); + Struct.Builder subStructBuilder = Struct.newBuilder(); + subStructBuilder.getMutableFields().put( + "number_value", Value.newBuilder().setNumberValue(1234).build()); + structBuilder.getMutableFields().put( + "struct_value", Value.newBuilder().setStructValue(subStructBuilder.build()).build()); + ListValue.Builder listBuilder = ListValue.newBuilder(); + listBuilder.addValues(Value.newBuilder().setNumberValue(1.125).build()); + listBuilder.addValues(Value.newBuilder().setNullValueValue(0).build()); + structBuilder.getMutableFields().put( + "list_value", Value.newBuilder().setListValue(listBuilder.build()).build()); + TestStruct message = builder.build(); + + assertEquals( + "{\n" + + " \"structValue\": {\n" + + " \"null_value\": null,\n" + + " \"number_value\": 1.25,\n" + + " \"string_value\": \"hello\",\n" + + " \"struct_value\": {\n" + + " \"number_value\": 1234.0\n" + + " },\n" + + " \"list_value\": [1.125, null]\n" + + " }\n" + + "}", toJsonString(message)); + assertRoundTripEquals(message); + } + + public void testAnyFields() throws Exception { + TestAllTypes content = TestAllTypes.newBuilder().setOptionalInt32(1234).build(); + TestAny message = TestAny.newBuilder().setAnyValue(Any.pack(content)).build(); + + // A TypeRegistry must be provided in order to convert Any types. + try { + toJsonString(message); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + + JsonFormat.TypeRegistry registry = JsonFormat.TypeRegistry.newBuilder() + .add(TestAllTypes.getDescriptor()).build(); + JsonFormat.Printer printer = JsonFormat.printer().usingTypeRegistry(registry); + + assertEquals( + "{\n" + + " \"anyValue\": {\n" + + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + + " \"optionalInt32\": 1234\n" + + " }\n" + + "}" , printer.print(message)); + assertRoundTripEquals(message, registry); + + + // Well-known types have a special formatting when embedded in Any. + // + // 1. Any in Any. + Any anyMessage = Any.pack(Any.pack(content)); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Any\",\n" + + " \"value\": {\n" + + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + + " \"optionalInt32\": 1234\n" + + " }\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + + // 2. Wrappers in Any. + anyMessage = Any.pack(Int32Value.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Int32Value\",\n" + + " \"value\": 12345\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(UInt32Value.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.UInt32Value\",\n" + + " \"value\": 12345\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(Int64Value.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Int64Value\",\n" + + " \"value\": \"12345\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(UInt64Value.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.UInt64Value\",\n" + + " \"value\": \"12345\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(FloatValue.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.FloatValue\",\n" + + " \"value\": 12345.0\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(DoubleValue.newBuilder().setValue(12345).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.DoubleValue\",\n" + + " \"value\": 12345.0\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(BoolValue.newBuilder().setValue(true).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.BoolValue\",\n" + + " \"value\": true\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(StringValue.newBuilder().setValue("Hello").build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.StringValue\",\n" + + " \"value\": \"Hello\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + anyMessage = Any.pack(BytesValue.newBuilder().setValue( + ByteString.copyFrom(new byte[]{1, 2})).build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.BytesValue\",\n" + + " \"value\": \"AQI=\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + + // 3. Timestamp in Any. + anyMessage = Any.pack(TimeUtil.parseTimestamp("1969-12-31T23:59:59Z")); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Timestamp\",\n" + + " \"value\": \"1969-12-31T23:59:59Z\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + + // 4. Duration in Any + anyMessage = Any.pack(TimeUtil.parseDuration("12345.10s")); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n" + + " \"value\": \"12345.100s\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + + // 5. FieldMask in Any + anyMessage = Any.pack(FieldMaskUtil.fromString("foo.bar,baz")); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.FieldMask\",\n" + + " \"value\": \"foo.bar,baz\"\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + + // 6. Struct in Any + Struct.Builder structBuilder = Struct.newBuilder(); + structBuilder.getMutableFields().put( + "number", Value.newBuilder().setNumberValue(1.125).build()); + anyMessage = Any.pack(structBuilder.build()); + assertEquals( + "{\n" + + " \"@type\": \"type.googleapis.com/google.protobuf.Struct\",\n" + + " \"value\": {\n" + + " \"number\": 1.125\n" + + " }\n" + + "}", printer.print(anyMessage)); + assertRoundTripEquals(anyMessage, registry); + } + + public void testParserMissingTypeUrl() throws Exception { + try { + Any.Builder builder = Any.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalInt32\": 1234\n" + + "}", builder); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + } + + public void testParserUnexpectedTypeUrl() throws Exception { + try { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"@type\": \"type.googleapis.com/json_test.TestAllTypes\",\n" + + " \"optionalInt32\": 12345\n" + + "}", builder); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + } + + public void testParserRejectTrailingComma() throws Exception { + try { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalInt32\": 12345,\n" + + "}", builder); + fail("Exception is expected."); + } catch (IOException e) { + // Expected. + } + + // TODO(xiaofeng): GSON allows trailing comma in arrays even after I set + // the JsonReader to non-lenient mode. If we want to enforce strict JSON + // compliance, we might want to switch to a different JSON parser or + // implement one by ourselves. + // try { + // TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + // JsonFormat.merge( + // "{\n" + // + " \"repeatedInt32\": [12345,]\n" + // + "}", builder); + // fail("Exception is expected."); + // } catch (IOException e) { + // // Expected. + // } + } + + public void testParserRejectInvalidBase64() throws Exception { + try { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalBytes\": \"!@#$\"\n" + + "}", builder); + fail("Exception is expected."); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } + + public void testParserRejectInvalidEnumValue() throws Exception { + try { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + mergeFromJson( + "{\n" + + " \"optionalNestedEnum\": \"XXX\"\n" + + "}", builder); + fail("Exception is expected."); + } catch (InvalidProtocolBufferException e) { + // Expected. + } + } +} diff --git a/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java new file mode 100644 index 0000000000..fe5617e115 --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/TimeUtilTest.java @@ -0,0 +1,439 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package com.google.protobuf.util; + +import com.google.protobuf.Duration; +import com.google.protobuf.Timestamp; + +import junit.framework.TestCase; + +import org.junit.Assert; + +import java.text.ParseException; + +/** Unit tests for {@link TimeUtil}. */ +public class TimeUtilTest extends TestCase { + public void testTimestampStringFormat() throws Exception { + Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z"); + Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z"); + assertEquals(TimeUtil.TIMESTAMP_SECONDS_MIN, start.getSeconds()); + assertEquals(0, start.getNanos()); + assertEquals(TimeUtil.TIMESTAMP_SECONDS_MAX, end.getSeconds()); + assertEquals(999999999, end.getNanos()); + assertEquals("0001-01-01T00:00:00Z", TimeUtil.toString(start)); + assertEquals("9999-12-31T23:59:59.999999999Z", TimeUtil.toString(end)); + + Timestamp value = TimeUtil.parseTimestamp("1970-01-01T00:00:00Z"); + assertEquals(0, value.getSeconds()); + assertEquals(0, value.getNanos()); + + // Test negative timestamps. + value = TimeUtil.parseTimestamp("1969-12-31T23:59:59.999Z"); + assertEquals(-1, value.getSeconds()); + // Nano part is in the range of [0, 999999999] for Timestamp. + assertEquals(999000000, value.getNanos()); + + // Test that 3, 6, or 9 digits are used for the fractional part. + value = Timestamp.newBuilder().setNanos(10).build(); + assertEquals("1970-01-01T00:00:00.000000010Z", TimeUtil.toString(value)); + value = Timestamp.newBuilder().setNanos(10000).build(); + assertEquals("1970-01-01T00:00:00.000010Z", TimeUtil.toString(value)); + value = Timestamp.newBuilder().setNanos(10000000).build(); + assertEquals("1970-01-01T00:00:00.010Z", TimeUtil.toString(value)); + + // Test that parsing accepts timezone offsets. + value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010+08:00"); + assertEquals("1969-12-31T16:00:00.010Z", TimeUtil.toString(value)); + value = TimeUtil.parseTimestamp("1970-01-01T00:00:00.010-08:00"); + assertEquals("1970-01-01T08:00:00.010Z", TimeUtil.toString(value)); + } + + public void testTimetampInvalidFormat() throws Exception { + try { + // Value too small. + Timestamp value = Timestamp.newBuilder() + .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MIN - 1).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Value too large. + Timestamp value = Timestamp.newBuilder() + .setSeconds(TimeUtil.TIMESTAMP_SECONDS_MAX + 1).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Invalid nanos value. + Timestamp value = Timestamp.newBuilder().setNanos(-1).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Invalid nanos value. + Timestamp value = Timestamp.newBuilder().setNanos(1000000000).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Value to small. + TimeUtil.parseTimestamp("0000-01-01T00:00:00Z"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Value to large. + TimeUtil.parseTimestamp("10000-01-01T00:00:00Z"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Missing 'T'. + TimeUtil.parseTimestamp("1970-01-01 00:00:00Z"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Missing 'Z'. + TimeUtil.parseTimestamp("1970-01-01T00:00:00"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Invalid offset. + TimeUtil.parseTimestamp("1970-01-01T00:00:00+0000"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Trailing text. + TimeUtil.parseTimestamp("1970-01-01T00:00:00Z0"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Invalid nanosecond value. + TimeUtil.parseTimestamp("1970-01-01T00:00:00.ABCZ"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + } + + public void testDurationStringFormat() throws Exception { + Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z"); + Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z"); + Duration duration = TimeUtil.distance(start, end); + assertEquals("315537897599.999999999s", TimeUtil.toString(duration)); + duration = TimeUtil.distance(end, start); + assertEquals("-315537897599.999999999s", TimeUtil.toString(duration)); + + // Generated output should contain 3, 6, or 9 fractional digits. + duration = Duration.newBuilder().setSeconds(1).build(); + assertEquals("1s", TimeUtil.toString(duration)); + duration = Duration.newBuilder().setNanos(10000000).build(); + assertEquals("0.010s", TimeUtil.toString(duration)); + duration = Duration.newBuilder().setNanos(10000).build(); + assertEquals("0.000010s", TimeUtil.toString(duration)); + duration = Duration.newBuilder().setNanos(10).build(); + assertEquals("0.000000010s", TimeUtil.toString(duration)); + + // Parsing accepts an fractional digits as long as they fit into nano + // precision. + duration = TimeUtil.parseDuration("0.1s"); + assertEquals(100000000, duration.getNanos()); + duration = TimeUtil.parseDuration("0.0001s"); + assertEquals(100000, duration.getNanos()); + duration = TimeUtil.parseDuration("0.0000001s"); + assertEquals(100, duration.getNanos()); + + // Duration must support range from -315,576,000,000s to +315576000000s + // which includes negative values. + duration = TimeUtil.parseDuration("315576000000.999999999s"); + assertEquals(315576000000L, duration.getSeconds()); + assertEquals(999999999, duration.getNanos()); + duration = TimeUtil.parseDuration("-315576000000.999999999s"); + assertEquals(-315576000000L, duration.getSeconds()); + assertEquals(-999999999, duration.getNanos()); + } + + public void testDurationInvalidFormat() throws Exception { + try { + // Value too small. + Duration value = Duration.newBuilder() + .setSeconds(TimeUtil.DURATION_SECONDS_MIN - 1).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Value too large. + Duration value = Duration.newBuilder() + .setSeconds(TimeUtil.DURATION_SECONDS_MAX + 1).build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Invalid nanos value. + Duration value = Duration.newBuilder().setSeconds(1).setNanos(-1) + .build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Invalid nanos value. + Duration value = Duration.newBuilder().setSeconds(-1).setNanos(1) + .build(); + TimeUtil.toString(value); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + try { + // Value too small. + TimeUtil.parseDuration("-315576000001s"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Value too large. + TimeUtil.parseDuration("315576000001s"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Empty. + TimeUtil.parseDuration(""); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Missing "s". + TimeUtil.parseDuration("0"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Invalid trailing data. + TimeUtil.parseDuration("0s0"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + + try { + // Invalid prefix. + TimeUtil.parseDuration("--1s"); + Assert.fail("Exception is expected."); + } catch (ParseException e) { + // Expected. + } + } + + public void testTimestampConversion() throws Exception { + Timestamp timestamp = + TimeUtil.parseTimestamp("1970-01-01T00:00:01.111111111Z"); + assertEquals(1111111111, TimeUtil.toNanos(timestamp)); + assertEquals(1111111, TimeUtil.toMicros(timestamp)); + assertEquals(1111, TimeUtil.toMillis(timestamp)); + timestamp = TimeUtil.createTimestampFromNanos(1111111111); + assertEquals("1970-01-01T00:00:01.111111111Z", TimeUtil.toString(timestamp)); + timestamp = TimeUtil.createTimestampFromMicros(1111111); + assertEquals("1970-01-01T00:00:01.111111Z", TimeUtil.toString(timestamp)); + timestamp = TimeUtil.createTimestampFromMillis(1111); + assertEquals("1970-01-01T00:00:01.111Z", TimeUtil.toString(timestamp)); + + timestamp = TimeUtil.parseTimestamp("1969-12-31T23:59:59.111111111Z"); + assertEquals(-888888889, TimeUtil.toNanos(timestamp)); + assertEquals(-888889, TimeUtil.toMicros(timestamp)); + assertEquals(-889, TimeUtil.toMillis(timestamp)); + timestamp = TimeUtil.createTimestampFromNanos(-888888889); + assertEquals("1969-12-31T23:59:59.111111111Z", TimeUtil.toString(timestamp)); + timestamp = TimeUtil.createTimestampFromMicros(-888889); + assertEquals("1969-12-31T23:59:59.111111Z", TimeUtil.toString(timestamp)); + timestamp = TimeUtil.createTimestampFromMillis(-889); + assertEquals("1969-12-31T23:59:59.111Z", TimeUtil.toString(timestamp)); + } + + public void testDurationConversion() throws Exception { + Duration duration = TimeUtil.parseDuration("1.111111111s"); + assertEquals(1111111111, TimeUtil.toNanos(duration)); + assertEquals(1111111, TimeUtil.toMicros(duration)); + assertEquals(1111, TimeUtil.toMillis(duration)); + duration = TimeUtil.createDurationFromNanos(1111111111); + assertEquals("1.111111111s", TimeUtil.toString(duration)); + duration = TimeUtil.createDurationFromMicros(1111111); + assertEquals("1.111111s", TimeUtil.toString(duration)); + duration = TimeUtil.createDurationFromMillis(1111); + assertEquals("1.111s", TimeUtil.toString(duration)); + + duration = TimeUtil.parseDuration("-1.111111111s"); + assertEquals(-1111111111, TimeUtil.toNanos(duration)); + assertEquals(-1111111, TimeUtil.toMicros(duration)); + assertEquals(-1111, TimeUtil.toMillis(duration)); + duration = TimeUtil.createDurationFromNanos(-1111111111); + assertEquals("-1.111111111s", TimeUtil.toString(duration)); + duration = TimeUtil.createDurationFromMicros(-1111111); + assertEquals("-1.111111s", TimeUtil.toString(duration)); + duration = TimeUtil.createDurationFromMillis(-1111); + assertEquals("-1.111s", TimeUtil.toString(duration)); + } + + public void testTimeOperations() throws Exception { + Timestamp start = TimeUtil.parseTimestamp("0001-01-01T00:00:00Z"); + Timestamp end = TimeUtil.parseTimestamp("9999-12-31T23:59:59.999999999Z"); + + Duration duration = TimeUtil.distance(start, end); + assertEquals("315537897599.999999999s", TimeUtil.toString(duration)); + Timestamp value = TimeUtil.add(start, duration); + assertEquals(end, value); + value = TimeUtil.subtract(end, duration); + assertEquals(start, value); + + duration = TimeUtil.distance(end, start); + assertEquals("-315537897599.999999999s", TimeUtil.toString(duration)); + value = TimeUtil.add(end, duration); + assertEquals(start, value); + value = TimeUtil.subtract(start, duration); + assertEquals(end, value); + + // Result is larger than Long.MAX_VALUE. + try { + duration = TimeUtil.parseDuration("315537897599.999999999s"); + duration = TimeUtil.multiply(duration, 315537897599.999999999); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + // Result is lesser than Long.MIN_VALUE. + try { + duration = TimeUtil.parseDuration("315537897599.999999999s"); + duration = TimeUtil.multiply(duration, -315537897599.999999999); + Assert.fail("Exception is expected."); + } catch (IllegalArgumentException e) { + // Expected. + } + + duration = TimeUtil.parseDuration("-1.125s"); + duration = TimeUtil.divide(duration, 2.0); + assertEquals("-0.562500s", TimeUtil.toString(duration)); + duration = TimeUtil.multiply(duration, 2.0); + assertEquals("-1.125s", TimeUtil.toString(duration)); + + duration = TimeUtil.add(duration, duration); + assertEquals("-2.250s", TimeUtil.toString(duration)); + + duration = TimeUtil.subtract(duration, TimeUtil.parseDuration("-1s")); + assertEquals("-1.250s", TimeUtil.toString(duration)); + + // Multiplications (with results larger than Long.MAX_VALUE in nanoseconds). + duration = TimeUtil.parseDuration("0.999999999s"); + assertEquals("315575999684.424s", + TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L))); + duration = TimeUtil.parseDuration("-0.999999999s"); + assertEquals("-315575999684.424s", + TimeUtil.toString(TimeUtil.multiply(duration, 315576000000L))); + assertEquals("315575999684.424s", + TimeUtil.toString(TimeUtil.multiply(duration, -315576000000L))); + + // Divisions (with values larger than Long.MAX_VALUE in nanoseconds). + Duration d1 = TimeUtil.parseDuration("315576000000s"); + Duration d2 = TimeUtil.subtract(d1, TimeUtil.createDurationFromNanos(1)); + assertEquals(1, TimeUtil.divide(d1, d2)); + assertEquals(0, TimeUtil.divide(d2, d1)); + assertEquals("0.000000001s", TimeUtil.toString(TimeUtil.remainder(d1, d2))); + assertEquals("315575999999.999999999s", + TimeUtil.toString(TimeUtil.remainder(d2, d1))); + + // Divisions involving negative values. + // + // (-5) / 2 = -2, remainder = -1 + d1 = TimeUtil.parseDuration("-5s"); + d2 = TimeUtil.parseDuration("2s"); + assertEquals(-2, TimeUtil.divide(d1, d2)); + assertEquals(-2, TimeUtil.divide(d1, 2).getSeconds()); + assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds()); + // (-5) / (-2) = 2, remainder = -1 + d1 = TimeUtil.parseDuration("-5s"); + d2 = TimeUtil.parseDuration("-2s"); + assertEquals(2, TimeUtil.divide(d1, d2)); + assertEquals(2, TimeUtil.divide(d1, -2).getSeconds()); + assertEquals(-1, TimeUtil.remainder(d1, d2).getSeconds()); + // 5 / (-2) = -2, remainder = 1 + d1 = TimeUtil.parseDuration("5s"); + d2 = TimeUtil.parseDuration("-2s"); + assertEquals(-2, TimeUtil.divide(d1, d2)); + assertEquals(-2, TimeUtil.divide(d1, -2).getSeconds()); + assertEquals(1, TimeUtil.remainder(d1, d2).getSeconds()); + } +} diff --git a/java/util/src/test/java/com/google/protobuf/util/json_test.proto b/java/util/src/test/java/com/google/protobuf/util/json_test.proto new file mode 100644 index 0000000000..b2753af6bf --- /dev/null +++ b/java/util/src/test/java/com/google/protobuf/util/json_test.proto @@ -0,0 +1,158 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package json_test; + +option java_package = "com.google.protobuf.util"; +option java_outer_classname = "JsonTestProto"; + +import "google/protobuf/any.proto"; +import "google/protobuf/wrappers.proto"; +import "google/protobuf/timestamp.proto"; +import "google/protobuf/duration.proto"; +import "google/protobuf/field_mask.proto"; +import "google/protobuf/struct.proto"; + +message TestAllTypes { + enum NestedEnum { + FOO = 0; + BAR = 1; + BAZ = 2; + } + message NestedMessage { + int32 value = 1; + } + + int32 optional_int32 = 1; + int64 optional_int64 = 2; + uint32 optional_uint32 = 3; + uint64 optional_uint64 = 4; + sint32 optional_sint32 = 5; + sint64 optional_sint64 = 6; + fixed32 optional_fixed32 = 7; + fixed64 optional_fixed64 = 8; + sfixed32 optional_sfixed32 = 9; + sfixed64 optional_sfixed64 = 10; + float optional_float = 11; + double optional_double = 12; + bool optional_bool = 13; + string optional_string = 14; + bytes optional_bytes = 15; + NestedMessage optional_nested_message = 18; + NestedEnum optional_nested_enum = 21; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + repeated NestedMessage repeated_nested_message = 48; + repeated NestedEnum repeated_nested_enum = 51; +} + +message TestMap { + // Instead of testing all combinations (too many), we only make sure all + // valid types have been used at least in one field as key and in one + // field as value. + map int32_to_int32_map = 1; + map int64_to_int32_map = 2; + map uint32_to_int32_map = 3; + map uint64_to_int32_map = 4; + map sint32_to_int32_map = 5; + map sint64_to_int32_map = 6; + map fixed32_to_int32_map = 7; + map fixed64_to_int32_map = 8; + map sfixed32_to_int32_map = 9; + map sfixed64_to_int32_map = 10; + map bool_to_int32_map = 11; + map string_to_int32_map = 12; + + map int32_to_int64_map = 101; + map int32_to_uint32_map = 102; + map int32_to_uint64_map = 103; + map int32_to_sint32_map = 104; + map int32_to_sint64_map = 105; + map int32_to_fixed32_map = 106; + map int32_to_fixed64_map = 107; + map int32_to_sfixed32_map = 108; + map int32_to_sfixed64_map = 109; + map int32_to_float_map = 110; + map int32_to_double_map = 111; + map int32_to_bool_map = 112; + map int32_to_string_map = 113; + map int32_to_bytes_map = 114; + map int32_to_message_map = 115; + map int32_to_enum_map = 116; +} + +message TestWrappers { + google.protobuf.Int32Value int32_value = 1; + google.protobuf.UInt32Value uint32_value = 2; + google.protobuf.Int64Value int64_value = 3; + google.protobuf.UInt64Value uint64_value = 4; + google.protobuf.FloatValue float_value = 5; + google.protobuf.DoubleValue double_value = 6; + google.protobuf.BoolValue bool_value = 7; + google.protobuf.StringValue string_value = 8; + google.protobuf.BytesValue bytes_value = 9; +} + +message TestTimestamp { + google.protobuf.Timestamp timestamp_value = 1; +} + +message TestDuration { + google.protobuf.Duration duration_value = 1; +} + +message TestFieldMask { + google.protobuf.FieldMask field_mask_value = 1; +} + +message TestStruct { + google.protobuf.Struct struct_value = 1; +} + +message TestAny { + google.protobuf.Any any_value = 1; +} diff --git a/python/google/protobuf/internal/containers.py b/python/google/protobuf/internal/containers.py index 72c2fa01bc..9c8275ebb4 100755 --- a/python/google/protobuf/internal/containers.py +++ b/python/google/protobuf/internal/containers.py @@ -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): diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py index 5c07cbe6b1..c30f633d38 100755 --- a/python/google/protobuf/internal/generator_test.py +++ b/python/google/protobuf/internal/generator_test.py @@ -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): diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py index 320ff0d27b..62abf1be63 100755 --- a/python/google/protobuf/internal/message_test.py +++ b/python/google/protobuf/internal/message_test.py @@ -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() diff --git a/python/google/protobuf/internal/packed_field_test.proto b/python/google/protobuf/internal/packed_field_test.proto index e69de29bb2..0dfdc10a87 100644 --- a/python/google/protobuf/internal/packed_field_test.proto +++ b/python/google/protobuf/internal/packed_field_test.proto @@ -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]; +} diff --git a/python/google/protobuf/internal/python_message.py b/python/google/protobuf/internal/python_message.py index ca9f76753b..a3e98467fa 100755 --- a/python/google/protobuf/internal/python_message.py +++ b/python/google/protobuf/internal/python_message.py @@ -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 - - -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) - - # Attach stuff to each FieldDescriptor for quick lookup later on. - for field in descriptor.fields: - _AttachFieldHelpers(cls, field) +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] + _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. - 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__())) + 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 diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py index 4eca4989ea..ef1ced4ea4 100755 --- a/python/google/protobuf/internal/reflection_test.py +++ b/python/google/protobuf/internal/reflection_test.py @@ -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) diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py index fec6538204..ac88fa81d8 100755 --- a/python/google/protobuf/internal/test_util.py +++ b/python/google/protobuf/internal/test_util.py @@ -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') diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index 06bd1ee558..00e67654d9 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -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): diff --git a/python/google/protobuf/internal/unknown_fields_test.py b/python/google/protobuf/internal/unknown_fields_test.py index 1b81ae79bf..0dda805bca 100755 --- a/python/google/protobuf/internal/unknown_fields_test.py +++ b/python/google/protobuf/internal/unknown_fields_test.py @@ -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) diff --git a/python/google/protobuf/pyext/cpp_message.py b/python/google/protobuf/pyext/cpp_message.py index 037bb72c7d..b215211ee5 100644 --- a/python/google/protobuf/pyext/cpp_message.py +++ b/python/google/protobuf/pyext/cpp_message.py @@ -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' diff --git a/python/google/protobuf/pyext/descriptor.cc b/python/google/protobuf/pyext/descriptor.cc index 2160757b3b..3806643f11 100644 --- a/python/google/protobuf/pyext/descriptor.cc +++ b/python/google/protobuf/pyext/descriptor.cc @@ -62,6 +62,14 @@ namespace google { namespace protobuf { namespace python { +// Store interned descriptors, so that the same C++ descriptor yields the same +// Python object. Objects are not immortal: this map does not own the +// references, and items are deleted when the last reference to the object is +// released. +// This is enough to support the "is" operator on live objects. +// All descriptors are stored here. +hash_map interned_descriptors; + PyObject* PyString_FromCppString(const string& str) { return PyString_FromStringAndSize(str.c_str(), str.size()); } @@ -147,6 +155,24 @@ static int CheckCalledFromGeneratedFile(const char* attr_name) { // Helper functions for descriptor objects. +// A set of templates to retrieve the C++ FileDescriptor of any descriptor. +template +const FileDescriptor* GetFileDescriptor(const DescriptorClass* descriptor) { + return descriptor->file(); +} +template<> +const FileDescriptor* GetFileDescriptor(const FileDescriptor* descriptor) { + return descriptor; +} +template<> +const FileDescriptor* GetFileDescriptor(const EnumValueDescriptor* descriptor) { + return descriptor->type()->file(); +} +template<> +const FileDescriptor* GetFileDescriptor(const OneofDescriptor* descriptor) { + return descriptor->containing_type()->file(); +} + // Converts options into a Python protobuf, and cache the result. // // This is a bit tricky because options can contain extension fields defined in @@ -156,8 +182,13 @@ static int CheckCalledFromGeneratedFile(const char* attr_name) { // Always returns a new reference. template static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { + // Options (and their extensions) are completely resolved in the proto file + // containing the descriptor. + PyDescriptorPool* pool = GetDescriptorPool_FromPool( + GetFileDescriptor(descriptor)->pool()); + hash_map* descriptor_options = - GetDescriptorPool()->descriptor_options; + pool->descriptor_options; // First search in the cache. if (descriptor_options->find(descriptor) != descriptor_options->end()) { PyObject *value = (*descriptor_options)[descriptor]; @@ -170,7 +201,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { const Message& options(descriptor->options()); const Descriptor *message_type = options.GetDescriptor(); PyObject* message_class(cdescriptor_pool::GetMessageClass( - GetDescriptorPool(), message_type)); + pool, message_type)); if (message_class == NULL) { PyErr_Format(PyExc_TypeError, "Could not retrieve class for Options: %s", message_type->full_name().c_str()); @@ -192,8 +223,8 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { options.SerializeToString(&serialized); io::CodedInputStream input( reinterpret_cast(serialized.c_str()), serialized.size()); - input.SetExtensionRegistry(GetDescriptorPool()->pool, - cmessage::GetMessageFactory()); + input.SetExtensionRegistry(pool->pool, + GetDescriptorPool()->message_factory); bool success = cmsg->message->MergePartialFromCodedStream(&input); if (!success) { PyErr_Format(PyExc_ValueError, "Error parsing Options message"); @@ -203,7 +234,7 @@ static PyObject* GetOrBuildOptions(const DescriptorClass *descriptor) { // Cache the result. Py_INCREF(value); - (*GetDescriptorPool()->descriptor_options)[descriptor] = value.get(); + (*pool->descriptor_options)[descriptor] = value.get(); return value.release(); } @@ -237,6 +268,9 @@ typedef struct PyBaseDescriptor { // Pointer to the C++ proto2 descriptor. // Like all descriptors, it is owned by the global DescriptorPool. const void* descriptor; + + // Owned reference to the DescriptorPool, to ensure it is kept alive. + PyDescriptorPool* pool; } PyBaseDescriptor; @@ -258,7 +292,9 @@ namespace descriptor { // 'was_created' is an optional pointer to a bool, and is set to true if a new // object was allocated. // Always return a new reference. -PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, +template +PyObject* NewInternedDescriptor(PyTypeObject* type, + const DescriptorClass* descriptor, bool* was_created) { if (was_created) { *was_created = false; @@ -270,8 +306,8 @@ PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, // See if the object is in the map of interned descriptors hash_map::iterator it = - GetDescriptorPool()->interned_descriptors->find(descriptor); - if (it != GetDescriptorPool()->interned_descriptors->end()) { + interned_descriptors.find(descriptor); + if (it != interned_descriptors.end()) { GOOGLE_DCHECK(Py_TYPE(it->second) == type); Py_INCREF(it->second); return it->second; @@ -283,10 +319,21 @@ PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, return NULL; } py_descriptor->descriptor = descriptor; + // and cache it. - GetDescriptorPool()->interned_descriptors->insert( + interned_descriptors.insert( std::make_pair(descriptor, reinterpret_cast(py_descriptor))); + // Ensures that the DescriptorPool stays alive. + PyDescriptorPool* pool = GetDescriptorPool_FromPool( + GetFileDescriptor(descriptor)->pool()); + if (pool == NULL) { + Py_DECREF(py_descriptor); + return NULL; + } + Py_INCREF(pool); + py_descriptor->pool = pool; + if (was_created) { *was_created = true; } @@ -295,7 +342,8 @@ PyObject* NewInternedDescriptor(PyTypeObject* type, const void* descriptor, static void Dealloc(PyBaseDescriptor* self) { // Remove from interned dictionary - GetDescriptorPool()->interned_descriptors->erase(self->descriptor); + interned_descriptors.erase(self->descriptor); + Py_CLEAR(self->pool); Py_TYPE(self)->tp_free(reinterpret_cast(self)); } diff --git a/python/google/protobuf/pyext/descriptor_pool.cc b/python/google/protobuf/pyext/descriptor_pool.cc index ecd9084720..7aed651d3f 100644 --- a/python/google/protobuf/pyext/descriptor_pool.cc +++ b/python/google/protobuf/pyext/descriptor_pool.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -53,9 +54,13 @@ namespace google { namespace protobuf { namespace python { +// A map to cache Python Pools per C++ pointer. +// Pointers are not owned here, and belong to the PyDescriptorPool. +static hash_map descriptor_pool_map; + namespace cdescriptor_pool { -PyDescriptorPool* NewDescriptorPool() { +static PyDescriptorPool* NewDescriptorPool() { PyDescriptorPool* cdescriptor_pool = PyObject_New( PyDescriptorPool, &PyDescriptorPool_Type); if (cdescriptor_pool == NULL) { @@ -67,32 +72,43 @@ 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 = new PyDescriptorPool::ClassesByMessageMap(); - cdescriptor_pool->interned_descriptors = - new hash_map(); cdescriptor_pool->descriptor_options = new hash_map(); + if (!descriptor_pool_map.insert( + std::make_pair(cdescriptor_pool->pool, cdescriptor_pool)).second) { + // Should never happen -- would indicate an internal error / bug. + PyErr_SetString(PyExc_ValueError, "DescriptorPool already registered"); + return NULL; + } + return cdescriptor_pool; } static void Dealloc(PyDescriptorPool* self) { typedef PyDescriptorPool::ClassesByMessageMap::iterator iterator; + descriptor_pool_map.erase(self->pool); for (iterator it = self->classes_by_descriptor->begin(); it != self->classes_by_descriptor->end(); ++it) { Py_DECREF(it->second); } delete self->classes_by_descriptor; - delete self->interned_descriptors; // its references were borrowed. for (hash_map::iterator it = self->descriptor_options->begin(); it != self->descriptor_options->end(); ++it) { Py_DECREF(it->second); } delete self->descriptor_options; + delete self->message_factory; Py_TYPE(self)->tp_free(reinterpret_cast(self)); } @@ -384,22 +400,43 @@ PyTypeObject PyDescriptorPool_Type = { PyObject_Del, // tp_free }; -static PyDescriptorPool* global_cdescriptor_pool = NULL; +// This is the DescriptorPool which contains all the definitions from the +// generated _pb2.py modules. +static PyDescriptorPool* python_generated_pool = NULL; bool InitDescriptorPool() { if (PyType_Ready(&PyDescriptorPool_Type) < 0) return false; - global_cdescriptor_pool = cdescriptor_pool::NewDescriptorPool(); - if (global_cdescriptor_pool == NULL) { + python_generated_pool = cdescriptor_pool::NewDescriptorPool(); + if (python_generated_pool == NULL) { return false; } + // Register this pool to be found for C++-generated descriptors. + descriptor_pool_map.insert( + std::make_pair(DescriptorPool::generated_pool(), + python_generated_pool)); return true; } PyDescriptorPool* GetDescriptorPool() { - return global_cdescriptor_pool; + return python_generated_pool; +} + +PyDescriptorPool* GetDescriptorPool_FromPool(const DescriptorPool* pool) { + // Fast path for standard descriptors. + if (pool == python_generated_pool->pool || + pool == DescriptorPool::generated_pool()) { + return python_generated_pool; + } + hash_map::iterator it = + descriptor_pool_map.find(pool); + if (it != descriptor_pool_map.end()) { + PyErr_SetString(PyExc_KeyError, "Unknown descriptor pool"); + return NULL; + } + return it->second; } } // namespace python diff --git a/python/google/protobuf/pyext/descriptor_pool.h b/python/google/protobuf/pyext/descriptor_pool.h index efb1abeb0e..541d920bc8 100644 --- a/python/google/protobuf/pyext/descriptor_pool.h +++ b/python/google/protobuf/pyext/descriptor_pool.h @@ -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. @@ -62,14 +72,6 @@ typedef struct PyDescriptorPool { typedef hash_map ClassesByMessageMap; ClassesByMessageMap* classes_by_descriptor; - // Store interned descriptors, so that the same C++ descriptor yields the same - // Python object. Objects are not immortal: this map does not own the - // references, and items are deleted when the last reference to the object is - // released. - // This is enough to support the "is" operator on live objects. - // All descriptors are stored here. - hash_map* interned_descriptors; - // Cache the options for any kind of descriptor. // Descriptor pointers are owned by the DescriptorPool above. // Python objects are owned by the map. @@ -81,9 +83,6 @@ extern PyTypeObject PyDescriptorPool_Type; namespace cdescriptor_pool { -// Builds a new DescriptorPool. Normally called only once per process. -PyDescriptorPool* NewDescriptorPool(); - // Looks up a message by name. // Returns a message Descriptor, or NULL if not found. const Descriptor* FindMessageTypeByName(PyDescriptorPool* self, @@ -140,6 +139,10 @@ PyObject* FindOneofByName(PyDescriptorPool* self, PyObject* arg); // Returns a *borrowed* reference. PyDescriptorPool* GetDescriptorPool(); +// Retrieve the python descriptor pool owning a C++ descriptor pool. +// Returns a *borrowed* reference. +PyDescriptorPool* GetDescriptorPool_FromPool(const DescriptorPool* pool); + // Initialize objects used by this module. bool InitDescriptorPool(); diff --git a/python/google/protobuf/pyext/extension_dict.cc b/python/google/protobuf/pyext/extension_dict.cc index b8d18f8d92..8ebbb27c46 100644 --- a/python/google/protobuf/pyext/extension_dict.cc +++ b/python/google/protobuf/pyext/extension_dict.cc @@ -33,6 +33,7 @@ #include +#include #include #include #include @@ -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 diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index a4843e8d3d..aa3ab97a67 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -49,9 +49,10 @@ #endif #include #include +#include #include +#include #include -#include #include #include #include @@ -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_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_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. = EnumTypeWrapper(). + // + // The enum descriptor we get from + // .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. = + 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 descriptors come from + // .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._FIELD_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(result.get()); + + // Insert the empty weakref into the base classes. + if (InsertEmptyWeakref( + reinterpret_cast(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(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(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(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(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& 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(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(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(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(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(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(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_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_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. = EnumTypeWrapper(). - // - // The enum descriptor we get from - // .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. = - 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 descriptors come from - // .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._FIELD_NUMBER = . - if (!AddFieldNumberToClass(cls, field)) { - return NULL; - } - } - - Py_RETURN_NONE; -} - PyObject* DeepCopy(CMessage* self, PyObject* arg) { PyObject* clone = PyObject_CallObject( reinterpret_cast(Py_TYPE(self)), NULL); @@ -2214,8 +2376,9 @@ PyObject* DeepCopy(CMessage* self, PyObject* arg) { Py_DECREF(clone); return NULL; } - if (MergeFrom(reinterpret_cast(clone), - reinterpret_cast(self)) == NULL) { + if (ScopedPyObjectPtr(MergeFrom( + reinterpret_cast(clone), + reinterpret_cast(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(&PyMessageMeta_Type)); + if (PyType_Ready(&CMessage_Type) < 0) { return false; } @@ -2628,86 +2799,106 @@ bool InitProto2MessageModule(PyObject *m) { PyModule_AddObject(m, "Message", reinterpret_cast(&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( - &RepeatedScalarContainer_Type)); + PyModule_AddObject(m, "RepeatedScalarContainer", + reinterpret_cast( + &RepeatedScalarContainer_Type)); - RepeatedCompositeContainer_Type.tp_hash = PyObject_HashNotImplemented; - if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) { - return false; - } + if (PyType_Ready(&RepeatedCompositeContainer_Type) < 0) { + return false; + } - PyModule_AddObject( - m, "RepeatedCompositeContainer", - reinterpret_cast( - &RepeatedCompositeContainer_Type)); - - // ScalarMapContainer_Type derives from our MutableMapping type. - PyObject* containers = - PyImport_ImportModule("google.protobuf.internal.containers"); - if (containers == NULL) { - return false; + PyModule_AddObject( + m, "RepeatedCompositeContainer", + reinterpret_cast( + &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; + } } - PyObject* mutable_mapping = - PyObject_GetAttrString(containers, "MutableMapping"); - Py_DECREF(containers); + // Initialize Map container types. + { + // ScalarMapContainer_Type derives from our MutableMapping type. + ScopedPyObjectPtr containers(PyImport_ImportModule( + "google.protobuf.internal.containers")); + if (containers == NULL) { + return false; + } - if (mutable_mapping == NULL) { - return false; - } + ScopedPyObjectPtr mutable_mapping( + PyObject_GetAttrString(containers, "MutableMapping")); + if (mutable_mapping == NULL) { + return false; + } - if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) { - Py_DECREF(mutable_mapping); - return false; - } + if (!PyObject_TypeCheck(mutable_mapping, &PyType_Type)) { + return false; + } - ScalarMapContainer_Type.tp_base = - reinterpret_cast(mutable_mapping); + Py_INCREF(mutable_mapping); + ScalarMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping.get()); - if (PyType_Ready(&ScalarMapContainer_Type) < 0) { - return false; - } + if (PyType_Ready(&ScalarMapContainer_Type) < 0) { + return false; + } - PyModule_AddObject(m, "ScalarMapContainer", - reinterpret_cast(&ScalarMapContainer_Type)); + PyModule_AddObject(m, "ScalarMapContainer", + reinterpret_cast(&ScalarMapContainer_Type)); - if (PyType_Ready(&ScalarMapIterator_Type) < 0) { - return false; - } + if (PyType_Ready(&ScalarMapIterator_Type) < 0) { + return false; + } - PyModule_AddObject(m, "ScalarMapIterator", - reinterpret_cast(&ScalarMapIterator_Type)); + PyModule_AddObject(m, "ScalarMapIterator", + reinterpret_cast(&ScalarMapIterator_Type)); - Py_INCREF(mutable_mapping); - MessageMapContainer_Type.tp_base = - reinterpret_cast(mutable_mapping); + Py_INCREF(mutable_mapping); + MessageMapContainer_Type.tp_base = + reinterpret_cast(mutable_mapping.get()); - if (PyType_Ready(&MessageMapContainer_Type) < 0) { - return false; - } + if (PyType_Ready(&MessageMapContainer_Type) < 0) { + return false; + } - PyModule_AddObject(m, "MessageMapContainer", - reinterpret_cast(&MessageMapContainer_Type)); + PyModule_AddObject(m, "MessageMapContainer", + reinterpret_cast(&MessageMapContainer_Type)); - if (PyType_Ready(&MessageMapIterator_Type) < 0) { - return false; - } + if (PyType_Ready(&MessageMapIterator_Type) < 0) { + return false; + } - PyModule_AddObject(m, "MessageMapIterator", - reinterpret_cast(&MessageMapIterator_Type)); + PyModule_AddObject(m, "MessageMapIterator", + reinterpret_cast(&MessageMapIterator_Type)); + } - ExtensionDict_Type.tp_hash = PyObject_HashNotImplemented; if (PyType_Ready(&ExtensionDict_Type) < 0) { return false; } - PyModule_AddObject( m, "ExtensionDict", reinterpret_cast(&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"); diff --git a/python/google/protobuf/pyext/message.h b/python/google/protobuf/pyext/message.h index 7360b20774..f147d433ef 100644 --- a/python/google/protobuf/pyext/message.h +++ b/python/google/protobuf/pyext/message.h @@ -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& new_owner); int AssureWritable(CMessage* self); - -DynamicMessageFactory* GetMessageFactory(); - } // namespace cmessage diff --git a/python/google/protobuf/pyext/message_map_container.cc b/python/google/protobuf/pyext/message_map_container.cc index ab8d8fb9f0..a4a7fbfe0e 100644 --- a/python/google/protobuf/pyext/message_map_container.cc +++ b/python/google/protobuf/pyext/message_map_container.cc @@ -32,6 +32,7 @@ #include +#include #include #include #include diff --git a/python/google/protobuf/pyext/repeated_composite_container.cc b/python/google/protobuf/pyext/repeated_composite_container.cc index 86b75d0f7c..fe2e600b8f 100644 --- a/python/google/protobuf/pyext/repeated_composite_container.cc +++ b/python/google/protobuf/pyext/repeated_composite_container.cc @@ -38,11 +38,13 @@ #include #endif +#include #include #include #include #include #include +#include #include #include @@ -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]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(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; - // Reverse the Message array. - for (int i = 0; i < length / 2; ++i) - reflection->SwapElements(message, descriptor, i, length - i - 1); + // 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 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( + 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 diff --git a/python/google/protobuf/pyext/repeated_scalar_container.cc b/python/google/protobuf/pyext/repeated_scalar_container.cc index fd19683604..7565c6fd90 100644 --- a/python/google/protobuf/pyext/repeated_scalar_container.cc +++ b/python/google/protobuf/pyext/repeated_scalar_container.cc @@ -39,10 +39,12 @@ #endif #include +#include #include #include #include #include +#include #include #include @@ -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 diff --git a/python/google/protobuf/pyext/scalar_map_container.cc b/python/google/protobuf/pyext/scalar_map_container.cc index 6f731d2715..80d294253e 100644 --- a/python/google/protobuf/pyext/scalar_map_container.cc +++ b/python/google/protobuf/pyext/scalar_map_container.cc @@ -32,6 +32,7 @@ #include +#include #include #include #include diff --git a/python/google/protobuf/pyext/scoped_pyobject_ptr.h b/python/google/protobuf/pyext/scoped_pyobject_ptr.h index 18ddd5cd4d..9979b83b49 100644 --- a/python/google/protobuf/pyext/scoped_pyobject_ptr.h +++ b/python/google/protobuf/pyext/scoped_pyobject_ptr.h @@ -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; diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py index 82fca66198..0c757264f4 100755 --- a/python/google/protobuf/reflection.py +++ b/python/google/protobuf/reflection.py @@ -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): diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index 8cbd68222e..82133765f9 100755 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -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. diff --git a/src/Makefile.am b/src/Makefile.am index 4c8919d705..caae2933e4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -191,6 +191,7 @@ libprotobuf_lite_la_SOURCES = \ google/protobuf/stubs/stringpiece.h \ google/protobuf/stubs/stringprintf.cc \ google/protobuf/stubs/stringprintf.h \ + google/protobuf/stubs/structurally_valid.cc \ google/protobuf/stubs/strutil.cc \ google/protobuf/stubs/strutil.h \ google/protobuf/stubs/time.cc \ @@ -232,7 +233,6 @@ libprotobuf_la_SOURCES = \ google/protobuf/service.cc \ google/protobuf/source_context.pb.cc \ google/protobuf/struct.pb.cc \ - google/protobuf/stubs/structurally_valid.cc \ google/protobuf/stubs/substitute.cc \ google/protobuf/stubs/substitute.h \ google/protobuf/text_format.cc \ @@ -659,7 +659,8 @@ COMMON_TEST_SOURCES = \ google/protobuf/testing/file.h check_PROGRAMS = protoc protobuf-test protobuf-lazy-descriptor-test \ - protobuf-lite-test test_plugin $(GZCHECKPROGRAMS) + protobuf-lite-test test_plugin protobuf-lite-arena-test \ + $(GZCHECKPROGRAMS) protobuf_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la \ ../gmock/gtest/lib/libgtest.la \ ../gmock/lib/libgmock.la \ @@ -756,21 +757,40 @@ protobuf_lazy_descriptor_test_SOURCES = \ $(COMMON_TEST_SOURCES) nodist_protobuf_lazy_descriptor_test_SOURCES = $(protoc_outputs) -# Build lite_unittest separately, since it doesn't use gtest. -protobuf_lite_test_LDADD = $(PTHREAD_LIBS) libprotobuf-lite.la -protobuf_lite_test_CXXFLAGS = $(NO_OPT_CXXFLAGS) -protobuf_lite_test_SOURCES = \ +COMMON_LITE_TEST_SOURCES = \ google/protobuf/arena_test_util.cc \ google/protobuf/arena_test_util.h \ - google/protobuf/lite_unittest.cc \ google/protobuf/map_lite_test_util.cc \ google/protobuf/map_lite_test_util.h \ google/protobuf/test_util_lite.cc \ google/protobuf/test_util_lite.h - # TODO(teboring) add the file back and make the test build. - # google/protobuf/map_lite_test.cc + +# Build lite_unittest separately, since it doesn't use gtest. It can't +# depend on gtest because our internal version of gtest depend on proto +# full runtime and we want to make sure this test builds without full +# runtime. +protobuf_lite_test_LDADD = $(PTHREAD_LIBS) libprotobuf-lite.la +protobuf_lite_test_CXXFLAGS = $(NO_OPT_CXXFLAGS) +protobuf_lite_test_SOURCES = \ + google/protobuf/lite_unittest.cc \ + $(COMMON_LITE_TEST_SOURCES) nodist_protobuf_lite_test_SOURCES = $(protoc_lite_outputs) +# lite_arena_unittest depends on gtest because teboring@ found that without +# gtest when building the test internally our memory sanitizer doesn't detect +# memory leaks (don't know why). +protobuf_lite_arena_test_LDADD = $(PTHREAD_LIBS) libprotobuf-lite.la \ + ../gmock/gtest/lib/libgtest.la \ + ../gmock/lib/libgmock.la \ + ../gmock/lib/libgmock_main.la +protobuf_lite_arena_test_CPPFLAGS = -I$(srcdir)/../gmock/include \ + -I$(srcdir)/../gmock/gtest/include +protobuf_lite_arena_test_CXXFLAGS = $(NO_OPT_CXXFLAGS) +protobuf_lite_arena_test_SOURCES = \ + google/protobuf/lite_arena_unittest.cc \ + $(COMMON_LITE_TEST_SOURCES) +nodist_protobuf_lite_arena_test_SOURCES = $(protoc_lite_outputs) + # Test plugin binary. test_plugin_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la \ ../gmock/gtest/lib/libgtest.la @@ -790,4 +810,5 @@ zcgunzip_SOURCES = google/protobuf/testing/zcgunzip.cc endif TESTS = protobuf-test protobuf-lazy-descriptor-test protobuf-lite-test \ - google/protobuf/compiler/zip_output_unittest.sh $(GZTESTS) + google/protobuf/compiler/zip_output_unittest.sh $(GZTESTS) \ + protobuf-lite-arena-test diff --git a/src/google/protobuf/any.cc b/src/google/protobuf/any.cc index c66fdfadec..c6ed37ae38 100644 --- a/src/google/protobuf/any.cc +++ b/src/google/protobuf/any.cc @@ -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; } diff --git a/src/google/protobuf/any.h b/src/google/protobuf/any.h index f681eceb69..7eeb6b707a 100644 --- a/src/google/protobuf/any.h +++ b/src/google/protobuf/any.h @@ -34,9 +34,9 @@ #include #include -#include #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/arena.cc b/src/google/protobuf/arena.cc index ed1c5ef2e0..907a6a20c5 100755 --- a/src/google/protobuf/arena.cc +++ b/src/google/protobuf/arena.cc @@ -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(google::protobuf::internal::NoBarrier_Load(&blocks_)); google::protobuf::internal::Release_Store(&blocks_, reinterpret_cast(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(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; } diff --git a/src/google/protobuf/arena.h b/src/google/protobuf/arena.h index 51149bae52..f06be4a3d7 100644 --- a/src/google/protobuf/arena.h +++ b/src/google/protobuf/arena.h @@ -31,6 +31,7 @@ #ifndef GOOGLE_PROTOBUF_ARENA_H__ #define GOOGLE_PROTOBUF_ARENA_H__ +#include #if __cplusplus >= 201103L #include #endif @@ -39,7 +40,8 @@ #include #include #include -#include +#include +#include #include namespace google { @@ -414,6 +416,9 @@ class LIBPROTOBUF_EXPORT Arena { // trivially destructible. template GOOGLE_ATTRIBUTE_ALWAYS_INLINE static T* CreateArray(::google::protobuf::Arena* arena, size_t num_elements) { + GOOGLE_CHECK_LE(num_elements, + std::numeric_limits::max() / sizeof(T)) + << "Requested size is too large to fit into size_t."; if (arena == NULL) { return static_cast(::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 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(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::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::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::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 + struct is_destructor_skippable { + template + static char DestructorSkippable( + const typename U::DestructorSkippable_*); + template + 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(static_cast(0))) == + sizeof(char) || google::protobuf::internal::has_trivial_destructor::value == true> + type; + static const type value; + }; + + // CreateMessage 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 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::max() / sizeof(T)) + << "Requested size is too large to fit into size_t."; return static_cast( AllocateAligned(RTTI_TYPE_ID(T), sizeof(T) * num_elements)); } template 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); @@ -590,8 +624,7 @@ class LIBPROTOBUF_EXPORT Arena { } template 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); @@ -600,7 +633,7 @@ class LIBPROTOBUF_EXPORT Arena { } template 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 - 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 - 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 - 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 - 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 - 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 - 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 GOOGLE_ATTRIBUTE_ALWAYS_INLINE - inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*) { + T* CreateMessageInternal(typename T::InternalArenaConstructable_*) { return CreateInternal(SkipDeleteList(static_cast(0)), this); } template GOOGLE_ATTRIBUTE_ALWAYS_INLINE - inline T* CreateMessageInternal(typename T::InternalArenaConstructable_*, - const Arg& arg) { + T* CreateMessageInternal(typename T::InternalArenaConstructable_*, + const Arg& arg) { return CreateInternal(SkipDeleteList(static_cast(0)), this, arg); } template 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(SkipDeleteList(static_cast(0)), this, arg1, arg2); } @@ -734,19 +767,29 @@ class LIBPROTOBUF_EXPORT Arena { template static void CreateInArenaStorage(T* ptr, Arena* arena) { CreateInArenaStorageInternal(ptr, arena, is_arena_constructable::value); + RegisterDestructorInternal(ptr, arena, is_destructor_skippable::value); } + template static void CreateInArenaStorageInternal( T* ptr, Arena* arena, google::protobuf::internal::true_type) { new (ptr) T(arena); } - template static void CreateInArenaStorageInternal( T* ptr, Arena* arena, google::protobuf::internal::false_type) { new (ptr) T; } + template + static void RegisterDestructorInternal( + T* ptr, Arena* arena, google::protobuf::internal::true_type) {} + template + 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 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 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::type Arena::is_arena_constructable::value = typename Arena::is_arena_constructable::type(); +template +const typename Arena::is_destructor_skippable::type + Arena::is_destructor_skippable::value = + typename Arena::is_destructor_skippable::type(); + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/arena_test_util.cc b/src/google/protobuf/arena_test_util.cc index 21f55c6e47..df9c5bd6b4 100644 --- a/src/google/protobuf/arena_test_util.cc +++ b/src/google/protobuf/arena_test_util.cc @@ -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 #include #include diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc index 6c185695ca..c9ca1fd18d 100644 --- a/src/google/protobuf/arena_unittest.cc +++ b/src/google/protobuf/arena_unittest.cc @@ -40,7 +40,9 @@ #include #include +#include #include +#include #include #include #include @@ -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(&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(&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(&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(&arena); - if (::google::protobuf::internal::has_trivial_destructor::value) { + if (google::protobuf::internal::has_trivial_destructor::value) { EXPECT_EQ(1, ArenaHooksTestUtil::num_allocations); } else { EXPECT_EQ(2, ArenaHooksTestUtil::num_allocations); diff --git a/src/google/protobuf/arenastring.h b/src/google/protobuf/arenastring.h index d829ed9191..1dacdc68c0 100755 --- a/src/google/protobuf/arenastring.h +++ b/src/google/protobuf/arenastring.h @@ -33,6 +33,7 @@ #include +#include #include #include @@ -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 { diff --git a/src/google/protobuf/arenastring_unittest.cc b/src/google/protobuf/arenastring_unittest.cc index 8ebd4b9cca..3fb582be1d 100644 --- a/src/google/protobuf/arenastring_unittest.cc +++ b/src/google/protobuf/arenastring_unittest.cc @@ -42,6 +42,7 @@ #endif #include +#include #include #include diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc index 0039b00a78..473eb4e6fb 100644 --- a/src/google/protobuf/compiler/code_generator.cc +++ b/src/google/protobuf/compiler/code_generator.cc @@ -34,6 +34,7 @@ #include +#include #include #include diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index e88718615a..26a4f0b066 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -1657,7 +1658,11 @@ bool CommandLineInterface::WriteDescriptorSet( &already_seen, file_set.mutable_file()); } } else { + set 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_) { diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc index e5b77c3363..46ea5c4e42 100644 --- a/src/google/protobuf/compiler/command_line_interface_unittest.cc +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -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" diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc index 13ed0b64b4..c3e9fe7448 100644 --- a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -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"; diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 70d3a60061..de4d7cc73b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -32,7 +32,6 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. -#include #include #include @@ -70,14 +69,11 @@ EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, EnumGenerator::~EnumGenerator() {} -void EnumGenerator::GenerateForwardDeclaration(io::Printer* printer) { +void EnumGenerator::FillForwardDeclaration(set* enum_names) { if (!options_.proto_h) { return; } - map 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) { diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 3e930856bd..f3aa72e440 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -35,6 +35,7 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ +#include #include #include #include @@ -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* 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 diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 965327b188..824e2205e4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -35,7 +35,6 @@ #include #include #include -#include #include namespace google { diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 43df1d8892..8d47d4e086 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -47,6 +47,7 @@ #include #include #include +#include #include #include diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 1f0a8205eb..5dae4cdd6c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -33,6 +33,7 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include +#include #include #ifndef _SHARED_PTR_H #include @@ -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 \n" // for swap() "\n" @@ -165,7 +255,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "#include \n" "#include \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 \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::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& classes() { return classes_; } + set& enums() { return enums_; } + + void Print(io::Printer* printer) const { + for (set::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::const_iterator it = classes_.begin(), + end = classes_.end(); + it != end; ++it) { + printer->Print("class $classname$;\n", "classname", it->c_str()); + } + for (map::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 namespaces_; + set classes_; + set 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 classes; for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateMessageForwardDeclaration(printer); + message_generators_[i]->FillMessageForwardDeclarations(&classes); + } + for (set::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( diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index e68f67bb53..29cdaea53d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -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. diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 9941637200..781526b505 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -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 output( + generator_context->Open(basename + ".proto.h")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateProtoHeader(&printer); + } + + basename.append(".pb"); { google::protobuf::scoped_ptr output( generator_context->Open(basename + ".h")); io::Printer printer(output.get(), '$'); - file_generator.GenerateHeader(&printer); + file_generator.GeneratePBHeader(&printer); } // Generate cc file. diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 0f3688d030..09845458b9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -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(this)->"; +} + +string DependentBaseConstDownCast() { + return "reinterpret_cast(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; } @@ -578,6 +600,94 @@ bool IsAnyMessage(const Descriptor* descriptor) { descriptor->file()->name() == kAnyProtoFile; } +enum Utf8CheckMode { + STRICT = 0, // Parsing will fail if non UTF-8 data is in string fields. + VERIFY = 1, // Only log an error but parsing will succeed. + NONE = 2, // No UTF-8 check. +}; + +// Which level of UTF-8 enforcemant is placed on this file. +static Utf8CheckMode GetUtf8CheckMode(const FieldDescriptor* field) { + if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { + return STRICT; + } else if (field->file()->options().optimize_for() != + FileOptions::LITE_RUNTIME) { + return VERIFY; + } else { + return NONE; + } +} + +static void GenerateUtf8CheckCode(const FieldDescriptor* field, + bool for_parse, + const map& variables, + const char* parameters, + const char* strict_function, + const char* verify_function, + io::Printer* printer) { + switch (GetUtf8CheckMode(field)) { + case STRICT: { + if (for_parse) { + printer->Print("DO_("); + } + printer->Print( + "::google::protobuf::internal::WireFormatLite::$function$(\n", + "function", strict_function); + printer->Indent(); + printer->Print(variables, parameters); + if (for_parse) { + printer->Print("::google::protobuf::internal::WireFormatLite::PARSE,\n"); + } else { + printer->Print("::google::protobuf::internal::WireFormatLite::SERIALIZE,\n"); + } + printer->Print("\"$full_name$\")", "full_name", field->full_name()); + if (for_parse) { + printer->Print(")"); + } + printer->Print(";\n"); + printer->Outdent(); + break; + } + case VERIFY: { + printer->Print( + "::google::protobuf::internal::WireFormat::$function$(\n", + "function", verify_function); + printer->Indent(); + printer->Print(variables, parameters); + if (for_parse) { + printer->Print("::google::protobuf::internal::WireFormat::PARSE,\n"); + } else { + printer->Print("::google::protobuf::internal::WireFormat::SERIALIZE,\n"); + } + printer->Print("\"$full_name$\");\n", "full_name", field->full_name()); + printer->Outdent(); + break; + } + case NONE: + break; + } +} + +void GenerateUtf8CheckCodeForString(const FieldDescriptor* field, + bool for_parse, + const map& variables, + const char* parameters, + io::Printer* printer) { + GenerateUtf8CheckCode(field, for_parse, variables, parameters, + "VerifyUtf8String", "VerifyUTF8StringNamedField", + printer); +} + +void GenerateUtf8CheckCodeForCord(const FieldDescriptor* field, + bool for_parse, + const map& variables, + const char* parameters, + io::Printer* printer) { + GenerateUtf8CheckCode(field, for_parse, variables, parameters, + "VerifyUtf8Cord", "VerifyUTF8CordNamedField", + printer); +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 4bbf830345..985cb04cae 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -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 @@ -195,11 +202,6 @@ inline bool HasGenericServices(const FileDescriptor* file) { file->options().cc_generic_services(); } -// Should string fields in this file verify that their contents are UTF-8? -inline bool HasUtf8Verification(const FileDescriptor* file) { - return file->options().optimize_for() != FileOptions::LITE_RUNTIME; -} - // Should we generate a separate, super-optimized code path for serializing to // flat arrays? We don't do this in Lite mode because we'd rather reduce code // size. @@ -263,6 +265,20 @@ inline bool SupportsArenas(const FieldDescriptor* field) { bool IsAnyMessage(const FileDescriptor* descriptor); bool IsAnyMessage(const Descriptor* descriptor); +void GenerateUtf8CheckCodeForString( + const FieldDescriptor* field, + bool for_parse, + const map& variables, + const char* parameters, + io::Printer* printer); + +void GenerateUtf8CheckCodeForCord( + const FieldDescriptor* field, + bool for_parse, + const map& variables, + const char* parameters, + io::Printer* printer); + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index 0ff0d27c3a..25acc61bed 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -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 variables(variables_); + variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : ""; + printer->Print(variables, "$this_message$$name$_.Clear();\n"); } void MapFieldGenerator:: @@ -231,6 +234,20 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "}\n"); } + const FieldDescriptor* key_field = + descriptor_->message_type()->FindFieldByName("key"); + if (key_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + key_field, true, variables_, + "entry->key().data(), entry->key().length(),\n", printer); + } + if (value_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + value_field, true, variables_, + "entry->mutable_value()->data(),\n" + "entry->mutable_value()->length(),\n", printer); + } + // If entry is allocated by arena, its desctructor should be avoided. if (SupportsArenas(descriptor_)) { printer->Print(variables_, @@ -258,7 +275,30 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, " entry.reset($name$_.New$wrapper$(it->first, it->second));\n" " ::google::protobuf::internal::WireFormatLite::Write$stream_writer$(\n" - " $number$, *entry, output);\n" + " $number$, *entry, output);\n"); + + printer->Indent(); + printer->Indent(); + + const FieldDescriptor* key_field = + descriptor_->message_type()->FindFieldByName("key"); + const FieldDescriptor* value_field = + descriptor_->message_type()->FindFieldByName("value"); + if (key_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + key_field, false, variables_, + "it->first.data(), it->first.length(),\n", printer); + } + if (value_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + value_field, false, variables_, + "it->second.data(), it->second.length(),\n", printer); + } + + printer->Outdent(); + printer->Outdent(); + + printer->Print( " }\n"); // If entry is allocated by arena, its desctructor should be avoided. @@ -293,7 +333,29 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { " entry.reset($name$_.New$wrapper$(it->first, it->second));\n" " target = ::google::protobuf::internal::WireFormatLite::\n" " Write$declared_type$NoVirtualToArray(\n" - " $number$, *entry, target);\n" + " $number$, *entry, target);\n"); + + printer->Indent(); + printer->Indent(); + + const FieldDescriptor* key_field = + descriptor_->message_type()->FindFieldByName("key"); + const FieldDescriptor* value_field = + descriptor_->message_type()->FindFieldByName("value"); + if (key_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + key_field, false, variables_, + "it->first.data(), it->first.length(),\n", printer); + } + if (value_field->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + value_field, false, variables_, + "it->second.data(), it->second.length(),\n", printer); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( " }\n"); // If entry is allocated by arena, its desctructor should be avoided. diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.h b/src/google/protobuf/compiler/cpp/cpp_map_field.h index d27d485162..5e205623b8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.h @@ -63,6 +63,7 @@ class MapFieldGenerator : public FieldGenerator { private: const FieldDescriptor* descriptor_; + const bool dependent_field_; map variables_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MapFieldGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index b0e38755a2..aa10b0bb0d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -39,7 +39,6 @@ #ifndef _SHARED_PTR_H #include #endif -#include #include #include #include @@ -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* 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* 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\n"; vars["dependent_classname"] = DependentBaseClassTemplateName(descriptor_) + ""; - vars["this_message"] = "reinterpret_cast(this)->"; - vars["this_const_message"] = "reinterpret_cast(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 vars; SetCommonFieldVariables(field, &vars, options_); vars["inline"] = is_inline ? "inline " : ""; + if (use_dependent_base_ && IsFieldDependent(field)) { + vars["tmpl"] = "template\n"; + vars["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_) + ""; + vars["this_message"] = "reinterpret_cast(this)->"; + vars["this_const_message"] = "reinterpret_cast(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\n"; - oneof_vars["inline"] = "inline "; - oneof_vars["dependent_classname"] = - DependentBaseClassTemplateName(descriptor_) + ""; - oneof_vars["this_message"] = "reinterpret_cast(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 diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 23dad10c1d..8e19a3f055 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -39,8 +39,8 @@ #ifndef _SHARED_PTR_H #include #endif +#include #include -#include #include #include @@ -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* class_names); + void FillEnumForwardDeclarations(set* enum_names); // Generate definitions of all nested enums (must come before class // definitions because those classes use the enums definitions). diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index ba318d10bc..b454589203 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -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 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()) + ""; + 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 \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 \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 \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 \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 \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 \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 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 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,79 +531,149 @@ 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 variables(variables_); + variables["inline"] = "inline "; + variables["dependent_classname"] = + DependentBaseClassTemplateName(descriptor_->containing_type()) + ""; + variables["this_message"] = "reinterpret_cast(this)->"; + // Const message access is needed for the dependent getter. + variables["this_const_message"] = "reinterpret_cast(this)->"; + variables["tmpl"] = "template \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 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 variables(variables_); + variables["field_member"] = + variables["oneof_prefix"] + variables["name"] + "_"; + + //printer->Print(variables, +} + +void MessageOneofFieldGenerator:: +InternalGenerateInlineAccessorDefinitions(const map& 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" - "$inline$ $type$* $classname$::unsafe_arena_release_$name$() {\n" - " if (has_$name$()) {\n" - " clear_has_$oneof_name$();\n" - " $type$* temp = $oneof_prefix$$name$_;\n" - " $oneof_prefix$$name$_ = 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())) { @@ -450,32 +681,42 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, // 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" + " if ($this_message$GetArenaNoVirtual() != NULL &&\n" " ::google::protobuf::Arena::GetArena($name$) == NULL) {\n" - " GetArenaNoVirtual()->Own($name$);\n" - " } else if (GetArenaNoVirtual() !=\n" + " $this_message$GetArenaNoVirtual()->Own($name$);\n" + " } else if ($this_message$GetArenaNoVirtual() !=\n" " ::google::protobuf::Arena::GetArena($name$)) {\n" - " $type$* new_$name$ = \n" - " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" - " GetArenaNoVirtual());\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 (GetArenaNoVirtual() != NULL) {\n" - " GetArenaNoVirtual()->Own($name$);\n" + " if ($this_message$GetArenaNoVirtual() != NULL) {\n" + " $this_message$GetArenaNoVirtual()->Own($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" - "$inline$ void $classname$::unsafe_arena_set_allocated_$name$(" - "$type$* $name$) {\n" + "$inline$ $type$* $classname$::unsafe_arena_release_$name$() {\n" + " if (has_$name$()) {\n" + " clear_has_$oneof_name$();\n" + " $type$* temp = $oneof_prefix$$name$_;\n" + " $oneof_prefix$$name$_ = NULL;\n" + " return temp;\n" + " } else {\n" + " return NULL;\n" + " }\n" + "}\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 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:: -GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { +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:: -GenerateInlineAccessorDefinitions(io::Printer* printer, - bool is_inline) const { +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 variables(variables_); - variables["inline"] = is_inline ? "inline" : ""; + // 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()) + ""; + variables["this_message"] = DependentBaseDownCast(); + variables["this_const_message"] = DependentBaseConstDownCast(); + + if (dependent_getter_) { + printer->Print(variables, + "template \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, - "$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" + "template \n" + "inline $type$* $dependent_classname$::mutable_$name$(int index) {\n" + // TODO(dlj): move insertion points " // @@protoc_insertion_point(field_mutable:$full_name$)\n" - " return $name$_.Mutable(index);\n" + " return $this_message$$name$_.Mutable(index);\n" "}\n" - "$inline$ $type$* $classname$::add_$name$() {\n" + "template \n" + "inline $type$* $dependent_classname$::add_$name$() {\n" " // @@protoc_insertion_point(field_add:$full_name$)\n" - " return $name$_.Add();\n" + " return $this_message$$name$_.Add();\n" "}\n"); + + + if (dependent_getter_) { + printer->Print(variables, + "template \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, - "$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" + "template \n" + "inline ::google::protobuf::RepeatedPtrField< $type$ >*\n" + "$dependent_classname$::mutable_$name$() {\n" " // @@protoc_insertion_point(field_mutable_list:$full_name$)\n" - " return &$name$_;\n" + " return &$this_message$$name$_;\n" "}\n"); } +void RepeatedMessageFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer, + bool is_inline) const { + map variables(variables_); + 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 variables(variables_); + variables["this_message"] = dependent_field_ ? DependentBaseDownCast() : ""; + printer->Print(variables, "$this_message$$name$_.Clear();\n"); } void RepeatedMessageFieldGenerator:: diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index 9ddf964395..35efd0fa7c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -68,6 +68,11 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateByteSize(io::Printer* printer) const; protected: + void GenerateArenaManipulationCode(const map& variables, + io::Printer* printer) const; + + virtual void GenerateGetterDeclaration(io::Printer* printer) const; + const FieldDescriptor* descriptor_; const bool dependent_field_; map 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& 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 variables_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 1a3896a15b..6b0821a6c1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -367,25 +367,19 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" " input, this->mutable_$name$()));\n"); - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::PARSE,\n" - " \"$full_name$\");\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, true, variables_, + "this->$name$().data(), this->$name$().length(),\n", printer); } } void StringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$full_name$\");\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, false, variables_, + "this->$name$().data(), this->$name$().length(),\n", printer); } printer->Print(variables_, "::google::protobuf::internal::WireFormatLite::Write$declared_type$MaybeAliased(\n" @@ -394,13 +388,10 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { void StringFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$full_name$\");\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, false, variables_, + "this->$name$().data(), this->$name$().length(),\n", printer); } printer->Print(variables_, "target =\n" @@ -421,7 +412,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 +596,29 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, void StringOneofFieldGenerator:: GenerateClearingCode(io::Printer* printer) const { + map 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 { + variables["this_message"] = ""; + } if (SupportsArenas(descriptor_)) { - printer->Print(variables_, - "$oneof_prefix$$name$_.Destroy($default_variable$,\n" - " GetArenaNoVirtual());\n"); + printer->Print(variables, + "$this_message$$oneof_prefix$$name$_.Destroy($default_variable$,\n" + " $this_message$GetArenaNoVirtual());\n"); } else { - printer->Print(variables_, - "$oneof_prefix$$name$_.DestroyNoArena($default_variable$);\n"); + printer->Print(variables, + "$this_message$$oneof_prefix$$name$_." + "DestroyNoArena($default_variable$);\n"); } } @@ -648,13 +656,10 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" " input, this->mutable_$name$()));\n"); - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$().data(), this->$name$().length(),\n" - " ::google::protobuf::internal::WireFormat::PARSE,\n" - " \"$full_name$\");\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, true, variables_, + "this->$name$().data(), this->$name$().length(),\n", printer); } } @@ -664,7 +669,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { RepeatedStringFieldGenerator:: RepeatedStringFieldGenerator(const FieldDescriptor* descriptor, const Options& options) - : descriptor_(descriptor) { + : descriptor_(descriptor) { SetStringVariables(descriptor, &variables_, options); } @@ -800,14 +805,12 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, "DO_(::google::protobuf::internal::WireFormatLite::Read$declared_type$(\n" " input, this->add_$name$()));\n"); - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$(this->$name$_size() - 1).data(),\n" - " this->$name$(this->$name$_size() - 1).length(),\n" - " ::google::protobuf::internal::WireFormat::PARSE,\n" - " \"$full_name$\");\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, true, variables_, + "this->$name$(this->$name$_size() - 1).data(),\n" + "this->$name$(this->$name$_size() - 1).length(),\n", + printer); } } @@ -815,14 +818,13 @@ void RepeatedStringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - "::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$(i).data(), this->$name$(i).length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$full_name$\");\n"); + printer->Indent(); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, false, variables_, + "this->$name$(i).data(), this->$name$(i).length(),\n", printer); } + printer->Outdent(); printer->Print(variables_, " ::google::protobuf::internal::WireFormatLite::Write$declared_type$(\n" " $number$, this->$name$(i), output);\n" @@ -833,14 +835,13 @@ void RepeatedStringFieldGenerator:: GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); - if (HasUtf8Verification(descriptor_->file()) && - descriptor_->type() == FieldDescriptor::TYPE_STRING) { - printer->Print(variables_, - " ::google::protobuf::internal::WireFormat::VerifyUTF8StringNamedField(\n" - " this->$name$(i).data(), this->$name$(i).length(),\n" - " ::google::protobuf::internal::WireFormat::SERIALIZE,\n" - " \"$full_name$\");\n"); + printer->Indent(); + if (descriptor_->type() == FieldDescriptor::TYPE_STRING) { + GenerateUtf8CheckCodeForString( + descriptor_, false, variables_, + "this->$name$(i).data(), this->$name$(i).length(),\n", printer); } + printer->Outdent(); printer->Print(variables_, " target = ::google::protobuf::internal::WireFormatLite::\n" " Write$declared_type$ToArray($number$, this->$name$(i), target);\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index d1f19cd942..616e20674f 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -93,6 +93,7 @@ class StringOneofFieldGenerator : public StringFieldGenerator { void GenerateMergeFromCodedStream(io::Printer* printer) const; private: + const bool dependent_field_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOneofFieldGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index bd1c0fde03..e5ef6ecd48 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -67,7 +67,9 @@ #include #include +#include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc index 43eb0ed583..33c9328fe3 100644 --- a/src/google/protobuf/compiler/importer_unittest.cc +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -44,6 +44,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc index 0353b6072e..8a09f3a83e 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -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" diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc index 39318a192a..558da968ee 100644 --- a/src/google/protobuf/compiler/java/java_enum_field.cc +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc index 697a07a733..2c3608c290 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc index e69de29bb2..621863861f 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_lite.cc @@ -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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 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 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 diff --git a/src/google/protobuf/compiler/java/java_enum_lite.h b/src/google/protobuf/compiler/java/java_enum_lite.h index e69de29bb2..ee2f5f7a88 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.h +++ b/src/google/protobuf/compiler/java/java_enum_lite.h @@ -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 +#include +#include + +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 canonical_values_; + + struct Alias { + const EnumValueDescriptor* value; + const EnumValueDescriptor* canonical_value; + }; + vector 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__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index 3f0fa11fe3..c54347677b 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -39,6 +39,7 @@ #include #endif +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h index 00f3c601e8..0e24da24c6 100644 --- a/src/google/protobuf/compiler/java/java_field.h +++ b/src/google/protobuf/compiler/java/java_field.h @@ -44,6 +44,7 @@ #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index 96d2545f31..7eef86a77a 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -332,6 +332,15 @@ 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"; +} + +inline bool CheckUtf8(const FieldDescriptor* descriptor) { + return descriptor->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 || + descriptor->file()->options().java_string_check_utf8(); +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc index 44b86cd74d..3e035c89b9 100644 --- a/src/google/protobuf/compiler/java/java_map_field.cc +++ b/src/google/protobuf/compiler/java/java_map_field.cc @@ -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"); } } diff --git a/src/google/protobuf/compiler/java/java_map_field_lite.cc b/src/google/protobuf/compiler/java/java_map_field_lite.cc index cd1698f0b3..4fe656d3bb 100644 --- a/src/google/protobuf/compiler/java/java_map_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_map_field_lite.cc @@ -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"); } } diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index 09b0fd945c..80d6e9ade1 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -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) { + 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( - "result = result && (has$name$() == other.has$name$());\n" - "if (has$name$()) {\n", - "name", info->capitalized_name); + "case $field_number$:\n", + "field_number", + SimpleItoa(field->number())); printer->Indent(); - } - field_generators_.get(field).GenerateEqualsCode(printer); - if (check_has_bits) { + field_generators_.get(field).GenerateEqualsCode(printer); + printer->Print("break;\n"); printer->Outdent(); - printer->Print( - "}\n"); } + 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 Any pack(\n" + " T message) {\n" + " return Any.newBuilder()\n" + " .setTypeUrl(getTypeUrl(message.getDescriptorForType()))\n" + " .setValue(message.toByteString())\n" + " .build();\n" + "}\n" + "\n" + "public boolean is(\n" + " java.lang.Class 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 unpack(\n" + " java.lang.Class 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 diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h index c3c3776514..be5bfb0754 100644 --- a/src/google/protobuf/compiler/java/java_message.h +++ b/src/google/protobuf/compiler/java/java_message.h @@ -122,6 +122,7 @@ class ImmutableMessageGenerator : public MessageGenerator { void GenerateEqualsAndHashCode(io::Printer* printer); void GenerateParser(io::Printer* printer); void GenerateParsingConstructor(io::Printer* printer); + void GenerateAnyMethods(io::Printer* printer); Context* context_; ClassNameResolver* name_resolver_; diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc index b180b4a718..b5f8e626fd 100644 --- a/src/google/protobuf/compiler/java/java_message_field.cc +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -452,11 +452,11 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$name$_ = input.readGroup($number$, $type$.PARSER,\n" + "$name$_ = input.readGroup($number$, $type$.parser(),\n" " extensionRegistry);\n"); } else { printer->Print(variables_, - "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + "$name$_ = input.readMessage($type$.parser(), extensionRegistry);\n"); } printer->Print(variables_, @@ -736,11 +736,12 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$oneof_name$_ = input.readGroup($number$, $type$.PARSER,\n" + "$oneof_name$_ = input.readGroup($number$, $type$.parser(),\n" " extensionRegistry);\n"); } else { printer->Print(variables_, - "$oneof_name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + "$oneof_name$_ =\n" + " input.readMessage($type$.parser(), extensionRegistry);\n"); } printer->Print(variables_, @@ -1232,11 +1233,11 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$name$_.add(input.readGroup($number$, $type$.PARSER,\n" + "$name$_.add(input.readGroup($number$, $type$.parser(),\n" " extensionRegistry));\n"); } else { printer->Print(variables_, - "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n"); + "$name$_.add(input.readMessage($type$.parser(), extensionRegistry));\n"); } } diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc index 8332202c5e..356520ec65 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -310,11 +310,11 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$name$_ = input.readGroup($number$, $type$.PARSER,\n" + "$name$_ = input.readGroup($number$, $type$.parser(),\n" " extensionRegistry);\n"); } else { printer->Print(variables_, - "$name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + "$name$_ = input.readMessage($type$.parser(), extensionRegistry);\n"); } printer->Print(variables_, @@ -521,11 +521,12 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$oneof_name$_ = input.readGroup($number$, $type$.PARSER,\n" + "$oneof_name$_ = input.readGroup($number$, $type$.parser(),\n" " extensionRegistry);\n"); } else { printer->Print(variables_, - "$oneof_name$_ = input.readMessage($type$.PARSER, extensionRegistry);\n"); + "$oneof_name$_ =\n" + " input.readMessage($type$.parser(), extensionRegistry);\n"); } printer->Print(variables_, @@ -885,11 +886,12 @@ GenerateParsingCode(io::Printer* printer) const { if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { printer->Print(variables_, - "$name$_.add(input.readGroup($number$, $type$.PARSER,\n" + "$name$_.add(input.readGroup($number$, $type$.parser(),\n" " extensionRegistry));\n"); } else { printer->Print(variables_, - "$name$_.add(input.readMessage($type$.PARSER, extensionRegistry));\n"); + "$name$_.add(\n" + " input.readMessage($type$.parser(), extensionRegistry));\n"); } } diff --git a/src/google/protobuf/compiler/java/java_message_lite.cc b/src/google/protobuf/compiler/java/java_message_lite.cc index 3accee92c7..8b6c75b8ba 100644 --- a/src/google/protobuf/compiler/java/java_message_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_lite.cc @@ -45,7 +45,7 @@ #include #include -#include +#include #include #include #include @@ -143,6 +143,17 @@ void ImmutableMessageLiteGenerator::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"); @@ -190,7 +201,7 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { // Nested types for (int i = 0; i < descriptor_->enum_type_count(); i++) { - EnumGenerator(descriptor_->enum_type(i), true, context_) + EnumLiteGenerator(descriptor_->enum_type(i), true, context_) .Generate(printer); } @@ -321,12 +332,12 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Print( "protected final Object dynamicMethod(\n" " com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n" - " Object... args) {\n" + " Object arg0, Object arg1) {\n" " switch (method) {\n" " case PARSE_PARTIAL_FROM: {\n" " return new $classname$(" - " (com.google.protobuf.CodedInputStream) args[0],\n" - " (com.google.protobuf.ExtensionRegistryLite) args[1]);\n" + " (com.google.protobuf.CodedInputStream) arg0,\n" + " (com.google.protobuf.ExtensionRegistryLite) arg1);\n" " }\n" " case NEW_INSTANCE: {\n" " return new $classname$(\n" @@ -370,7 +381,25 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { printer->Outdent(); printer->Print( - "}\n"); + "}\n" + "case GET_DEFAULT_INSTANCE: {\n" + " return DEFAULT_INSTANCE;\n" + "}\n" + "case GET_PARSER: {\n" + // Generally one would use the lazy initialization holder pattern for + // manipulating static fields but that has exceptional cost on Android as + // it will generate an extra class for every message. Instead, use the + // double-check locking pattern which works just as well. + " if (PARSER == null) {" + " synchronized ($classname$.class) {\n" + " if (PARSER == null) {\n" + " PARSER = new DefaultInstanceBasedParser(DEFAULT_INSTANCE);\n" + " }\n" + " }\n" + " }\n" + " return PARSER;\n" + "}\n", + "classname", name_resolver_->GetImmutableClassName(descriptor_)); printer->Outdent(); printer->Outdent(); @@ -413,18 +442,6 @@ void ImmutableMessageLiteGenerator::Generate(io::Printer* printer) { GenerateParser(printer); - // LITE_RUNTIME uses this to implement the *ForType methods at the - // GeneratedMessageLite level. - printer->Print( - "static {\n" - " com.google.protobuf.GeneratedMessageLite.onLoad(\n" - " $classname$.class, new com.google.protobuf.GeneratedMessageLite\n" - " .PrototypeHolder<$classname$, Builder>(\n" - " DEFAULT_INSTANCE, PARSER));" - "}\n" - "\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_)); - // Extensions must be declared after the DEFAULT_INSTANCE is initialized // because the DEFAULT_INSTANCE is used by the extension to lazily retrieve // the outer class's FileDescriptor. @@ -554,54 +571,54 @@ GenerateParseFromMethods(io::Printer* printer) { "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return PARSER.parseFrom(data);\n" + " return parser().parseFrom(data);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.ByteString data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return PARSER.parseFrom(data, extensionRegistry);\n" + " return parser().parseFrom(data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(byte[] data)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return PARSER.parseFrom(data);\n" + " return parser().parseFrom(data);\n" "}\n" "public static $classname$ parseFrom(\n" " byte[] data,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws com.google.protobuf.InvalidProtocolBufferException {\n" - " return PARSER.parseFrom(data, extensionRegistry);\n" + " return parser().parseFrom(data, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return PARSER.parseFrom(input);\n" + " return parser().parseFrom(input);\n" "}\n" "public static $classname$ parseFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return PARSER.parseFrom(input, extensionRegistry);\n" + " return parser().parseFrom(input, extensionRegistry);\n" "}\n" "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" " throws java.io.IOException {\n" - " return PARSER.parseDelimitedFrom(input);\n" + " return parser().parseDelimitedFrom(input);\n" "}\n" "public static $classname$ parseDelimitedFrom(\n" " java.io.InputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return PARSER.parseDelimitedFrom(input, extensionRegistry);\n" + " return parser().parseDelimitedFrom(input, extensionRegistry);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" - " return PARSER.parseFrom(input);\n" + " return parser().parseFrom(input);\n" "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input,\n" " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" " throws java.io.IOException {\n" - " return PARSER.parseFrom(input, extensionRegistry);\n" + " return parser().parseFrom(input, extensionRegistry);\n" "}\n" "\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); @@ -652,7 +669,7 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodIsInitialized( "if (isInitialized == 1) return DEFAULT_INSTANCE;\n" "if (isInitialized == 0) return null;\n" "\n" - "boolean shouldMemoize = ((Boolean) args[0]).booleanValue();\n"); + "boolean shouldMemoize = ((Boolean) arg0).booleanValue();\n"); // Check that all required fields in this message are set. // TODO(kenton): We can optimize this when we switch to putting all the @@ -778,7 +795,7 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMakeImmutable( .GenerateDynamicMethodMakeImmutableCode(printer); } printer->Print( - "return null;"); + "return null;\n"); } // =================================================================== @@ -786,7 +803,7 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMakeImmutable( void ImmutableMessageLiteGenerator::GenerateDynamicMethodNewBuilder( io::Printer* printer) { printer->Print( - "return new Builder();"); + "return new Builder();\n"); } // =================================================================== @@ -796,9 +813,8 @@ void ImmutableMessageLiteGenerator::GenerateDynamicMethodMergeFrom( printer->Print( // Optimization: If other is the default instance, we know none of its // fields are set so we can skip the merge. - "Object arg = args[0];\n" - "if (arg == $classname$.getDefaultInstance()) return this;\n" - "$classname$ other = ($classname$) arg;\n", + "if (arg0 == $classname$.getDefaultInstance()) return this;\n" + "$classname$ other = ($classname$) arg0;\n", "classname", name_resolver_->GetImmutableClassName(descriptor_)); for (int i = 0; i < descriptor_->field_count(); i++) { @@ -1151,9 +1167,11 @@ GenerateParsingConstructor(io::Printer* printer) { // =================================================================== void ImmutableMessageLiteGenerator::GenerateParser(io::Printer* printer) { printer->Print( - "public static final com.google.protobuf.Parser<$classname$> PARSER =\n" - " new DefaultInstanceBasedParser(DEFAULT_INSTANCE);\n" - "\n", + "private static volatile com.google.protobuf.Parser<$classname$> PARSER;\n" + "\n" + "public static com.google.protobuf.Parser<$classname$> parser() {\n" + " return DEFAULT_INSTANCE.getParserForType();\n" + "}\n", "classname", descriptor_->name()); } diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 7bebe12a94..178bbe1900 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc index 217ff9b69c..392333b8cb 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -35,6 +35,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc index 7baead153a..11bfc12d1b 100644 --- a/src/google/protobuf/compiler/java/java_service.cc +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -39,7 +39,6 @@ #include #include #include -#include #include namespace google { diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc index 68e863ccc3..72ebaecabb 100644 --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,10 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, " if (value == null) {\n" " throw new NullPointerException();\n" " }\n"; + (*variables)["writeString"] = + "com.google.protobuf.GeneratedMessage.writeString"; + (*variables)["computeStringSize"] = + "com.google.protobuf.GeneratedMessage.computeStringSize"; // TODO(birdo): Add @deprecated javadoc when generating javadoc is supported // by the proto compiler @@ -126,10 +131,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, GenerateSetBitToLocal(messageBitIndex); } -bool CheckUtf8(const FieldDescriptor* descriptor) { - return descriptor->file()->options().java_string_check_utf8(); -} - } // namespace // =================================================================== @@ -433,7 +434,7 @@ void ImmutableStringFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "if ($is_field_present_message$) {\n" - " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + " $writeString$(output, $number$, $name$_);\n" "}\n"); } @@ -441,8 +442,7 @@ void ImmutableStringFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "if ($is_field_present_message$) {\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + " size += $computeStringSize$($number$, $name$_);\n" "}\n"); } @@ -689,7 +689,7 @@ void ImmutableStringOneofFieldGenerator:: GenerateSerializationCode(io::Printer* printer) const { printer->Print(variables_, "if ($has_oneof_case_message$) {\n" - " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + " $writeString$(output, $number$, $oneof_name$_);\n" "}\n"); } @@ -697,8 +697,7 @@ void ImmutableStringOneofFieldGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "if ($has_oneof_case_message$) {\n" - " size += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + " size += $computeStringSize$($number$, $oneof_name$_);\n" "}\n"); } @@ -1007,12 +1006,12 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.write$capitalized_type$NoTag($name$_.get(i));\n" + " writeStringNoTag(output, $name$_.getRaw(i));\n" "}\n"); } else { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeBytes($number$, $name$_.getByteString(i));\n" + " $writeString$(output, $number$, $name$_.getRaw(i));\n" "}\n"); } } @@ -1026,8 +1025,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" - " dataSize += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSizeNoTag($name$_.getByteString(i));\n" + " dataSize += computeStringSizeNoTag($name$_.getRaw(i));\n" "}\n"); printer->Print( diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index 51bb245c49..092e3c2934 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -64,7 +65,8 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, map* variables) { SetCommonFieldVariables(descriptor, info, variables); - (*variables)["empty_list"] = "emptyLazyStringArrayList()"; + (*variables)["empty_list"] = + "com.google.protobuf.GeneratedMessageLite.emptyProtobufList()"; (*variables)["default"] = ImmutableDefaultValue(descriptor, name_resolver); (*variables)["default_init"] = @@ -101,7 +103,7 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, (*variables)["clear_has_field_bit_message"] = ""; (*variables)["is_field_present_message"] = - "!get" + (*variables)["capitalized_name"] + "Bytes().isEmpty()"; + "!get" + (*variables)["capitalized_name"] + ".isEmpty()"; } // For repeated builders, the underlying list tracks mutability state. @@ -113,10 +115,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, GenerateSetBitToLocal(messageBitIndex); } -bool CheckUtf8(const FieldDescriptor* descriptor) { - return descriptor->file()->options().java_string_check_utf8(); -} - } // namespace // =================================================================== @@ -144,8 +142,9 @@ int ImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { return 0; } -// A note about how strings are handled. This code used to just store a String -// in the Message. This had two issues: +// A note about how strings are handled. In the SPEED and CODE_SIZE runtimes, +// strings are not stored as java.lang.String in the Message because of two +// issues: // // 1. It wouldn't roundtrip byte arrays that were not vaid UTF-8 encoded // strings, but rather fields that were raw bytes incorrectly marked @@ -160,22 +159,14 @@ int ImmutableStringFieldLiteGenerator::GetNumBitsForBuilder() const { // it many cases, the field is never even read by the application code. This // avoids unnecessary conversions in the common use cases. // -// So now, the field for String is maintained as an Object reference which can -// either store a String or a ByteString. The code uses an instanceof check -// to see which one it has and converts to the other one if needed. It remembers -// the last value requested (in a thread safe manner) as this is most likely -// the one needed next. The thread safety is such that if two threads both -// convert the field because the changes made by each thread were not visible to -// the other, they may cause a conversion to happen more times than would -// otherwise be necessary. This was deemed better than adding synchronization -// overhead. It will not cause any corruption issues or affect the behavior of -// the API. The instanceof check is also highly optimized in the JVM and we -// decided it was better to reduce the memory overhead by not having two -// separate fields but rather use dynamic type checking. -// -// For single fields, the logic for this is done inside the generated code. For -// repeated fields, the logic is done in LazyStringArrayList and -// UnmodifiableLazyStringList. +// In the LITE_RUNTIME, we store strings as java.lang.String because we assume +// that the users of this runtime are not subject to proto1 constraints and are +// running code on devices that are user facing. That is, the developers are +// properly incentivized to only fetch the data they need to read and wish to +// reduce the number of allocations incurred when running on a user's device. + +// TODO(dweis): Consider dropping all of the *Bytes() methods. They really +// shouldn't be necessary or used on devices. void ImmutableStringFieldLiteGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { if (SupportFieldPresence(descriptor_->file())) { @@ -195,7 +186,7 @@ GenerateInterfaceMembers(io::Printer* printer) const { void ImmutableStringFieldLiteGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private java.lang.Object $name$_;\n"); + "private java.lang.String $name$_;\n"); PrintExtraFieldInfo(variables_, printer); if (SupportFieldPresence(descriptor_->file())) { @@ -209,40 +200,13 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$() {\n" - " java.lang.Object ref = $name$_;\n" - " if (ref instanceof java.lang.String) {\n" - " return (java.lang.String) ref;\n" - " } else {\n" - " com.google.protobuf.ByteString bs = \n" - " (com.google.protobuf.ByteString) ref;\n" - " java.lang.String s = bs.toStringUtf8();\n"); - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, - " $name$_ = s;\n"); - } else { - printer->Print(variables_, - " if (bs.isValidUtf8()) {\n" - " $name$_ = s;\n" - " }\n"); - } - printer->Print(variables_, - " return s;\n" - " }\n" + " return $name$_;\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public com.google.protobuf.ByteString\n" " get$capitalized_name$Bytes() {\n" - " java.lang.Object ref = $name$_;\n" - " if (ref instanceof java.lang.String) {\n" - " com.google.protobuf.ByteString b = \n" - " com.google.protobuf.ByteString.copyFromUtf8(\n" - " (java.lang.String) ref);\n" - " $name$_ = b;\n" - " return b;\n" - " } else {\n" - " return (com.google.protobuf.ByteString) ref;\n" - " }\n" + " return com.google.protobuf.ByteString.copyFromUtf8($name$_);\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -273,7 +237,7 @@ GenerateMembers(io::Printer* printer) const { } printer->Print(variables_, " $set_has_field_bit_message$\n" - " $name$_ = value;\n" + " $name$_ = value.toStringUtf8();\n" "}\n"); } @@ -368,7 +332,7 @@ GenerateParsingCode(io::Printer* printer) const { "String s = input.readStringRequireUtf8();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { + } else { // Lite runtime should attempt to reduce allocations by attempting to // construct the string directly from the input stream buffer. This avoids // spurious intermediary ByteString allocations, cutting overall allocations @@ -377,11 +341,6 @@ GenerateParsingCode(io::Printer* printer) const { "String s = input.readString();\n" "$set_has_field_bit_message$\n" "$name$_ = s;\n"); - } else { - printer->Print(variables_, - "com.google.protobuf.ByteString bs = input.readBytes();\n" - "$set_has_field_bit_message$\n" - "$name$_ = bs;\n"); } } @@ -392,18 +351,24 @@ GenerateParsingDoneCode(io::Printer* printer) const { void ImmutableStringFieldLiteGenerator:: GenerateSerializationCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by serializing the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. printer->Print(variables_, "if ($is_field_present_message$) {\n" - " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + " output.writeString($number$, get$capitalized_name$());\n" "}\n"); } void ImmutableStringFieldLiteGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by computing on the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. printer->Print(variables_, "if ($is_field_present_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + " .computeStringSize($number$, get$capitalized_name$());\n" "}\n"); } @@ -458,51 +423,22 @@ GenerateMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public java.lang.String get$capitalized_name$() {\n" - " java.lang.Object ref $default_init$;\n" + " java.lang.String ref $default_init$;\n" " if ($has_oneof_case_message$) {\n" - " ref = $oneof_name$_;\n" - " }\n" - " if (ref instanceof java.lang.String) {\n" - " return (java.lang.String) ref;\n" - " } else {\n" - " com.google.protobuf.ByteString bs = \n" - " (com.google.protobuf.ByteString) ref;\n" - " java.lang.String s = bs.toStringUtf8();\n"); - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, - " if ($has_oneof_case_message$) {\n" - " $oneof_name$_ = s;\n" - " }\n"); - } else { - printer->Print(variables_, - " if (bs.isValidUtf8() && ($has_oneof_case_message$)) {\n" - " $oneof_name$_ = s;\n" - " }\n"); - } - printer->Print(variables_, - " return s;\n" + " ref = (java.lang.String) $oneof_name$_;\n" " }\n" + " return ref;\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, "$deprecation$public com.google.protobuf.ByteString\n" " get$capitalized_name$Bytes() {\n" - " java.lang.Object ref $default_init$;\n" + " java.lang.String ref $default_init$;\n" " if ($has_oneof_case_message$) {\n" - " ref = $oneof_name$_;\n" - " }\n" - " if (ref instanceof java.lang.String) {\n" - " com.google.protobuf.ByteString b = \n" - " com.google.protobuf.ByteString.copyFromUtf8(\n" - " (java.lang.String) ref);\n" - " if ($has_oneof_case_message$) {\n" - " $oneof_name$_ = b;\n" - " }\n" - " return b;\n" - " } else {\n" - " return (com.google.protobuf.ByteString) ref;\n" + " ref = (java.lang.String) $oneof_name$_;\n" " }\n" + " return com.google.protobuf.ByteString.copyFromUtf8(ref);\n" "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -533,7 +469,7 @@ GenerateMembers(io::Printer* printer) const { } printer->Print(variables_, " $set_oneof_case_message$;\n" - " $oneof_name$_ = value;\n" + " $oneof_name$_ = value.toStringUtf8();\n" "}\n"); } @@ -603,7 +539,7 @@ GenerateParsingCode(io::Printer* printer) const { "String s = input.readStringRequireUtf8();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { + } else { // Lite runtime should attempt to reduce allocations by attempting to // construct the string directly from the input stream buffer. This avoids // spurious intermediary ByteString allocations, cutting overall allocations @@ -612,28 +548,29 @@ GenerateParsingCode(io::Printer* printer) const { "String s = input.readString();\n" "$set_oneof_case_message$;\n" "$oneof_name$_ = s;\n"); - } else { - printer->Print(variables_, - "com.google.protobuf.ByteString bs = input.readBytes();\n" - "$set_oneof_case_message$;\n" - "$oneof_name$_ = bs;\n"); } } void ImmutableStringOneofFieldLiteGenerator:: GenerateSerializationCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by serializing the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. printer->Print(variables_, "if ($has_oneof_case_message$) {\n" - " output.writeBytes($number$, get$capitalized_name$Bytes());\n" + " output.writeString($number$, get$capitalized_name$());\n" "}\n"); } void ImmutableStringOneofFieldLiteGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by computing on the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. printer->Print(variables_, "if ($has_oneof_case_message$) {\n" " size += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSize($number$, get$capitalized_name$Bytes());\n" + " .computeStringSize($number$, get$capitalized_name$());\n" "}\n"); } @@ -667,7 +604,7 @@ void RepeatedImmutableStringFieldLiteGenerator:: GenerateInterfaceMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$com.google.protobuf.ProtocolStringList\n" + "$deprecation$java.util.List\n" " get$capitalized_name$List();\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -685,12 +622,11 @@ GenerateInterfaceMembers(io::Printer* printer) const { void RepeatedImmutableStringFieldLiteGenerator:: GenerateMembers(io::Printer* printer) const { printer->Print(variables_, - "private com.google.protobuf.LazyStringArrayList $name$_;\n"); + "private com.google.protobuf.Internal.ProtobufList $name$_;\n"); PrintExtraFieldInfo(variables_, printer); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$public com.google.protobuf.ProtocolStringList\n" - " get$capitalized_name$List() {\n" + "$deprecation$public java.util.List get$capitalized_name$List() {\n" " return $name$_;\n" // note: unmodifiable list "}\n"); WriteFieldDocComment(printer, descriptor_); @@ -707,7 +643,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "$deprecation$public com.google.protobuf.ByteString\n" " get$capitalized_name$Bytes(int index) {\n" - " return $name$_.getByteString(index);\n" + " return com.google.protobuf.ByteString.copyFromUtf8(\n" + " $name$_.get(index));\n" "}\n"); if (descriptor_->options().packed() && @@ -719,7 +656,8 @@ GenerateMembers(io::Printer* printer) const { printer->Print(variables_, "private void ensure$capitalized_name$IsMutable() {\n" " if (!$is_mutable$) {\n" - " $name$_ = new com.google.protobuf.LazyStringArrayList($name$_);\n" + " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList(\n" + " $name$_);\n" " }\n" "}\n"); @@ -764,7 +702,7 @@ GenerateMembers(io::Printer* printer) const { } printer->Print(variables_, " ensure$capitalized_name$IsMutable();\n" - " $name$_.add(value);\n" + " $name$_.add(value.toStringUtf8());\n" "}\n"); } @@ -772,10 +710,10 @@ void RepeatedImmutableStringFieldLiteGenerator:: GenerateBuilderMembers(io::Printer* printer) const { WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, - "$deprecation$public com.google.protobuf.ProtocolStringList\n" + "$deprecation$public java.util.List\n" " get$capitalized_name$List() {\n" - " return ((com.google.protobuf.LazyStringList)\n" - " instance.get$capitalized_name$List()).getUnmodifiableView();\n" + " return java.util.Collections.unmodifiableList(\n" + " instance.get$capitalized_name$List());\n" "}\n"); WriteFieldDocComment(printer, descriptor_); printer->Print(variables_, @@ -875,20 +813,17 @@ GenerateParsingCode(io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, "String s = input.readStringRequireUtf8();\n"); - } else if (!HasDescriptorMethods(descriptor_->file())) { + } else { // Lite runtime should attempt to reduce allocations by attempting to // construct the string directly from the input stream buffer. This avoids // spurious intermediary ByteString allocations, cutting overall allocations // in half. printer->Print(variables_, "String s = input.readString();\n"); - } else { - printer->Print(variables_, - "com.google.protobuf.ByteString bs = input.readBytes();\n"); } printer->Print(variables_, "if (!$is_mutable$) {\n" - " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n" "}\n"); if (CheckUtf8(descriptor_) || !HasDescriptorMethods(descriptor_->file())) { printer->Print(variables_, @@ -905,7 +840,7 @@ GenerateParsingCodeFromPacked(io::Printer* printer) const { "int length = input.readRawVarint32();\n" "int limit = input.pushLimit(length);\n" "if (!$is_mutable$ && input.getBytesUntilLimit() > 0) {\n" - " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" + " $name$_ = com.google.protobuf.GeneratedMessageLite.newProtobufList();\n" "}\n" "while (input.getBytesUntilLimit() > 0) {\n"); if (CheckUtf8(descriptor_)) { @@ -932,6 +867,9 @@ GenerateParsingDoneCode(io::Printer* printer) const { void RepeatedImmutableStringFieldLiteGenerator:: GenerateSerializationCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by serializing the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. if (descriptor_->options().packed()) { printer->Print(variables_, "if (get$capitalized_name$List().size() > 0) {\n" @@ -939,18 +877,21 @@ GenerateSerializationCode(io::Printer* printer) const { " output.writeRawVarint32($name$MemoizedSerializedSize);\n" "}\n" "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.write$capitalized_type$NoTag($name$_.get(i));\n" + " output.writeStringNoTag($name$_.get(i));\n" "}\n"); } else { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" - " output.writeBytes($number$, $name$_.getByteString(i));\n" + " output.writeString($number$, $name$_.get(i));\n" "}\n"); } } void RepeatedImmutableStringFieldLiteGenerator:: GenerateSerializedSizeCode(io::Printer* printer) const { + // Lite runtime should reduce allocations by computing on the string directly. + // This avoids spurious intermediary ByteString allocations, cutting overall + // allocations in half. printer->Print(variables_, "{\n" " int dataSize = 0;\n"); @@ -959,7 +900,7 @@ GenerateSerializedSizeCode(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < $name$_.size(); i++) {\n" " dataSize += com.google.protobuf.CodedOutputStream\n" - " .computeBytesSizeNoTag($name$_.getByteString(i));\n" + " .computeStringSizeNoTag($name$_.get(i));\n" "}\n"); printer->Print( diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index a2eeee2d31..4d0184252b 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -938,6 +939,42 @@ void Parser::GenerateMapEntry(const MapField& map_field, } else { value_field->set_type_name(map_field.value_type_name); } + // Propagate the "enforce_utf8" option to key and value fields if they + // are strings. This helps simplify the implementation of code generators + // and also reflection-based parsing code. + // + // The following definition: + // message Foo { + // map value = 1 [enforce_utf8 = false]; + // } + // will be interpreted as: + // message Foo { + // message ValueEntry { + // option map_entry = true; + // string key = 1 [enforce_utf8 = false]; + // string value = 2 [enforce_utf8 = false]; + // } + // repeated ValueEntry value = 1 [enforce_utf8 = false]; + // } + // + // TODO(xiaofeng): Remove this when the "enforce_utf8" option is removed + // from protocol compiler. + for (int i = 0; i < field->options().uninterpreted_option_size(); ++i) { + const UninterpretedOption& option = + field->options().uninterpreted_option(i); + if (option.name_size() == 1 && + option.name(0).name_part() == "enforce_utf8" && + !option.name(0).is_extension()) { + if (key_field->type() == FieldDescriptorProto::TYPE_STRING) { + key_field->mutable_options()->add_uninterpreted_option() + ->CopyFrom(option); + } + if (value_field->type() == FieldDescriptorProto::TYPE_STRING) { + value_field->mutable_options()->add_uninterpreted_option() + ->CopyFrom(option); + } + } + } } bool Parser::ParseFieldOptions(FieldDescriptorProto* field, diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index 16012e9603..007b001c35 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -323,7 +323,7 @@ class LIBPROTOBUF_EXPORT Parser { const LocationRecorder& service_location, const FileDescriptorProto* containing_file); - // Parse one statement within a message, enum, or service block, inclunding + // Parse one statement within a message, enum, or service block, including // final semicolon. bool ParseMessageStatement(DescriptorProto* message, const LocationRecorder& message_location, @@ -364,7 +364,7 @@ class LIBPROTOBUF_EXPORT Parser { const LocationRecorder& extensions_location, const FileDescriptorProto* containing_file); - // Parse an "reserved" declaration. + // Parse a "reserved" declaration. bool ParseReserved(DescriptorProto* message, const LocationRecorder& message_location); bool ParseReservedNames(DescriptorProto* message, @@ -415,7 +415,7 @@ class LIBPROTOBUF_EXPORT Parser { Message* mutable_options); // Parse "required", "optional", or "repeated" and fill in "label" - // with the value. Returns true if shuch a label is consumed. + // with the value. Returns true if such a label is consumed. bool ParseLabel(FieldDescriptorProto::Label* label, const FileDescriptorProto* containing_file); diff --git a/src/google/protobuf/compiler/plugin.cc b/src/google/protobuf/compiler/plugin.cc index cdcaffde97..2bebf1f3ad 100644 --- a/src/google/protobuf/compiler/plugin.cc +++ b/src/google/protobuf/compiler/plugin.cc @@ -48,6 +48,7 @@ #include #endif +#include #include #include #include diff --git a/src/google/protobuf/compiler/plugin.pb.cc b/src/google/protobuf/compiler/plugin.pb.cc index e7890fae05..994bc392fd 100644 --- a/src/google/protobuf/compiler/plugin.pb.cc +++ b/src/google/protobuf/compiler/plugin.pb.cc @@ -629,28 +629,28 @@ int CodeGeneratorRequest::proto_file_size() const { void CodeGeneratorRequest::clear_proto_file() { proto_file_.Clear(); } - const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const { +const ::google::protobuf::FileDescriptorProto& CodeGeneratorRequest::proto_file(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Get(index); } - ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::mutable_proto_file(int index) { +::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::mutable_proto_file(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Mutable(index); } - ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::add_proto_file() { +::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::add_proto_file() { // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& -CodeGeneratorRequest::proto_file() const { - // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) - return proto_file_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* CodeGeneratorRequest::mutable_proto_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return &proto_file_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& +CodeGeneratorRequest::proto_file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) + return proto_file_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -1535,28 +1535,28 @@ int CodeGeneratorResponse::file_size() const { void CodeGeneratorResponse::clear_file() { file_.Clear(); } - const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const { +const ::google::protobuf::compiler::CodeGeneratorResponse_File& CodeGeneratorResponse::file(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Get(index); } - ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::mutable_file(int index) { +::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::mutable_file(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Mutable(index); } - ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::add_file() { +::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorResponse::add_file() { // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& -CodeGeneratorResponse::file() const { - // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file) - return file_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* CodeGeneratorResponse::mutable_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorResponse.file) return &file_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& +CodeGeneratorResponse::file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file) + return file_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h index 6fcaea2e80..ab79bdae45 100644 --- a/src/google/protobuf/compiler/plugin.pb.h +++ b/src/google/protobuf/compiler/plugin.pb.h @@ -144,10 +144,10 @@ class LIBPROTOC_EXPORT CodeGeneratorRequest : public ::google::protobuf::Message const ::google::protobuf::FileDescriptorProto& proto_file(int index) const; ::google::protobuf::FileDescriptorProto* mutable_proto_file(int index); ::google::protobuf::FileDescriptorProto* add_proto_file(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& - proto_file() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* mutable_proto_file(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& + proto_file() const; // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorRequest) private: @@ -378,10 +378,10 @@ class LIBPROTOC_EXPORT CodeGeneratorResponse : public ::google::protobuf::Messag const ::google::protobuf::compiler::CodeGeneratorResponse_File& file(int index) const; ::google::protobuf::compiler::CodeGeneratorResponse_File* mutable_file(int index); ::google::protobuf::compiler::CodeGeneratorResponse_File* add_file(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& - file() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* mutable_file(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& + file() const; // @@protoc_insertion_point(class_scope:google.protobuf.compiler.CodeGeneratorResponse) private: @@ -534,16 +534,16 @@ inline ::google::protobuf::FileDescriptorProto* CodeGeneratorRequest::add_proto_ // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return proto_file_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& -CodeGeneratorRequest::proto_file() const { - // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) - return proto_file_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* CodeGeneratorRequest::mutable_proto_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) return &proto_file_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& +CodeGeneratorRequest::proto_file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorRequest.proto_file) + return proto_file_; +} // ------------------------------------------------------------------- @@ -784,16 +784,16 @@ inline ::google::protobuf::compiler::CodeGeneratorResponse_File* CodeGeneratorRe // @@protoc_insertion_point(field_add:google.protobuf.compiler.CodeGeneratorResponse.file) return file_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& -CodeGeneratorResponse::file() const { - // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file) - return file_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >* CodeGeneratorResponse::mutable_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.compiler.CodeGeneratorResponse.file) return &file_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::compiler::CodeGeneratorResponse_File >& +CodeGeneratorResponse::file() const { + // @@protoc_insertion_point(field_list:google.protobuf.compiler.CodeGeneratorResponse.file) + return file_; +} #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc index 7b3b5fa3cd..4d500f90e9 100644 --- a/src/google/protobuf/compiler/python/python_generator.cc +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -58,6 +58,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h index 2ddac6015b..aa0f5fced6 100644 --- a/src/google/protobuf/compiler/python/python_generator.h +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -38,6 +38,7 @@ #include #include +#include #include namespace google { diff --git a/src/google/protobuf/compiler/subprocess.cc b/src/google/protobuf/compiler/subprocess.cc index a3cff1f8b8..85429924ca 100644 --- a/src/google/protobuf/compiler/subprocess.cc +++ b/src/google/protobuf/compiler/subprocess.cc @@ -42,6 +42,7 @@ #include #endif +#include #include #include #include diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index 2855c377e1..5256b83c44 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -53,6 +53,8 @@ #include #include #include +#include +#include #include #include #include @@ -1092,6 +1094,7 @@ const DescriptorPool* DescriptorPool::generated_pool() { return generated_pool_; } + DescriptorPool* DescriptorPool::internal_generated_pool() { InitGeneratedPoolOnce(); return generated_pool_; @@ -3699,6 +3702,14 @@ static bool ExistingFileMatchesProto(const FileDescriptor* existing_file, const FileDescriptorProto& proto) { FileDescriptorProto existing_proto; existing_file->CopyTo(&existing_proto); + // TODO(liujisi): Remove it when CopyTo supports copying syntax params when + // syntax="proto2". + if (existing_file->syntax() == FileDescriptor::SYNTAX_PROTO2 && + proto.has_syntax()) { + existing_proto.set_syntax( + existing_file->SyntaxName(existing_file->syntax())); + } + return existing_proto.SerializeAsString() == proto.SerializeAsString(); } @@ -4649,7 +4660,7 @@ void DescriptorBuilder::CrossLinkMessage( // safe. if (oneof_decl->field_count() > 0 && message->field(i - 1)->containing_oneof() != oneof_decl) { - AddWarning( + AddError( message->full_name() + "." + message->field(i - 1)->name(), proto.field(i - 1), DescriptorPool::ErrorCollector::OTHER, strings::Substitute( @@ -5088,8 +5099,7 @@ void DescriptorBuilder::ValidateProto3Field( field->containing_type()->full_name() + "\" which is a proto3 message type."); } - bool allow_groups = false; - if (field->type() == FieldDescriptor::TYPE_GROUP && !allow_groups) { + if (field->type() == FieldDescriptor::TYPE_GROUP) { AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, "Groups are not supported in proto3 syntax."); @@ -5307,6 +5317,14 @@ bool DescriptorBuilder::ValidateMapEntry(FieldDescriptor* field, // are added. } + if (value->type() == FieldDescriptor::TYPE_ENUM) { + if (value->enum_type()->value(0)->number() != 0) { + AddError( + field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Enum value in map must define 0 as the first value."); + } + } + return true; } diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index ca87d63415..2ab316a521 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -58,6 +58,7 @@ #include #include #include +#include // TYPE_BOOL is defined in the MacOS's ConditionalMacros.h. #ifdef TYPE_BOOL @@ -860,7 +861,7 @@ class LIBPROTOBUF_EXPORT EnumDescriptor { friend class FieldDescriptor; friend class EnumValueDescriptor; friend class FileDescriptor; - friend class LIBPROTOBUF_EXPORT internal::GeneratedMessageReflection; + friend class internal::GeneratedMessageReflection; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumDescriptor); }; @@ -1332,6 +1333,7 @@ class LIBPROTOBUF_EXPORT DescriptorPool { // this pool. Do not add your own descriptors to this pool. static const DescriptorPool* generated_pool(); + // Find a FileDescriptor in the pool by file name. Returns NULL if not // found. const FileDescriptor* FindFileByName(const string& name) const; diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc index 5e7eeaa786..6f9905aa97 100644 --- a/src/google/protobuf/descriptor.pb.cc +++ b/src/google/protobuf/descriptor.pb.cc @@ -755,9 +755,9 @@ void protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto() { "tion\032\206\001\n\010Location\022\020\n\004path\030\001 \003(\005B\002\020\001\022\020\n\004s" "pan\030\002 \003(\005B\002\020\001\022\030\n\020leading_comments\030\003 \001(\t\022" "\031\n\021trailing_comments\030\004 \001(\t\022!\n\031leading_de" - "tached_comments\030\006 \003(\tB[\n\023com.google.prot" + "tached_comments\030\006 \003(\tB;\n\023com.google.prot" "obufB\020DescriptorProtosH\001Z\ndescriptor\242\002\003G" - "PB\252\002\032Google.Protobuf.Reflection\260\002\001", 4994); + "PB", 4962); ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile( "google/protobuf/descriptor.proto", &protobuf_RegisterTypes); FileDescriptorSet::default_instance_ = new FileDescriptorSet(); @@ -1066,28 +1066,28 @@ int FileDescriptorSet::file_size() const { void FileDescriptorSet::clear_file() { file_.Clear(); } - const ::google::protobuf::FileDescriptorProto& FileDescriptorSet::file(int index) const { +const ::google::protobuf::FileDescriptorProto& FileDescriptorSet::file(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorSet.file) return file_.Get(index); } - ::google::protobuf::FileDescriptorProto* FileDescriptorSet::mutable_file(int index) { +::google::protobuf::FileDescriptorProto* FileDescriptorSet::mutable_file(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorSet.file) return file_.Mutable(index); } - ::google::protobuf::FileDescriptorProto* FileDescriptorSet::add_file() { +::google::protobuf::FileDescriptorProto* FileDescriptorSet::add_file() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorSet.file) return file_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& -FileDescriptorSet::file() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorSet.file) - return file_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* FileDescriptorSet::mutable_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorSet.file) return &file_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& +FileDescriptorSet::file() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorSet.file) + return file_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -2091,28 +2091,28 @@ int FileDescriptorProto::message_type_size() const { void FileDescriptorProto::clear_message_type() { message_type_.Clear(); } - const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { +const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.message_type) return message_type_.Get(index); } - ::google::protobuf::DescriptorProto* FileDescriptorProto::mutable_message_type(int index) { +::google::protobuf::DescriptorProto* FileDescriptorProto::mutable_message_type(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.message_type) return message_type_.Mutable(index); } - ::google::protobuf::DescriptorProto* FileDescriptorProto::add_message_type() { +::google::protobuf::DescriptorProto* FileDescriptorProto::add_message_type() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.message_type) return message_type_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& -FileDescriptorProto::message_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.message_type) - return message_type_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* FileDescriptorProto::mutable_message_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.message_type) return &message_type_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +FileDescriptorProto::message_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.message_type) + return message_type_; +} // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; int FileDescriptorProto::enum_type_size() const { @@ -2121,28 +2121,28 @@ int FileDescriptorProto::enum_type_size() const { void FileDescriptorProto::clear_enum_type() { enum_type_.Clear(); } - const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { +const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.enum_type) return enum_type_.Get(index); } - ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::mutable_enum_type(int index) { +::google::protobuf::EnumDescriptorProto* FileDescriptorProto::mutable_enum_type(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.enum_type) return enum_type_.Mutable(index); } - ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::add_enum_type() { +::google::protobuf::EnumDescriptorProto* FileDescriptorProto::add_enum_type() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.enum_type) return enum_type_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& -FileDescriptorProto::enum_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.enum_type) - return enum_type_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* FileDescriptorProto::mutable_enum_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.enum_type) return &enum_type_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +FileDescriptorProto::enum_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.enum_type) + return enum_type_; +} // repeated .google.protobuf.ServiceDescriptorProto service = 6; int FileDescriptorProto::service_size() const { @@ -2151,28 +2151,28 @@ int FileDescriptorProto::service_size() const { void FileDescriptorProto::clear_service() { service_.Clear(); } - const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { +const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.service) return service_.Get(index); } - ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::mutable_service(int index) { +::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::mutable_service(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.service) return service_.Mutable(index); } - ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::add_service() { +::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::add_service() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.service) return service_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& -FileDescriptorProto::service() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.service) - return service_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* FileDescriptorProto::mutable_service() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.service) return &service_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& +FileDescriptorProto::service() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.service) + return service_; +} // repeated .google.protobuf.FieldDescriptorProto extension = 7; int FileDescriptorProto::extension_size() const { @@ -2181,28 +2181,28 @@ int FileDescriptorProto::extension_size() const { void FileDescriptorProto::clear_extension() { extension_.Clear(); } - const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { +const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.extension) return extension_.Get(index); } - ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::mutable_extension(int index) { +::google::protobuf::FieldDescriptorProto* FileDescriptorProto::mutable_extension(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.extension) return extension_.Mutable(index); } - ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::add_extension() { +::google::protobuf::FieldDescriptorProto* FileDescriptorProto::add_extension() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.extension) return extension_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -FileDescriptorProto::extension() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.extension) - return extension_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* FileDescriptorProto::mutable_extension() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.extension) return &extension_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +FileDescriptorProto::extension() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.extension) + return extension_; +} // optional .google.protobuf.FileOptions options = 8; bool FileDescriptorProto::has_options() const { @@ -2218,11 +2218,11 @@ void FileDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); clear_has_options(); } - const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { +const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { +::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::FileOptions; @@ -2230,13 +2230,13 @@ void FileDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.options) return options_; } - ::google::protobuf::FileOptions* FileDescriptorProto::release_options() { +::google::protobuf::FileOptions* FileDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::FileOptions* temp = options_; options_ = NULL; return temp; } - void FileDescriptorProto::set_allocated_options(::google::protobuf::FileOptions* options) { +void FileDescriptorProto::set_allocated_options(::google::protobuf::FileOptions* options) { delete options_; options_ = options; if (options) { @@ -2261,11 +2261,11 @@ void FileDescriptorProto::clear_source_code_info() { if (source_code_info_ != NULL) source_code_info_->::google::protobuf::SourceCodeInfo::Clear(); clear_has_source_code_info(); } - const ::google::protobuf::SourceCodeInfo& FileDescriptorProto::source_code_info() const { +const ::google::protobuf::SourceCodeInfo& FileDescriptorProto::source_code_info() const { // @@protoc_insertion_point(field_get:google.protobuf.FileDescriptorProto.source_code_info) return source_code_info_ != NULL ? *source_code_info_ : *default_instance_->source_code_info_; } - ::google::protobuf::SourceCodeInfo* FileDescriptorProto::mutable_source_code_info() { +::google::protobuf::SourceCodeInfo* FileDescriptorProto::mutable_source_code_info() { set_has_source_code_info(); if (source_code_info_ == NULL) { source_code_info_ = new ::google::protobuf::SourceCodeInfo; @@ -2273,13 +2273,13 @@ void FileDescriptorProto::clear_source_code_info() { // @@protoc_insertion_point(field_mutable:google.protobuf.FileDescriptorProto.source_code_info) return source_code_info_; } - ::google::protobuf::SourceCodeInfo* FileDescriptorProto::release_source_code_info() { +::google::protobuf::SourceCodeInfo* FileDescriptorProto::release_source_code_info() { clear_has_source_code_info(); ::google::protobuf::SourceCodeInfo* temp = source_code_info_; source_code_info_ = NULL; return temp; } - void FileDescriptorProto::set_allocated_source_code_info(::google::protobuf::SourceCodeInfo* source_code_info) { +void FileDescriptorProto::set_allocated_source_code_info(::google::protobuf::SourceCodeInfo* source_code_info) { delete source_code_info_; source_code_info_ = source_code_info; if (source_code_info) { @@ -3720,28 +3720,28 @@ int DescriptorProto::field_size() const { void DescriptorProto::clear_field() { field_.Clear(); } - const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { +const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.field) return field_.Get(index); } - ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_field(int index) { +::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_field(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.field) return field_.Mutable(index); } - ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_field() { +::google::protobuf::FieldDescriptorProto* DescriptorProto::add_field() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.field) return field_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -DescriptorProto::field() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.field) - return field_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* DescriptorProto::mutable_field() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.field) return &field_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::field() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.field) + return field_; +} // repeated .google.protobuf.FieldDescriptorProto extension = 6; int DescriptorProto::extension_size() const { @@ -3750,28 +3750,28 @@ int DescriptorProto::extension_size() const { void DescriptorProto::clear_extension() { extension_.Clear(); } - const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { +const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.extension) return extension_.Get(index); } - ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_extension(int index) { +::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_extension(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.extension) return extension_.Mutable(index); } - ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_extension() { +::google::protobuf::FieldDescriptorProto* DescriptorProto::add_extension() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension) return extension_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -DescriptorProto::extension() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension) - return extension_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* DescriptorProto::mutable_extension() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension) return &extension_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::extension() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension) + return extension_; +} // repeated .google.protobuf.DescriptorProto nested_type = 3; int DescriptorProto::nested_type_size() const { @@ -3780,28 +3780,28 @@ int DescriptorProto::nested_type_size() const { void DescriptorProto::clear_nested_type() { nested_type_.Clear(); } - const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { +const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.nested_type) return nested_type_.Get(index); } - ::google::protobuf::DescriptorProto* DescriptorProto::mutable_nested_type(int index) { +::google::protobuf::DescriptorProto* DescriptorProto::mutable_nested_type(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.nested_type) return nested_type_.Mutable(index); } - ::google::protobuf::DescriptorProto* DescriptorProto::add_nested_type() { +::google::protobuf::DescriptorProto* DescriptorProto::add_nested_type() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.nested_type) return nested_type_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& -DescriptorProto::nested_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.nested_type) - return nested_type_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* DescriptorProto::mutable_nested_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.nested_type) return &nested_type_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +DescriptorProto::nested_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.nested_type) + return nested_type_; +} // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; int DescriptorProto::enum_type_size() const { @@ -3810,28 +3810,28 @@ int DescriptorProto::enum_type_size() const { void DescriptorProto::clear_enum_type() { enum_type_.Clear(); } - const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { +const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.enum_type) return enum_type_.Get(index); } - ::google::protobuf::EnumDescriptorProto* DescriptorProto::mutable_enum_type(int index) { +::google::protobuf::EnumDescriptorProto* DescriptorProto::mutable_enum_type(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.enum_type) return enum_type_.Mutable(index); } - ::google::protobuf::EnumDescriptorProto* DescriptorProto::add_enum_type() { +::google::protobuf::EnumDescriptorProto* DescriptorProto::add_enum_type() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.enum_type) return enum_type_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& -DescriptorProto::enum_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.enum_type) - return enum_type_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* DescriptorProto::mutable_enum_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.enum_type) return &enum_type_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +DescriptorProto::enum_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.enum_type) + return enum_type_; +} // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; int DescriptorProto::extension_range_size() const { @@ -3840,28 +3840,28 @@ int DescriptorProto::extension_range_size() const { void DescriptorProto::clear_extension_range() { extension_range_.Clear(); } - const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { +const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.extension_range) return extension_range_.Get(index); } - ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::mutable_extension_range(int index) { +::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::mutable_extension_range(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.extension_range) return extension_range_.Mutable(index); } - ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::add_extension_range() { +::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::add_extension_range() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension_range) return extension_range_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& -DescriptorProto::extension_range() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension_range) - return extension_range_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* DescriptorProto::mutable_extension_range() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension_range) return &extension_range_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& +DescriptorProto::extension_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension_range) + return extension_range_; +} // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8; int DescriptorProto::oneof_decl_size() const { @@ -3870,28 +3870,28 @@ int DescriptorProto::oneof_decl_size() const { void DescriptorProto::clear_oneof_decl() { oneof_decl_.Clear(); } - const ::google::protobuf::OneofDescriptorProto& DescriptorProto::oneof_decl(int index) const { +const ::google::protobuf::OneofDescriptorProto& DescriptorProto::oneof_decl(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.oneof_decl) return oneof_decl_.Get(index); } - ::google::protobuf::OneofDescriptorProto* DescriptorProto::mutable_oneof_decl(int index) { +::google::protobuf::OneofDescriptorProto* DescriptorProto::mutable_oneof_decl(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.oneof_decl) return oneof_decl_.Mutable(index); } - ::google::protobuf::OneofDescriptorProto* DescriptorProto::add_oneof_decl() { +::google::protobuf::OneofDescriptorProto* DescriptorProto::add_oneof_decl() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.oneof_decl) return oneof_decl_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& -DescriptorProto::oneof_decl() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.oneof_decl) - return oneof_decl_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >* DescriptorProto::mutable_oneof_decl() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.oneof_decl) return &oneof_decl_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& +DescriptorProto::oneof_decl() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.oneof_decl) + return oneof_decl_; +} // optional .google.protobuf.MessageOptions options = 7; bool DescriptorProto::has_options() const { @@ -3907,11 +3907,11 @@ void DescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); clear_has_options(); } - const ::google::protobuf::MessageOptions& DescriptorProto::options() const { +const ::google::protobuf::MessageOptions& DescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { +::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::MessageOptions; @@ -3919,13 +3919,13 @@ void DescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.options) return options_; } - ::google::protobuf::MessageOptions* DescriptorProto::release_options() { +::google::protobuf::MessageOptions* DescriptorProto::release_options() { clear_has_options(); ::google::protobuf::MessageOptions* temp = options_; options_ = NULL; return temp; } - void DescriptorProto::set_allocated_options(::google::protobuf::MessageOptions* options) { +void DescriptorProto::set_allocated_options(::google::protobuf::MessageOptions* options) { delete options_; options_ = options; if (options) { @@ -3943,28 +3943,28 @@ int DescriptorProto::reserved_range_size() const { void DescriptorProto::clear_reserved_range() { reserved_range_.Clear(); } - const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { +const ::google::protobuf::DescriptorProto_ReservedRange& DescriptorProto::reserved_range(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.DescriptorProto.reserved_range) return reserved_range_.Get(index); } - ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { +::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::mutable_reserved_range(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.DescriptorProto.reserved_range) return reserved_range_.Mutable(index); } - ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { +::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_reserved_range() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) return reserved_range_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& -DescriptorProto::reserved_range() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) - return reserved_range_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* DescriptorProto::mutable_reserved_range() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) return &reserved_range_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} // repeated string reserved_name = 10; int DescriptorProto::reserved_name_size() const { @@ -5062,11 +5062,11 @@ void FieldDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); clear_has_options(); } - const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { +const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.FieldDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() { +::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::FieldOptions; @@ -5074,13 +5074,13 @@ void FieldDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.FieldDescriptorProto.options) return options_; } - ::google::protobuf::FieldOptions* FieldDescriptorProto::release_options() { +::google::protobuf::FieldOptions* FieldDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::FieldOptions* temp = options_; options_ = NULL; return temp; } - void FieldDescriptorProto::set_allocated_options(::google::protobuf::FieldOptions* options) { +void FieldDescriptorProto::set_allocated_options(::google::protobuf::FieldOptions* options) { delete options_; options_ = options; if (options) { @@ -5804,28 +5804,28 @@ int EnumDescriptorProto::value_size() const { void EnumDescriptorProto::clear_value() { value_.Clear(); } - const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { +const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.value) return value_.Get(index); } - ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::mutable_value(int index) { +::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::mutable_value(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.value) return value_.Mutable(index); } - ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::add_value() { +::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::add_value() { // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.value) return value_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& -EnumDescriptorProto::value() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.value) - return value_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* EnumDescriptorProto::mutable_value() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumDescriptorProto.value) return &value_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& +EnumDescriptorProto::value() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.value) + return value_; +} // optional .google.protobuf.EnumOptions options = 3; bool EnumDescriptorProto::has_options() const { @@ -5841,11 +5841,11 @@ void EnumDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); clear_has_options(); } - const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { +const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.EnumDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { +::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::EnumOptions; @@ -5853,13 +5853,13 @@ void EnumDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.EnumDescriptorProto.options) return options_; } - ::google::protobuf::EnumOptions* EnumDescriptorProto::release_options() { +::google::protobuf::EnumOptions* EnumDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::EnumOptions* temp = options_; options_ = NULL; return temp; } - void EnumDescriptorProto::set_allocated_options(::google::protobuf::EnumOptions* options) { +void EnumDescriptorProto::set_allocated_options(::google::protobuf::EnumOptions* options) { delete options_; options_ = options; if (options) { @@ -6304,11 +6304,11 @@ void EnumValueDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); clear_has_options(); } - const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() const { +const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.EnumValueDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_options() { +::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::EnumValueOptions; @@ -6316,13 +6316,13 @@ void EnumValueDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueDescriptorProto.options) return options_; } - ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::release_options() { +::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::EnumValueOptions* temp = options_; options_ = NULL; return temp; } - void EnumValueDescriptorProto::set_allocated_options(::google::protobuf::EnumValueOptions* options) { +void EnumValueDescriptorProto::set_allocated_options(::google::protobuf::EnumValueOptions* options) { delete options_; options_ = options; if (options) { @@ -6740,28 +6740,28 @@ int ServiceDescriptorProto::method_size() const { void ServiceDescriptorProto::clear_method() { method_.Clear(); } - const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { +const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.ServiceDescriptorProto.method) return method_.Get(index); } - ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::mutable_method(int index) { +::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::mutable_method(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.method) return method_.Mutable(index); } - ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::add_method() { +::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::add_method() { // @@protoc_insertion_point(field_add:google.protobuf.ServiceDescriptorProto.method) return method_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& -ServiceDescriptorProto::method() const { - // @@protoc_insertion_point(field_list:google.protobuf.ServiceDescriptorProto.method) - return method_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* ServiceDescriptorProto::mutable_method() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceDescriptorProto.method) return &method_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& +ServiceDescriptorProto::method() const { + // @@protoc_insertion_point(field_list:google.protobuf.ServiceDescriptorProto.method) + return method_; +} // optional .google.protobuf.ServiceOptions options = 3; bool ServiceDescriptorProto::has_options() const { @@ -6777,11 +6777,11 @@ void ServiceDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); clear_has_options(); } - const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() const { +const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.ServiceDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_options() { +::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::ServiceOptions; @@ -6789,13 +6789,13 @@ void ServiceDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceDescriptorProto.options) return options_; } - ::google::protobuf::ServiceOptions* ServiceDescriptorProto::release_options() { +::google::protobuf::ServiceOptions* ServiceDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::ServiceOptions* temp = options_; options_ = NULL; return temp; } - void ServiceDescriptorProto::set_allocated_options(::google::protobuf::ServiceOptions* options) { +void ServiceDescriptorProto::set_allocated_options(::google::protobuf::ServiceOptions* options) { delete options_; options_ = options; if (options) { @@ -7480,11 +7480,11 @@ void MethodDescriptorProto::clear_options() { if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); clear_has_options(); } - const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const { +const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const { // @@protoc_insertion_point(field_get:google.protobuf.MethodDescriptorProto.options) return options_ != NULL ? *options_ : *default_instance_->options_; } - ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options() { +::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options() { set_has_options(); if (options_ == NULL) { options_ = new ::google::protobuf::MethodOptions; @@ -7492,13 +7492,13 @@ void MethodDescriptorProto::clear_options() { // @@protoc_insertion_point(field_mutable:google.protobuf.MethodDescriptorProto.options) return options_; } - ::google::protobuf::MethodOptions* MethodDescriptorProto::release_options() { +::google::protobuf::MethodOptions* MethodDescriptorProto::release_options() { clear_has_options(); ::google::protobuf::MethodOptions* temp = options_; options_ = NULL; return temp; } - void MethodDescriptorProto::set_allocated_options(::google::protobuf::MethodOptions* options) { +void MethodDescriptorProto::set_allocated_options(::google::protobuf::MethodOptions* options) { delete options_; options_ = options; if (options) { @@ -9025,28 +9025,28 @@ int FileOptions::uninterpreted_option_size() const { void FileOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& FileOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& FileOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FileOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* FileOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* FileOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FileOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* FileOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* FileOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.FileOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -FileOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* FileOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +FileOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -9568,28 +9568,28 @@ int MessageOptions::uninterpreted_option_size() const { void MessageOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& MessageOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& MessageOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.MessageOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* MessageOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* MessageOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.MessageOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* MessageOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* MessageOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.MessageOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -MessageOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.MessageOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* MessageOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.MessageOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +MessageOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.MessageOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -10298,28 +10298,28 @@ int FieldOptions::uninterpreted_option_size() const { void FieldOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& FieldOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& FieldOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.FieldOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* FieldOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* FieldOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.FieldOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* FieldOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* FieldOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.FieldOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -FieldOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.FieldOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* FieldOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FieldOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +FieldOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.FieldOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -10721,28 +10721,28 @@ int EnumOptions::uninterpreted_option_size() const { void EnumOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& EnumOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& EnumOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.EnumOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* EnumOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* EnumOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.EnumOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* EnumOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* EnumOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.EnumOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -EnumOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* EnumOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +EnumOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -11070,28 +11070,28 @@ int EnumValueOptions::uninterpreted_option_size() const { void EnumValueOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& EnumValueOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& EnumValueOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.EnumValueOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* EnumValueOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* EnumValueOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.EnumValueOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* EnumValueOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* EnumValueOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.EnumValueOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -EnumValueOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumValueOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* EnumValueOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumValueOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +EnumValueOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumValueOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -11419,28 +11419,28 @@ int ServiceOptions::uninterpreted_option_size() const { void ServiceOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& ServiceOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& ServiceOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.ServiceOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* ServiceOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* ServiceOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.ServiceOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* ServiceOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* ServiceOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.ServiceOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -ServiceOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.ServiceOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* ServiceOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +ServiceOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.ServiceOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -11768,28 +11768,28 @@ int MethodOptions::uninterpreted_option_size() const { void MethodOptions::clear_uninterpreted_option() { uninterpreted_option_.Clear(); } - const ::google::protobuf::UninterpretedOption& MethodOptions::uninterpreted_option(int index) const { +const ::google::protobuf::UninterpretedOption& MethodOptions::uninterpreted_option(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.MethodOptions.uninterpreted_option) return uninterpreted_option_.Get(index); } - ::google::protobuf::UninterpretedOption* MethodOptions::mutable_uninterpreted_option(int index) { +::google::protobuf::UninterpretedOption* MethodOptions::mutable_uninterpreted_option(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.MethodOptions.uninterpreted_option) return uninterpreted_option_.Mutable(index); } - ::google::protobuf::UninterpretedOption* MethodOptions::add_uninterpreted_option() { +::google::protobuf::UninterpretedOption* MethodOptions::add_uninterpreted_option() { // @@protoc_insertion_point(field_add:google.protobuf.MethodOptions.uninterpreted_option) return uninterpreted_option_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -MethodOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.MethodOptions.uninterpreted_option) - return uninterpreted_option_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* MethodOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.MethodOptions.uninterpreted_option) return &uninterpreted_option_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +MethodOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.MethodOptions.uninterpreted_option) + return uninterpreted_option_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS @@ -12710,28 +12710,28 @@ int UninterpretedOption::name_size() const { void UninterpretedOption::clear_name() { name_.Clear(); } - const ::google::protobuf::UninterpretedOption_NamePart& UninterpretedOption::name(int index) const { +const ::google::protobuf::UninterpretedOption_NamePart& UninterpretedOption::name(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.UninterpretedOption.name) return name_.Get(index); } - ::google::protobuf::UninterpretedOption_NamePart* UninterpretedOption::mutable_name(int index) { +::google::protobuf::UninterpretedOption_NamePart* UninterpretedOption::mutable_name(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.UninterpretedOption.name) return name_.Mutable(index); } - ::google::protobuf::UninterpretedOption_NamePart* UninterpretedOption::add_name() { +::google::protobuf::UninterpretedOption_NamePart* UninterpretedOption::add_name() { // @@protoc_insertion_point(field_add:google.protobuf.UninterpretedOption.name) return name_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& -UninterpretedOption::name() const { - // @@protoc_insertion_point(field_list:google.protobuf.UninterpretedOption.name) - return name_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >* UninterpretedOption::mutable_name() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.UninterpretedOption.name) return &name_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& +UninterpretedOption::name() const { + // @@protoc_insertion_point(field_list:google.protobuf.UninterpretedOption.name) + return name_; +} // optional string identifier_value = 3; bool UninterpretedOption::has_identifier_value() const { @@ -13916,28 +13916,28 @@ int SourceCodeInfo::location_size() const { void SourceCodeInfo::clear_location() { location_.Clear(); } - const ::google::protobuf::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const { +const ::google::protobuf::SourceCodeInfo_Location& SourceCodeInfo::location(int index) const { // @@protoc_insertion_point(field_get:google.protobuf.SourceCodeInfo.location) return location_.Get(index); } - ::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::mutable_location(int index) { +::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::mutable_location(int index) { // @@protoc_insertion_point(field_mutable:google.protobuf.SourceCodeInfo.location) return location_.Mutable(index); } - ::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::add_location() { +::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::add_location() { // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.location) return location_.Add(); } - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& -SourceCodeInfo::location() const { - // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.location) - return location_; -} - ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* +::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* SourceCodeInfo::mutable_location() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.location) return &location_; } +const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& +SourceCodeInfo::location() const { + // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.location) + return location_; +} #endif // PROTOBUF_INLINE_NOT_IN_HEADERS diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h index 2aa076ae34..931ff02d61 100644 --- a/src/google/protobuf/descriptor.pb.h +++ b/src/google/protobuf/descriptor.pb.h @@ -38,28 +38,28 @@ void LIBPROTOBUF_EXPORT protobuf_AddDesc_google_2fprotobuf_2fdescriptor_2eproto( void protobuf_AssignDesc_google_2fprotobuf_2fdescriptor_2eproto(); void protobuf_ShutdownFile_google_2fprotobuf_2fdescriptor_2eproto(); -class FileDescriptorSet; -class FileDescriptorProto; class DescriptorProto; class DescriptorProto_ExtensionRange; class DescriptorProto_ReservedRange; -class FieldDescriptorProto; -class OneofDescriptorProto; class EnumDescriptorProto; +class EnumOptions; class EnumValueDescriptorProto; -class ServiceDescriptorProto; -class MethodDescriptorProto; +class EnumValueOptions; +class FieldDescriptorProto; +class FieldOptions; +class FileDescriptorProto; +class FileDescriptorSet; class FileOptions; class MessageOptions; -class FieldOptions; -class EnumOptions; -class EnumValueOptions; -class ServiceOptions; +class MethodDescriptorProto; class MethodOptions; -class UninterpretedOption; -class UninterpretedOption_NamePart; +class OneofDescriptorProto; +class ServiceDescriptorProto; +class ServiceOptions; class SourceCodeInfo; class SourceCodeInfo_Location; +class UninterpretedOption; +class UninterpretedOption_NamePart; enum FieldDescriptorProto_Type { FieldDescriptorProto_Type_TYPE_DOUBLE = 1, @@ -249,10 +249,10 @@ class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message const ::google::protobuf::FileDescriptorProto& file(int index) const; ::google::protobuf::FileDescriptorProto* mutable_file(int index); ::google::protobuf::FileDescriptorProto* add_file(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& - file() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* mutable_file(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& + file() const; // @@protoc_insertion_point(class_scope:google.protobuf.FileDescriptorSet) private: @@ -405,10 +405,10 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag const ::google::protobuf::DescriptorProto& message_type(int index) const; ::google::protobuf::DescriptorProto* mutable_message_type(int index); ::google::protobuf::DescriptorProto* add_message_type(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& - message_type() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_message_type(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& + message_type() const; // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; int enum_type_size() const; @@ -417,10 +417,10 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); ::google::protobuf::EnumDescriptorProto* add_enum_type(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& - enum_type() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& + enum_type() const; // repeated .google.protobuf.ServiceDescriptorProto service = 6; int service_size() const; @@ -429,10 +429,10 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag const ::google::protobuf::ServiceDescriptorProto& service(int index) const; ::google::protobuf::ServiceDescriptorProto* mutable_service(int index); ::google::protobuf::ServiceDescriptorProto* add_service(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& - service() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* mutable_service(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& + service() const; // repeated .google.protobuf.FieldDescriptorProto extension = 7; int extension_size() const; @@ -441,10 +441,10 @@ class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Messag const ::google::protobuf::FieldDescriptorProto& extension(int index) const; ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); ::google::protobuf::FieldDescriptorProto* add_extension(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& - extension() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& + extension() const; // optional .google.protobuf.FileOptions options = 8; bool has_options() const; @@ -797,10 +797,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::FieldDescriptorProto& field(int index) const; ::google::protobuf::FieldDescriptorProto* mutable_field(int index); ::google::protobuf::FieldDescriptorProto* add_field(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& - field() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_field(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& + field() const; // repeated .google.protobuf.FieldDescriptorProto extension = 6; int extension_size() const; @@ -809,10 +809,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::FieldDescriptorProto& extension(int index) const; ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); ::google::protobuf::FieldDescriptorProto* add_extension(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& - extension() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& + extension() const; // repeated .google.protobuf.DescriptorProto nested_type = 3; int nested_type_size() const; @@ -821,10 +821,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::DescriptorProto& nested_type(int index) const; ::google::protobuf::DescriptorProto* mutable_nested_type(int index); ::google::protobuf::DescriptorProto* add_nested_type(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& - nested_type() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_nested_type(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& + nested_type() const; // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; int enum_type_size() const; @@ -833,10 +833,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); ::google::protobuf::EnumDescriptorProto* add_enum_type(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& - enum_type() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& + enum_type() const; // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; int extension_range_size() const; @@ -845,10 +845,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::DescriptorProto_ExtensionRange& extension_range(int index) const; ::google::protobuf::DescriptorProto_ExtensionRange* mutable_extension_range(int index); ::google::protobuf::DescriptorProto_ExtensionRange* add_extension_range(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& - extension_range() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* mutable_extension_range(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& + extension_range() const; // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8; int oneof_decl_size() const; @@ -857,10 +857,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::OneofDescriptorProto& oneof_decl(int index) const; ::google::protobuf::OneofDescriptorProto* mutable_oneof_decl(int index); ::google::protobuf::OneofDescriptorProto* add_oneof_decl(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& - oneof_decl() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >* mutable_oneof_decl(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& + oneof_decl() const; // optional .google.protobuf.MessageOptions options = 7; bool has_options() const; @@ -878,10 +878,10 @@ class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { const ::google::protobuf::DescriptorProto_ReservedRange& reserved_range(int index) const; ::google::protobuf::DescriptorProto_ReservedRange* mutable_reserved_range(int index); ::google::protobuf::DescriptorProto_ReservedRange* add_reserved_range(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& - reserved_range() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* mutable_reserved_range(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& + reserved_range() const; // repeated string reserved_name = 10; int reserved_name_size() const; @@ -1361,10 +1361,10 @@ class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Messag const ::google::protobuf::EnumValueDescriptorProto& value(int index) const; ::google::protobuf::EnumValueDescriptorProto* mutable_value(int index); ::google::protobuf::EnumValueDescriptorProto* add_value(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& - value() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* mutable_value(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& + value() const; // optional .google.protobuf.EnumOptions options = 3; bool has_options() const; @@ -1596,10 +1596,10 @@ class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Mes const ::google::protobuf::MethodDescriptorProto& method(int index) const; ::google::protobuf::MethodDescriptorProto* mutable_method(int index); ::google::protobuf::MethodDescriptorProto* add_method(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& - method() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* mutable_method(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& + method() const; // optional .google.protobuf.ServiceOptions options = 3; bool has_options() const; @@ -2014,10 +2014,10 @@ class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(FileOptions) // @@protoc_insertion_point(class_scope:google.protobuf.FileOptions) @@ -2182,10 +2182,10 @@ class LIBPROTOBUF_EXPORT MessageOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(MessageOptions) // @@protoc_insertion_point(class_scope:google.protobuf.MessageOptions) @@ -2381,10 +2381,10 @@ class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(FieldOptions) // @@protoc_insertion_point(class_scope:google.protobuf.FieldOptions) @@ -2508,10 +2508,10 @@ class LIBPROTOBUF_EXPORT EnumOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(EnumOptions) // @@protoc_insertion_point(class_scope:google.protobuf.EnumOptions) @@ -2616,10 +2616,10 @@ class LIBPROTOBUF_EXPORT EnumValueOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(EnumValueOptions) // @@protoc_insertion_point(class_scope:google.protobuf.EnumValueOptions) @@ -2721,10 +2721,10 @@ class LIBPROTOBUF_EXPORT ServiceOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(ServiceOptions) // @@protoc_insertion_point(class_scope:google.protobuf.ServiceOptions) @@ -2826,10 +2826,10 @@ class LIBPROTOBUF_EXPORT MethodOptions : public ::google::protobuf::Message { const ::google::protobuf::UninterpretedOption& uninterpreted_option(int index) const; ::google::protobuf::UninterpretedOption* mutable_uninterpreted_option(int index); ::google::protobuf::UninterpretedOption* add_uninterpreted_option(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& - uninterpreted_option() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* mutable_uninterpreted_option(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& + uninterpreted_option() const; GOOGLE_PROTOBUF_EXTENSION_ACCESSORS(MethodOptions) // @@protoc_insertion_point(class_scope:google.protobuf.MethodOptions) @@ -3033,10 +3033,10 @@ class LIBPROTOBUF_EXPORT UninterpretedOption : public ::google::protobuf::Messag const ::google::protobuf::UninterpretedOption_NamePart& name(int index) const; ::google::protobuf::UninterpretedOption_NamePart* mutable_name(int index); ::google::protobuf::UninterpretedOption_NamePart* add_name(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& - name() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >* mutable_name(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& + name() const; // optional string identifier_value = 3; bool has_identifier_value() const; @@ -3356,10 +3356,10 @@ class LIBPROTOBUF_EXPORT SourceCodeInfo : public ::google::protobuf::Message { const ::google::protobuf::SourceCodeInfo_Location& location(int index) const; ::google::protobuf::SourceCodeInfo_Location* mutable_location(int index); ::google::protobuf::SourceCodeInfo_Location* add_location(); - const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& - location() const; ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* mutable_location(); + const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& + location() const; // @@protoc_insertion_point(class_scope:google.protobuf.SourceCodeInfo) private: @@ -3402,16 +3402,16 @@ inline ::google::protobuf::FileDescriptorProto* FileDescriptorSet::add_file() { // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorSet.file) return file_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& -FileDescriptorSet::file() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorSet.file) - return file_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* FileDescriptorSet::mutable_file() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorSet.file) return &file_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& +FileDescriptorSet::file() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorSet.file) + return file_; +} // ------------------------------------------------------------------- @@ -3656,16 +3656,16 @@ inline ::google::protobuf::DescriptorProto* FileDescriptorProto::add_message_typ // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.message_type) return message_type_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& -FileDescriptorProto::message_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.message_type) - return message_type_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* FileDescriptorProto::mutable_message_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.message_type) return &message_type_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +FileDescriptorProto::message_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.message_type) + return message_type_; +} // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; inline int FileDescriptorProto::enum_type_size() const { @@ -3686,16 +3686,16 @@ inline ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::add_enum_ty // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.enum_type) return enum_type_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& -FileDescriptorProto::enum_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.enum_type) - return enum_type_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* FileDescriptorProto::mutable_enum_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.enum_type) return &enum_type_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +FileDescriptorProto::enum_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.enum_type) + return enum_type_; +} // repeated .google.protobuf.ServiceDescriptorProto service = 6; inline int FileDescriptorProto::service_size() const { @@ -3716,16 +3716,16 @@ inline ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::add_serv // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.service) return service_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& -FileDescriptorProto::service() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.service) - return service_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* FileDescriptorProto::mutable_service() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.service) return &service_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& +FileDescriptorProto::service() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.service) + return service_; +} // repeated .google.protobuf.FieldDescriptorProto extension = 7; inline int FileDescriptorProto::extension_size() const { @@ -3746,16 +3746,16 @@ inline ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::add_extens // @@protoc_insertion_point(field_add:google.protobuf.FileDescriptorProto.extension) return extension_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -FileDescriptorProto::extension() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.extension) - return extension_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* FileDescriptorProto::mutable_extension() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileDescriptorProto.extension) return &extension_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +FileDescriptorProto::extension() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileDescriptorProto.extension) + return extension_; +} // optional .google.protobuf.FileOptions options = 8; inline bool FileDescriptorProto::has_options() const { @@ -4076,16 +4076,16 @@ inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_field() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.field) return field_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -DescriptorProto::field() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.field) - return field_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* DescriptorProto::mutable_field() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.field) return &field_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::field() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.field) + return field_; +} // repeated .google.protobuf.FieldDescriptorProto extension = 6; inline int DescriptorProto::extension_size() const { @@ -4106,16 +4106,16 @@ inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_extension( // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension) return extension_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& -DescriptorProto::extension() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension) - return extension_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* DescriptorProto::mutable_extension() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension) return &extension_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::extension() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension) + return extension_; +} // repeated .google.protobuf.DescriptorProto nested_type = 3; inline int DescriptorProto::nested_type_size() const { @@ -4136,16 +4136,16 @@ inline ::google::protobuf::DescriptorProto* DescriptorProto::add_nested_type() { // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.nested_type) return nested_type_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& -DescriptorProto::nested_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.nested_type) - return nested_type_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* DescriptorProto::mutable_nested_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.nested_type) return &nested_type_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +DescriptorProto::nested_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.nested_type) + return nested_type_; +} // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; inline int DescriptorProto::enum_type_size() const { @@ -4166,16 +4166,16 @@ inline ::google::protobuf::EnumDescriptorProto* DescriptorProto::add_enum_type() // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.enum_type) return enum_type_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& -DescriptorProto::enum_type() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.enum_type) - return enum_type_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* DescriptorProto::mutable_enum_type() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.enum_type) return &enum_type_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +DescriptorProto::enum_type() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.enum_type) + return enum_type_; +} // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; inline int DescriptorProto::extension_range_size() const { @@ -4196,16 +4196,16 @@ inline ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::add_ // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.extension_range) return extension_range_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& -DescriptorProto::extension_range() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension_range) - return extension_range_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* DescriptorProto::mutable_extension_range() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.extension_range) return &extension_range_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& +DescriptorProto::extension_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.extension_range) + return extension_range_; +} // repeated .google.protobuf.OneofDescriptorProto oneof_decl = 8; inline int DescriptorProto::oneof_decl_size() const { @@ -4226,16 +4226,16 @@ inline ::google::protobuf::OneofDescriptorProto* DescriptorProto::add_oneof_decl // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.oneof_decl) return oneof_decl_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& -DescriptorProto::oneof_decl() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.oneof_decl) - return oneof_decl_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >* DescriptorProto::mutable_oneof_decl() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.oneof_decl) return &oneof_decl_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::OneofDescriptorProto >& +DescriptorProto::oneof_decl() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.oneof_decl) + return oneof_decl_; +} // optional .google.protobuf.MessageOptions options = 7; inline bool DescriptorProto::has_options() const { @@ -4299,16 +4299,16 @@ inline ::google::protobuf::DescriptorProto_ReservedRange* DescriptorProto::add_r // @@protoc_insertion_point(field_add:google.protobuf.DescriptorProto.reserved_range) return reserved_range_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& -DescriptorProto::reserved_range() const { - // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) - return reserved_range_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >* DescriptorProto::mutable_reserved_range() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.DescriptorProto.reserved_range) return &reserved_range_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ReservedRange >& +DescriptorProto::reserved_range() const { + // @@protoc_insertion_point(field_list:google.protobuf.DescriptorProto.reserved_range) + return reserved_range_; +} // repeated string reserved_name = 10; inline int DescriptorProto::reserved_name_size() const { @@ -4854,16 +4854,16 @@ inline ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::add_va // @@protoc_insertion_point(field_add:google.protobuf.EnumDescriptorProto.value) return value_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& -EnumDescriptorProto::value() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.value) - return value_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* EnumDescriptorProto::mutable_value() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumDescriptorProto.value) return &value_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& +EnumDescriptorProto::value() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumDescriptorProto.value) + return value_; +} // optional .google.protobuf.EnumOptions options = 3; inline bool EnumDescriptorProto::has_options() const { @@ -5108,16 +5108,16 @@ inline ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::add_me // @@protoc_insertion_point(field_add:google.protobuf.ServiceDescriptorProto.method) return method_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& -ServiceDescriptorProto::method() const { - // @@protoc_insertion_point(field_list:google.protobuf.ServiceDescriptorProto.method) - return method_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* ServiceDescriptorProto::mutable_method() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceDescriptorProto.method) return &method_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& +ServiceDescriptorProto::method() const { + // @@protoc_insertion_point(field_list:google.protobuf.ServiceDescriptorProto.method) + return method_; +} // optional .google.protobuf.ServiceOptions options = 3; inline bool ServiceDescriptorProto::has_options() const { @@ -5945,16 +5945,16 @@ inline ::google::protobuf::UninterpretedOption* FileOptions::add_uninterpreted_o // @@protoc_insertion_point(field_add:google.protobuf.FileOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -FileOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.FileOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* FileOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FileOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +FileOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.FileOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6075,16 +6075,16 @@ inline ::google::protobuf::UninterpretedOption* MessageOptions::add_uninterprete // @@protoc_insertion_point(field_add:google.protobuf.MessageOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -MessageOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.MessageOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* MessageOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.MessageOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +MessageOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.MessageOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6255,16 +6255,16 @@ inline ::google::protobuf::UninterpretedOption* FieldOptions::add_uninterpreted_ // @@protoc_insertion_point(field_add:google.protobuf.FieldOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -FieldOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.FieldOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* FieldOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.FieldOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +FieldOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.FieldOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6337,16 +6337,16 @@ inline ::google::protobuf::UninterpretedOption* EnumOptions::add_uninterpreted_o // @@protoc_insertion_point(field_add:google.protobuf.EnumOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -EnumOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* EnumOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +EnumOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6395,16 +6395,16 @@ inline ::google::protobuf::UninterpretedOption* EnumValueOptions::add_uninterpre // @@protoc_insertion_point(field_add:google.protobuf.EnumValueOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -EnumValueOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.EnumValueOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* EnumValueOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.EnumValueOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +EnumValueOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.EnumValueOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6453,16 +6453,16 @@ inline ::google::protobuf::UninterpretedOption* ServiceOptions::add_uninterprete // @@protoc_insertion_point(field_add:google.protobuf.ServiceOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -ServiceOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.ServiceOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* ServiceOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.ServiceOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +ServiceOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.ServiceOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6511,16 +6511,16 @@ inline ::google::protobuf::UninterpretedOption* MethodOptions::add_uninterpreted // @@protoc_insertion_point(field_add:google.protobuf.MethodOptions.uninterpreted_option) return uninterpreted_option_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& -MethodOptions::uninterpreted_option() const { - // @@protoc_insertion_point(field_list:google.protobuf.MethodOptions.uninterpreted_option) - return uninterpreted_option_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >* MethodOptions::mutable_uninterpreted_option() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.MethodOptions.uninterpreted_option) return &uninterpreted_option_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption >& +MethodOptions::uninterpreted_option() const { + // @@protoc_insertion_point(field_list:google.protobuf.MethodOptions.uninterpreted_option) + return uninterpreted_option_; +} // ------------------------------------------------------------------- @@ -6626,16 +6626,16 @@ inline ::google::protobuf::UninterpretedOption_NamePart* UninterpretedOption::ad // @@protoc_insertion_point(field_add:google.protobuf.UninterpretedOption.name) return name_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& -UninterpretedOption::name() const { - // @@protoc_insertion_point(field_list:google.protobuf.UninterpretedOption.name) - return name_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >* UninterpretedOption::mutable_name() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.UninterpretedOption.name) return &name_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::UninterpretedOption_NamePart >& +UninterpretedOption::name() const { + // @@protoc_insertion_point(field_list:google.protobuf.UninterpretedOption.name) + return name_; +} // optional string identifier_value = 3; inline bool UninterpretedOption::has_identifier_value() const { @@ -7115,16 +7115,16 @@ inline ::google::protobuf::SourceCodeInfo_Location* SourceCodeInfo::add_location // @@protoc_insertion_point(field_add:google.protobuf.SourceCodeInfo.location) return location_.Add(); } -inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& -SourceCodeInfo::location() const { - // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.location) - return location_; -} inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >* SourceCodeInfo::mutable_location() { // @@protoc_insertion_point(field_mutable_list:google.protobuf.SourceCodeInfo.location) return &location_; } +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::SourceCodeInfo_Location >& +SourceCodeInfo::location() const { + // @@protoc_insertion_point(field_list:google.protobuf.SourceCodeInfo.location) + return location_; +} #endif // !PROTOBUF_INLINE_NOT_IN_HEADERS // ------------------------------------------------------------------- diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index 9d3dd8fbae..8f90a956cf 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -42,9 +42,9 @@ syntax = "proto2"; package google.protobuf; option go_package = "descriptor"; option java_package = "com.google.protobuf"; -option javanano_use_deprecated_package = true; option java_outer_classname = "DescriptorProtos"; -option csharp_namespace = "Google.Protobuf.Reflection"; +// Re-enable this once the tools have picked up the csharp_namespace option. +// option csharp_namespace = "Google.ProtocolBuffers.DescriptorProtos"; option objc_class_prefix = "GPB"; // descriptor.proto must be optimized for speed because reflection-based @@ -301,10 +301,12 @@ message FileOptions { // If set true, then the Java code generator will generate equals() and // hashCode() methods for all messages defined in the .proto file. - // - In the full runtime, this is purely a speed optimization, as the + // This increases generated code size, potentially substantially for large + // protos, which may harm a memory-constrained application. + // - In the full runtime this is a speed optimization, as the // AbstractMessage base class includes reflection-based implementations of // these methods. - //- In the lite runtime, setting this option changes the semantics of + // - In the lite runtime, setting this option changes the semantics of // equals() and hashCode() to more closely match those of the full runtime; // the generated methods compute their results based on field values rather // than object identity. (Implementations should not assume that hashcodes @@ -525,6 +527,7 @@ message FieldOptions { // For Google-internal migration only. Do not use. optional bool weak = 10 [default=false]; + // The parser stores options it doesn't recognize here. See above. repeated UninterpretedOption uninterpreted_option = 999; diff --git a/src/google/protobuf/descriptor_database_unittest.cc b/src/google/protobuf/descriptor_database_unittest.cc index 1c03c4438e..a87fa04915 100644 --- a/src/google/protobuf/descriptor_database_unittest.cc +++ b/src/google/protobuf/descriptor_database_unittest.cc @@ -42,7 +42,9 @@ #include #include +#include #include +#include #include #include diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index 760df097e6..e9b027dbb6 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -49,6 +49,8 @@ #include #include +#include +#include #include #include @@ -368,6 +370,37 @@ TEST_F(FileDescriptorTest, BuildAgain) { EXPECT_TRUE(pool_.BuildFile(file) == NULL); } +TEST_F(FileDescriptorTest, BuildAgainWithSyntax) { + // Test that if te call BuildFile again on the same input we get the same + // FileDescriptor back even if syntax param is specified. + FileDescriptorProto proto_syntax2; + proto_syntax2.set_name("foo_syntax2"); + proto_syntax2.set_syntax("proto2"); + + const FileDescriptor* proto2_descriptor = pool_.BuildFile(proto_syntax2); + EXPECT_TRUE(proto2_descriptor != NULL); + EXPECT_EQ(proto2_descriptor, pool_.BuildFile(proto_syntax2)); + + FileDescriptorProto implicit_proto2; + implicit_proto2.set_name("foo_implicit_syntax2"); + + const FileDescriptor* implicit_proto2_descriptor = + pool_.BuildFile(implicit_proto2); + EXPECT_TRUE(implicit_proto2_descriptor != NULL); + // We get the same FileDescriptor back if syntax param is explicitly + // specified. + implicit_proto2.set_syntax("proto2"); + EXPECT_EQ(implicit_proto2_descriptor, pool_.BuildFile(implicit_proto2)); + + FileDescriptorProto proto_syntax3; + proto_syntax3.set_name("foo_syntax3"); + proto_syntax3.set_syntax("proto3"); + + const FileDescriptor* proto3_descriptor = pool_.BuildFile(proto_syntax3); + EXPECT_TRUE(proto3_descriptor != NULL); + EXPECT_EQ(proto3_descriptor, pool_.BuildFile(proto_syntax3)); +} + TEST_F(FileDescriptorTest, Syntax) { FileDescriptorProto proto; proto.set_name("foo"); @@ -3583,7 +3616,7 @@ TEST_F(ValidationErrorTest, FieldOneofIndexNegative) { TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) { // Fields belonging to the same oneof must be defined consecutively. - BuildFileWithWarnings( + BuildFileWithErrors( "name: \"foo.proto\" " "message_type {" " name: \"Foo\"" @@ -3600,7 +3633,7 @@ TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) { "\"foos\" oneof definition.\n"); // Prevent interleaved fields, which belong to different oneofs. - BuildFileWithWarnings( + BuildFileWithErrors( "name: \"foo2.proto\" " "message_type {" " name: \"Foo2\"" @@ -3621,6 +3654,25 @@ TEST_F(ValidationErrorTest, OneofFieldsConsecutiveDefinition) { "foo2.proto: Foo2.foo2: OTHER: Fields in the same oneof must be defined " "consecutively. \"foo2\" cannot be defined before the completion of the " "\"bars\" oneof definition.\n"); + + // Another case for normal fields and different oneof fields interleave. + BuildFileWithErrors( + "name: \"foo3.proto\" " + "message_type {" + " name: \"Foo3\"" + " field { name:\"foo1\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " field { name:\"bar1\" number: 2 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 1 }" + " field { name:\"baz\" number: 3 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name:\"foo2\" number: 4 label:LABEL_OPTIONAL type:TYPE_INT32 " + " oneof_index: 0 }" + " oneof_decl { name:\"foos\" }" + " oneof_decl { name:\"bars\" }" + "}", + "foo3.proto: Foo3.baz: OTHER: Fields in the same oneof must be defined " + "consecutively. \"baz\" cannot be defined before the completion of the " + "\"foos\" oneof definition.\n"); } TEST_F(ValidationErrorTest, FieldNumberConflict) { @@ -5369,6 +5421,35 @@ TEST_F(ValidationErrorTest, MapEntryConflictsWithOneof) { "with an existing oneof type.\n"); } +TEST_F(ValidationErrorTest, MapEntryUsesNoneZeroEnumDefaultValue) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type {" + " name: \"Bar\"" + " value { name:\"ENUM_A\" number:1 }" + " value { name:\"ENUM_B\" number:2 }" + "}" + "message_type {" + " name: 'Foo' " + " field { " + " name: 'foo_map' number: 1 label:LABEL_REPEATED " + " type_name: 'FooMapEntry' " + " } " + " nested_type { " + " name: 'FooMapEntry' " + " options { map_entry: true } " + " field { " + " name: 'key' number: 1 type:TYPE_INT32 label:LABEL_OPTIONAL " + " } " + " field { " + " name: 'value' number: 2 type_name:\"Bar\" label:LABEL_OPTIONAL " + " } " + " } " + "}", + "foo.proto: Foo.foo_map: " + "TYPE: Enum value in map must define 0 as the first value.\n"); +} + TEST_F(ValidationErrorTest, Proto3RequiredFields) { BuildFileWithErrors( "name: 'foo.proto' " diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc index 318ce6f9fb..2324d9525d 100644 --- a/src/google/protobuf/dynamic_message.cc +++ b/src/google/protobuf/dynamic_message.cc @@ -65,6 +65,7 @@ #include #include +#include #include #include @@ -79,6 +80,7 @@ #include #include #include +#include namespace google { namespace protobuf { @@ -87,7 +89,7 @@ using internal::WireFormat; using internal::ExtensionSet; using internal::GeneratedMessageReflection; using internal::MapField; -using internal::MapFieldBase; +using internal::DynamicMapField; using internal::ArenaStringPtr; @@ -116,7 +118,7 @@ int FieldSpaceUsed(const FieldDescriptor* field) { case FD::CPPTYPE_ENUM : return sizeof(RepeatedField); case FD::CPPTYPE_MESSAGE: if (IsMapFieldInApi(field)) { - return sizeof(MapFieldBase); + return sizeof(DynamicMapField); } else { return sizeof(RepeatedPtrField); } @@ -389,7 +391,8 @@ void DynamicMessage::SharedCtor() { new(field_ptr) Message*(NULL); } else { if (IsMapFieldInApi(field)) { - new (field_ptr) MapFieldBase(); + new (field_ptr) DynamicMapField( + type_info_->factory->GetPrototypeNoLock(field->message_type())); } else { new (field_ptr) RepeatedPtrField(); } @@ -437,8 +440,8 @@ DynamicMessage::~DynamicMessage() { &(reinterpret_cast( type_info_->prototype->OffsetToPointer( type_info_->offsets[i]))->Get(NULL)); - reinterpret_cast(field_ptr)->Destroy(default_value, - NULL); + reinterpret_cast(field_ptr)->Destroy( + default_value, NULL); break; } } @@ -480,7 +483,7 @@ DynamicMessage::~DynamicMessage() { case FieldDescriptor::CPPTYPE_MESSAGE: if (IsMapFieldInApi(field)) { - reinterpret_cast(field_ptr)->~MapFieldBase(); + reinterpret_cast(field_ptr)->~DynamicMapField(); } else { reinterpret_cast*>(field_ptr) ->~RepeatedPtrField(); @@ -496,8 +499,8 @@ DynamicMessage::~DynamicMessage() { &(reinterpret_cast( type_info_->prototype->OffsetToPointer( type_info_->offsets[i]))->Get(NULL)); - reinterpret_cast(field_ptr)->Destroy(default_value, - NULL); + reinterpret_cast(field_ptr)->Destroy( + default_value, NULL); break; } } @@ -723,8 +726,14 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( // Allocate the prototype. void* base = operator new(size); memset(base, 0, size); + // The prototype in type_info has to be set before creating the prototype + // instance on memory. e.g., message Foo { map a = 1; }. When + // creating prototype for Foo, prototype of the map entry will also be + // created, which needs the address of the prototype of Foo (the value in + // map). To break the cyclic dependency, we have to assgin the address of + // prototype into type_info first. + type_info->prototype = static_cast(base); DynamicMessage* prototype = new(base) DynamicMessage(type_info); - type_info->prototype = prototype; // Construct the reflection object. if (type->oneof_decl_count() > 0) { diff --git a/src/google/protobuf/dynamic_message.h b/src/google/protobuf/dynamic_message.h index b5928a5260..f74cd7dd25 100644 --- a/src/google/protobuf/dynamic_message.h +++ b/src/google/protobuf/dynamic_message.h @@ -45,6 +45,7 @@ #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc index 522a092aa8..b9796c764f 100644 --- a/src/google/protobuf/dynamic_message_unittest.cc +++ b/src/google/protobuf/dynamic_message_unittest.cc @@ -40,6 +40,7 @@ // reflection_ops_unittest, cover the rest of the functionality used by // DynamicMessage. +#include #include #include #include @@ -48,6 +49,7 @@ #include #include +#include #include #include diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h index c371e011e7..9ef4e27d10 100644 --- a/src/google/protobuf/extension_set.h +++ b/src/google/protobuf/extension_set.h @@ -45,6 +45,7 @@ #include +#include #include @@ -330,6 +331,8 @@ class LIBPROTOBUF_EXPORT ExtensionSet { const MessageLite& prototype, desc); MessageLite* AddMessage(const FieldDescriptor* descriptor, MessageFactory* factory); + void AddAllocatedMessage(const FieldDescriptor* descriptor, + MessageLite* new_entry); #undef desc void RemoveLast(int number); @@ -578,6 +581,10 @@ class LIBPROTOBUF_EXPORT ExtensionSet { bool MaybeNewExtension(int number, const FieldDescriptor* descriptor, Extension** result); + // Gets the repeated extension for the given descriptor, creating it if + // it does not exist. + Extension* MaybeNewRepeatedExtension(const FieldDescriptor* descriptor); + // Parse a single MessageSet item -- called just after the item group start // tag has been read. bool ParseMessageSetItem(io::CodedInputStream* input, diff --git a/src/google/protobuf/extension_set_heavy.cc b/src/google/protobuf/extension_set_heavy.cc index 330bd8287d..82e3e09963 100644 --- a/src/google/protobuf/extension_set_heavy.cc +++ b/src/google/protobuf/extension_set_heavy.cc @@ -213,8 +213,7 @@ MessageLite* ExtensionSet::ReleaseMessage(const FieldDescriptor* descriptor, } } -MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, - MessageFactory* factory) { +ExtensionSet::Extension* ExtensionSet::MaybeNewRepeatedExtension(const FieldDescriptor* descriptor) { Extension* extension; if (MaybeNewExtension(descriptor->number(), descriptor, &extension)) { extension->type = descriptor->type(); @@ -225,6 +224,12 @@ MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, } else { GOOGLE_DCHECK_TYPE(*extension, REPEATED, MESSAGE); } + return extension; +} + +MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, + MessageFactory* factory) { + Extension* extension = MaybeNewRepeatedExtension(descriptor); // RepeatedPtrField does not know how to Add() since it cannot // allocate an abstract object, so we have to be tricky. @@ -244,6 +249,13 @@ MessageLite* ExtensionSet::AddMessage(const FieldDescriptor* descriptor, return result; } +void ExtensionSet::AddAllocatedMessage(const FieldDescriptor* descriptor, + MessageLite* new_entry) { + Extension* extension = MaybeNewRepeatedExtension(descriptor); + + extension->repeated_message_value->AddAllocated(new_entry); +} + static bool ValidateEnumUsingDescriptor(const void* arg, int number) { return reinterpret_cast(arg) ->FindValueByNumber(number) != NULL; diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc index 684ce00255..1569120d15 100644 --- a/src/google/protobuf/extension_set_unittest.cc +++ b/src/google/protobuf/extension_set_unittest.cc @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -173,7 +174,7 @@ TEST(ExtensionSetTest, SetAllocatedExtension) { } TEST(ExtensionSetTest, ReleaseExtension) { - unittest::TestMessageSet message; + proto2_wireformat_unittest::TestMessageSet message; EXPECT_FALSE(message.HasExtension( unittest::TestMessageSetExtension1::message_set_extension)); // Add a extension using SetAllocatedExtension diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc index 412c48a1e6..b9957e7517 100644 --- a/src/google/protobuf/generated_message_reflection.cc +++ b/src/google/protobuf/generated_message_reflection.cc @@ -35,6 +35,7 @@ #include #include +#include #include #include #include @@ -818,7 +819,16 @@ void GeneratedMessageReflection::ClearField( } case FieldDescriptor::CPPTYPE_MESSAGE: - (*MutableRaw(message, field))->Clear(); + if (has_bits_offset_ == -1) { + // Proto3 does not have has-bits and we need to set a message field + // to NULL in order to indicate its un-presence. + if (GetArena(message) == NULL) { + delete *MutableRaw(message, field); + } + *MutableRaw(message, field) = NULL; + } else { + (*MutableRaw(message, field))->Clear(); + } break; } } @@ -1649,6 +1659,25 @@ Message* GeneratedMessageReflection::AddMessage( } } +void GeneratedMessageReflection::AddAllocatedMessage( + Message* message, const FieldDescriptor* field, + Message* new_entry) const { + USAGE_CHECK_ALL(AddAllocatedMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + MutableExtensionSet(message)->AddAllocatedMessage(field, new_entry); + } else { + RepeatedPtrFieldBase* repeated = NULL; + if (IsMapFieldInApi(field)) { + repeated = + MutableRaw(message, field)->MutableRepeatedField(); + } else { + repeated = MutableRaw(message, field); + } + repeated->AddAllocated >(new_entry); + } +} + void* GeneratedMessageReflection::MutableRawRepeatedField( Message* message, const FieldDescriptor* field, FieldDescriptor::CppType cpptype, @@ -1675,6 +1704,37 @@ void* GeneratedMessageReflection::MutableRawRepeatedField( } } +const void* GeneratedMessageReflection::GetRawRepeatedField( + const Message& message, const FieldDescriptor* field, + FieldDescriptor::CppType cpptype, + int ctype, const Descriptor* desc) const { + USAGE_CHECK_REPEATED("GetRawRepeatedField"); + if (field->cpp_type() != cpptype) + ReportReflectionUsageTypeError(descriptor_, + field, "GetRawRepeatedField", cpptype); + if (ctype >= 0) + GOOGLE_CHECK_EQ(field->options().ctype(), ctype) << "subtype mismatch"; + if (desc != NULL) + GOOGLE_CHECK_EQ(field->message_type(), desc) << "wrong submessage type"; + if (field->is_extension()) { + // Should use extension_set::GetRawRepeatedField. However, the required + // parameter "default repeated value" is not very easy to get here. + // Map is not supported in extensions, it is acceptable to use + // extension_set::MutableRawRepeatedField which does not change the message. + return MutableExtensionSet(const_cast(&message)) + ->MutableRawRepeatedField( + field->number(), field->type(), field->is_packed(), field); + } else { + // Trigger transform for MapField + if (IsMapFieldInApi(field)) { + return &(reinterpret_cast( + reinterpret_cast(&message) + + offsets_[field->index()])->GetRepeatedField()); + } + return reinterpret_cast(&message) + offsets_[field->index()]; + } +} + const FieldDescriptor* GeneratedMessageReflection::GetOneofFieldDescriptor( const Message& message, const OneofDescriptor* oneof_descriptor) const { @@ -1685,6 +1745,69 @@ const FieldDescriptor* GeneratedMessageReflection::GetOneofFieldDescriptor( return descriptor_->FindFieldByNumber(field_number); } +bool GeneratedMessageReflection::ContainsMapKey( + const Message& message, + const FieldDescriptor* field, + const MapKey& key) const { + USAGE_CHECK(IsMapFieldInApi(field), + "LookupMapValue", + "Field is not a map field."); + return GetRaw(message, field).ContainsMapKey(key); +} + +bool GeneratedMessageReflection::InsertOrLookupMapValue( + Message* message, + const FieldDescriptor* field, + const MapKey& key, + MapValueRef* val) const { + USAGE_CHECK(IsMapFieldInApi(field), + "InsertOrLookupMapValue", + "Field is not a map field."); + val->SetType(field->message_type()->FindFieldByName("value")->cpp_type()); + return MutableRaw(message, field)->InsertMapValue(key, val); +} + +bool GeneratedMessageReflection::DeleteMapValue( + Message* message, + const FieldDescriptor* field, + const MapKey& key) const { + USAGE_CHECK(IsMapFieldInApi(field), + "DeleteMapValue", + "Field is not a map field."); + return MutableRaw(message, field)->DeleteMapValue(key); +} + +MapIterator GeneratedMessageReflection::MapBegin( + Message* message, + const FieldDescriptor* field) const { + USAGE_CHECK(IsMapFieldInApi(field), + "MapBegin", + "Field is not a map field."); + MapIterator iter(message, field); + GetRaw(*message, field).MapBegin(&iter); + return iter; +} + +MapIterator GeneratedMessageReflection::MapEnd( + Message* message, + const FieldDescriptor* field) const { + USAGE_CHECK(IsMapFieldInApi(field), + "MapEnd", + "Field is not a map field."); + MapIterator iter(message, field); + GetRaw(*message, field).MapEnd(&iter); + return iter; +} + +int GeneratedMessageReflection::MapSize( + const Message& message, + const FieldDescriptor* field) const { + USAGE_CHECK(IsMapFieldInApi(field), + "MapSize", + "Field is not a map field."); + return GetRaw(message, field).size(); +} + // ----------------------------------------------------------------------------- const FieldDescriptor* GeneratedMessageReflection::FindKnownExtensionByName( @@ -2098,6 +2221,14 @@ void* GeneratedMessageReflection::RepeatedFieldData( } } +MapFieldBase* GeneratedMessageReflection::MapData( + Message* message, const FieldDescriptor* field) const { + USAGE_CHECK(IsMapFieldInApi(field), + "GetMapData", + "Field is not a map field."); + return MutableRaw(message, field); +} + GeneratedMessageReflection* GeneratedMessageReflection::NewGeneratedMessageReflection( const Descriptor* descriptor, diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index dc8abb98a0..85ce6e2eec 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -58,7 +58,9 @@ class GMR_Handlers; } // namespace upb namespace protobuf { - class DescriptorPool; +class DescriptorPool; +class MapKey; +class MapValueRef; } namespace protobuf { @@ -261,6 +263,25 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { const Message& message, const OneofDescriptor* oneof_descriptor) const; + private: + bool ContainsMapKey(const Message& message, + const FieldDescriptor* field, + const MapKey& key) const; + bool InsertOrLookupMapValue(Message* message, + const FieldDescriptor* field, + const MapKey& key, + MapValueRef* val) const; + bool DeleteMapValue(Message* message, + const FieldDescriptor* field, + const MapKey& key) const; + MapIterator MapBegin( + Message* message, + const FieldDescriptor* field) const; + MapIterator MapEnd( + Message* message, + const FieldDescriptor* field) const; + int MapSize(const Message& message, const FieldDescriptor* field) const; + public: void SetInt32 (Message* message, const FieldDescriptor* field, int32 value) const; @@ -371,6 +392,9 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { int value) const; Message* AddMessage(Message* message, const FieldDescriptor* field, MessageFactory* factory = NULL) const; + void AddAllocatedMessage( + Message* message, const FieldDescriptor* field, + Message* new_entry) const; const FieldDescriptor* FindKnownExtensionByName(const string& name) const; const FieldDescriptor* FindKnownExtensionByNumber(int number) const; @@ -391,9 +415,14 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { static const int kUnknownFieldSetInMetadata = -1; protected: - virtual void* MutableRawRepeatedField( + void* MutableRawRepeatedField( Message* message, const FieldDescriptor* field, FieldDescriptor::CppType, - int ctype, const Descriptor* desc) const; + int ctype, const Descriptor* desc) const override; + + const void* GetRawRepeatedField( + const Message& message, const FieldDescriptor* field, + FieldDescriptor::CppType, int ctype, + const Descriptor* desc) const override; virtual MessageFactory* GetMessageFactory() const; @@ -407,7 +436,7 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { // To parse directly into a proto2 generated class, the class GMR_Handlers // needs access to member offsets and hasbits. - friend class LIBPROTOBUF_EXPORT upb::google_opensource::GMR_Handlers; + friend class upb::google_opensource::GMR_Handlers; const Descriptor* descriptor_; const Message* default_instance_; @@ -539,6 +568,9 @@ class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { Message* sub_message, const FieldDescriptor* field) const; + internal::MapFieldBase* MapData( + Message* message, const FieldDescriptor* field) const; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratedMessageReflection); }; diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc index ca1a2918bb..df04384481 100644 --- a/src/google/protobuf/generated_message_reflection_unittest.cc +++ b/src/google/protobuf/generated_message_reflection_unittest.cc @@ -47,6 +47,7 @@ #include #include +#include #include #include #include @@ -546,6 +547,57 @@ TEST(GeneratedMessageReflectionTest, SetAllocatedExtensionMessageTest) { &to_message, TestUtil::ReflectionTester::IS_NULL); } +TEST(GeneratedMessageReflectionTest, AddRepeatedMessage) { + unittest::TestAllTypes message; + + const Reflection* reflection = message.GetReflection(); + const Reflection* nested_reflection = + unittest::TestAllTypes::NestedMessage::default_instance().GetReflection(); + + const FieldDescriptor* nested_bb = + unittest::TestAllTypes::NestedMessage::descriptor()->FindFieldByName( + "bb"); + + Message* nested = reflection->AddMessage( + &message, F("repeated_nested_message")); + nested_reflection->SetInt32(nested, nested_bb, 11); + + EXPECT_EQ(11, message.repeated_nested_message(0).bb()); +} + +TEST(GeneratedMessageReflectionTest, MutableRepeatedMessage) { + unittest::TestAllTypes message; + + const Reflection* reflection = message.GetReflection(); + const Reflection* nested_reflection = + unittest::TestAllTypes::NestedMessage::default_instance().GetReflection(); + + const FieldDescriptor* nested_bb = + unittest::TestAllTypes::NestedMessage::descriptor()->FindFieldByName( + "bb"); + + message.add_repeated_nested_message()->set_bb(12); + + Message* nested = reflection->MutableRepeatedMessage( + &message, F("repeated_nested_message"), 0); + EXPECT_EQ(12, nested_reflection->GetInt32(*nested, nested_bb)); + nested_reflection->SetInt32(nested, nested_bb, 13); + EXPECT_EQ(13, message.repeated_nested_message(0).bb()); +} + +TEST(GeneratedMessageReflectionTest, AddAllocatedMessage) { + unittest::TestAllTypes message; + + const Reflection* reflection = message.GetReflection(); + + unittest::TestAllTypes::NestedMessage* nested = + new unittest::TestAllTypes::NestedMessage(); + nested->set_bb(11); + reflection->AddAllocatedMessage(&message, F("repeated_nested_message"), nested); + EXPECT_EQ(1, message.repeated_nested_message_size()); + EXPECT_EQ(11, message.repeated_nested_message(0).bb()); +} + TEST(GeneratedMessageReflectionTest, ListFieldsOneOf) { unittest::TestOneof2 message; TestUtil::SetOneof1(&message); diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc index 3b8650d627..4bcd354fe6 100644 --- a/src/google/protobuf/io/coded_stream.cc +++ b/src/google/protobuf/io/coded_stream.cc @@ -44,6 +44,7 @@ #include #include #include +#include #include #include @@ -338,9 +339,9 @@ namespace { // The first part of the pair is true iff the read was successful. The second // part is buffer + (number of bytes read). This function is always inlined, // so returning a pair is costless. -inline ::std::pair ReadVarint32FromArray( +GOOGLE_ATTRIBUTE_ALWAYS_INLINE ::std::pair ReadVarint32FromArray( uint32 first_byte, const uint8* buffer, - uint32* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + uint32* value); inline ::std::pair ReadVarint32FromArray( uint32 first_byte, const uint8* buffer, uint32* value) { // Fast path: We have enough bytes left in the buffer to guarantee that diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h index ad351dc3ae..361c406b5b 100644 --- a/src/google/protobuf/io/coded_stream.h +++ b/src/google/protobuf/io/coded_stream.h @@ -191,16 +191,15 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Like GetDirectBufferPointer, but this method is inlined, and does not // attempt to Refresh() if the buffer is currently empty. - inline void GetDirectBufferPointerInline(const void** data, - int* size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE void GetDirectBufferPointerInline(const void** data, + int* size); // Read raw bytes, copying them into the given buffer. bool ReadRaw(void* buffer, int size); // Like the above, with inlined optimizations. This should only be used // by the protobuf implementation. - inline bool InternalReadRawInline(void* buffer, - int size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InternalReadRawInline(void* buffer, int size); // Like ReadRaw, but reads into a string. // @@ -212,8 +211,8 @@ class LIBPROTOBUF_EXPORT CodedInputStream { bool ReadString(string* buffer, int size); // Like the above, with inlined optimizations. This should only be used // by the protobuf implementation. - inline bool InternalReadStringInline(string* buffer, - int size) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InternalReadStringInline(string* buffer, + int size); // Read a 32-bit little-endian integer. @@ -243,7 +242,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // Always inline because this is only called in one place per parse loop // but it is called for every iteration of said loop, so it should be fast. // GCC doesn't want to inline this by default. - uint32 ReadTag() GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE uint32 ReadTag(); // This usually a faster alternative to ReadTag() when cutoff is a manifest // constant. It does particularly well for cutoff >= 127. The first part @@ -253,8 +252,8 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // above cutoff or is 0. (There's intentional wiggle room when tag is 0, // because that can arise in several ways, and for best performance we want // to avoid an extra "is tag == 0?" check here.) - inline std::pair ReadTagWithCutoff(uint32 cutoff) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE std::pair ReadTagWithCutoff( + uint32 cutoff); // Usually returns true if calling ReadVarint32() now would produce the given // value. Will always return false if ReadVarint32() would not return the @@ -263,7 +262,7 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // parameter. // Always inline because this collapses to a small number of instructions // when given a constant parameter, but GCC doesn't want to inline by default. - bool ExpectTag(uint32 expected) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool ExpectTag(uint32 expected); // Like above, except this reads from the specified buffer. The caller is // responsible for ensuring that the buffer is large enough to read a varint @@ -272,9 +271,9 @@ class LIBPROTOBUF_EXPORT CodedInputStream { // // Returns a pointer beyond the expected tag if it was found, or NULL if it // was not. - static const uint8* ExpectTagFromArray( + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static const uint8* ExpectTagFromArray( const uint8* buffer, - uint32 expected) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + uint32 expected); // Usually returns true if no more bytes can be read. Always returns false // if more bytes can be read. If ExpectAtEnd() returns true, a subsequent @@ -766,8 +765,8 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { // but GCC by default doesn't want to inline this. void WriteTag(uint32 value); // Like WriteTag() but writing directly to the target array. - static uint8* WriteTagToArray( - uint32 value, uint8* target) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static uint8* WriteTagToArray(uint32 value, + uint8* target); // Returns the number of bytes needed to encode the given value as a varint. static int VarintSize32(uint32 value); @@ -831,8 +830,8 @@ class LIBPROTOBUF_EXPORT CodedOutputStream { // WriteVarint32FallbackToArray. Meanwhile, WriteVarint32() is already // out-of-line, so it should just invoke this directly to avoid any extra // function call overhead. - static uint8* WriteVarint64ToArrayInline( - uint64 value, uint8* target) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE static uint8* WriteVarint64ToArrayInline( + uint64 value, uint8* target); static int VarintSize32Fallback(uint32 value); }; diff --git a/src/google/protobuf/io/coded_stream_inl.h b/src/google/protobuf/io/coded_stream_inl.h index fa20f20886..d95b06e04e 100644 --- a/src/google/protobuf/io/coded_stream_inl.h +++ b/src/google/protobuf/io/coded_stream_inl.h @@ -36,6 +36,7 @@ #ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_INL_H__ #define GOOGLE_PROTOBUF_IO_CODED_STREAM_INL_H__ +#include #include #include #include diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index 02d5bad3b5..630c908690 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -41,6 +41,8 @@ #include #include +#include +#include #include #include #include diff --git a/src/google/protobuf/io/gzip_stream.cc b/src/google/protobuf/io/gzip_stream.cc index dd7c036e30..1be6c86359 100644 --- a/src/google/protobuf/io/gzip_stream.cc +++ b/src/google/protobuf/io/gzip_stream.cc @@ -33,10 +33,12 @@ // This file contains the implementation of classes GzipInputStream and // GzipOutputStream. + #if HAVE_ZLIB #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc index 3ae8c26831..7d88650626 100644 --- a/src/google/protobuf/io/printer.cc +++ b/src/google/protobuf/io/printer.cc @@ -34,6 +34,7 @@ #include #include +#include #include namespace google { diff --git a/src/google/protobuf/io/printer_unittest.cc b/src/google/protobuf/io/printer_unittest.cc index 1331a8d996..258dd9861f 100644 --- a/src/google/protobuf/io/printer_unittest.cc +++ b/src/google/protobuf/io/printer_unittest.cc @@ -37,6 +37,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/io/strtod.cc b/src/google/protobuf/io/strtod.cc index 56973439d9..579de9aa79 100644 --- a/src/google/protobuf/io/strtod.cc +++ b/src/google/protobuf/io/strtod.cc @@ -34,6 +34,7 @@ #include #include +#include #include namespace google { diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc index 60bd7957d4..7ccd633da2 100644 --- a/src/google/protobuf/io/tokenizer.cc +++ b/src/google/protobuf/io/tokenizer.cc @@ -90,6 +90,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/io/tokenizer.h b/src/google/protobuf/io/tokenizer.h index 98576f56cb..49885eda9c 100644 --- a/src/google/protobuf/io/tokenizer.h +++ b/src/google/protobuf/io/tokenizer.h @@ -40,6 +40,7 @@ #include #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc index de096fb9d8..6526056a3e 100644 --- a/src/google/protobuf/io/tokenizer_unittest.cc +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -41,6 +41,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/io/zero_copy_stream.cc b/src/google/protobuf/io/zero_copy_stream.cc index f77c768fc9..186de00141 100644 --- a/src/google/protobuf/io/zero_copy_stream.cc +++ b/src/google/protobuf/io/zero_copy_stream.cc @@ -34,6 +34,7 @@ #include +#include #include namespace google { diff --git a/src/google/protobuf/io/zero_copy_stream_impl.cc b/src/google/protobuf/io/zero_copy_stream_impl.cc index f7901b2797..7ec2b5da5c 100644 --- a/src/google/protobuf/io/zero_copy_stream_impl.cc +++ b/src/google/protobuf/io/zero_copy_stream_impl.cc @@ -46,6 +46,7 @@ #include #include +#include #include diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc index 97b73b88e7..686e63f252 100644 --- a/src/google/protobuf/io/zero_copy_stream_impl_lite.cc +++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.cc @@ -39,6 +39,7 @@ #include #include +#include #include namespace google { diff --git a/src/google/protobuf/io/zero_copy_stream_impl_lite.h b/src/google/protobuf/io/zero_copy_stream_impl_lite.h index 4360b18f5e..9cdf037854 100644 --- a/src/google/protobuf/io/zero_copy_stream_impl_lite.h +++ b/src/google/protobuf/io/zero_copy_stream_impl_lite.h @@ -48,6 +48,7 @@ #include #include #include +#include #include diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc index f2e5b629df..3850e76c40 100644 --- a/src/google/protobuf/io/zero_copy_stream_unittest.cc +++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc @@ -46,6 +46,7 @@ // "parametized tests" so that one set of tests can be used on all the // implementations. + #ifdef _MSC_VER #include #else @@ -66,6 +67,8 @@ #endif #include +#include +#include #include #include #include diff --git a/src/google/protobuf/lite_arena_unittest.cc b/src/google/protobuf/lite_arena_unittest.cc new file mode 100644 index 0000000000..f0bee880aa --- /dev/null +++ b/src/google/protobuf/lite_arena_unittest.cc @@ -0,0 +1,83 @@ +// 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. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace { + +TEST(LiteArenaTest, MapNoHeapAllocation) { + // Allocate a large initial block to avoid mallocs during hooked test. + std::vector arena_block(128 * 1024); + google::protobuf::ArenaOptions options; + options.initial_block = &arena_block[0]; + options.initial_block_size = arena_block.size(); + google::protobuf::Arena arena(options); + string data; + data.reserve(128 * 1024); + + { + // TODO(teboring): Enable no heap check when ArenaStringPtr is used in + // Map. + // google::protobuf::internal::NoHeapChecker no_heap; + + protobuf_unittest::TestArenaMapLite* from = + google::protobuf::Arena::CreateMessage(&arena); + google::protobuf::MapLiteTestUtil::SetArenaMapFields(from); + from->SerializeToString(&data); + + protobuf_unittest::TestArenaMapLite* to = + google::protobuf::Arena::CreateMessage(&arena); + to->ParseFromString(data); + google::protobuf::MapLiteTestUtil::ExpectArenaMapFieldsSet(*to); + } +} + +TEST(LiteArenaTest, UnknownFieldMemLeak) { + google::protobuf::Arena arena; + protobuf_unittest::ForeignMessageArenaLite* message = + google::protobuf::Arena::CreateMessage( + &arena); + string data = "\012\000"; + int original_capacity = data.capacity(); + while (data.capacity() <= original_capacity) { + data.append("a"); + } + data[1] = data.size() - 2; + message->ParseFromString(data); +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/lite_unittest.cc b/src/google/protobuf/lite_unittest.cc index 5af8b24dee..d1948ab5cc 100644 --- a/src/google/protobuf/lite_unittest.cc +++ b/src/google/protobuf/lite_unittest.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -376,13 +377,6 @@ int main(int argc, char* argv[]) { google::protobuf::MapLiteTestUtil::ExpectMapFieldsSetInitialized(message); } - { - // Proto2SetMapFieldsInitialized - protobuf_unittest::TestEnumStartWithNonZeroMapLite message; - EXPECT_EQ(protobuf_unittest::PROTO2_NON_ZERO_MAP_ENUM_FOO_LITE, - (*message.mutable_map_field())[0]); - } - { // Clear protobuf_unittest::TestMapLite message; @@ -692,37 +686,6 @@ int main(int argc, char* argv[]) { EXPECT_TRUE(map_message.IsInitialized()); } - // arena support for map ========================================= - - { - // ParsingAndSerializingNoHeapAllocation - - // Allocate a large initial block to avoid mallocs during hooked test. - std::vector arena_block(128 * 1024); - google::protobuf::ArenaOptions options; - options.initial_block = &arena_block[0]; - options.initial_block_size = arena_block.size(); - google::protobuf::Arena arena(options); - string data; - data.reserve(128 * 1024); - - { - google::protobuf::internal::NoHeapChecker no_heap; - - protobuf_unittest::TestArenaMapLite* from = - google::protobuf::Arena::CreateMessage( - &arena); - google::protobuf::MapLiteTestUtil::SetArenaMapFields(from); - from->SerializeToString(&data); - - protobuf_unittest::TestArenaMapLite* to = - google::protobuf::Arena::CreateMessage( - &arena); - to->ParseFromString(data); - google::protobuf::MapLiteTestUtil::ExpectArenaMapFieldsSet(*to); - } - } - std::cout << "PASS" << std::endl; return 0; } diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h index d928b7a759..c43df13ed7 100644 --- a/src/google/protobuf/map.h +++ b/src/google/protobuf/map.h @@ -35,9 +35,12 @@ #include #include // To support Visual Studio 2008 +#include #include #include #include +#include +#include namespace google { namespace protobuf { @@ -47,13 +50,379 @@ class Map; template struct is_proto_enum; +class MapIterator; + namespace internal { template class MapFieldLite; -} + +template +class MapField; + +template +class TypeDefinedMapFieldBase; + +class DynamicMapField; + +class GeneratedMessageReflection; +} // namespace internal + +#define TYPE_CHECK(EXPECTEDTYPE, METHOD) \ + if (type() != EXPECTEDTYPE) { \ + GOOGLE_LOG(FATAL) \ + << "Protocol Buffer map usage error:\n" \ + << METHOD << " type does not match\n" \ + << " Expected : " \ + << FieldDescriptor::CppTypeName(EXPECTEDTYPE) << "\n" \ + << " Actual : " \ + << FieldDescriptor::CppTypeName(type()); \ + } + +// MapKey is an union type for representing any possible +// map key. +class LIBPROTOBUF_EXPORT MapKey { + public: + MapKey() : type_(0) { + } + MapKey(const MapKey& other) : type_(0) { + CopyFrom(other); + } + + ~MapKey() { + if (type_ == FieldDescriptor::CPPTYPE_STRING) { + delete val_.string_value_; + } + } + + FieldDescriptor::CppType type() const { + if (type_ == 0) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer map usage error:\n" + << "MapKey::type MapKey is not initialized. " + << "Call set methods to initialize MapKey."; + } + return (FieldDescriptor::CppType)type_; + } + + void SetInt64Value(int64 value) { + SetType(FieldDescriptor::CPPTYPE_INT64); + val_.int64_value_ = value; + } + void SetUInt64Value(uint64 value) { + SetType(FieldDescriptor::CPPTYPE_UINT64); + val_.uint64_value_ = value; + } + void SetInt32Value(int32 value) { + SetType(FieldDescriptor::CPPTYPE_INT32); + val_.int32_value_ = value; + } + void SetUInt32Value(uint32 value) { + SetType(FieldDescriptor::CPPTYPE_UINT32); + val_.uint32_value_ = value; + } + void SetBoolValue(bool value) { + SetType(FieldDescriptor::CPPTYPE_BOOL); + val_.bool_value_ = value; + } + void SetStringValue(const string& val) { + SetType(FieldDescriptor::CPPTYPE_STRING); + *val_.string_value_ = val; + } + + int64 GetInt64Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64, + "MapKey::GetInt64Value"); + return val_.int64_value_; + } + uint64 GetUInt64Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64, + "MapKey::GetUInt64Value"); + return val_.uint64_value_; + } + int32 GetInt32Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32, + "MapKey::GetInt32Value"); + return val_.int32_value_; + } + uint32 GetUInt32Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32, + "MapKey::GetUInt32Value"); + return val_.uint32_value_; + } + int32 GetBoolValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, + "MapKey::GetBoolValue"); + return val_.bool_value_; + } + const string& GetStringValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING, + "MapKey::GetStringValue"); + return *val_.string_value_; + } + + bool operator==(const MapKey& other) const { + if (type_ != other.type_) { + return false; + } + switch (type()) { + case FieldDescriptor::CPPTYPE_STRING: + return *val_.string_value_ == *other.val_.string_value_; + case FieldDescriptor::CPPTYPE_INT64: + return val_.int64_value_ == other.val_.int64_value_; + case FieldDescriptor::CPPTYPE_INT32: + return val_.int32_value_ == other.val_.int32_value_; + case FieldDescriptor::CPPTYPE_UINT64: + return val_.uint64_value_ == other.val_.uint64_value_; + case FieldDescriptor::CPPTYPE_UINT32: + return val_.uint32_value_ == other.val_.uint32_value_; + case FieldDescriptor::CPPTYPE_BOOL: + return val_.bool_value_ == other.val_.bool_value_; + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; + } + } + + void CopyFrom(const MapKey& other) { + SetType(other.type()); + switch (type_) { + case FieldDescriptor::CPPTYPE_STRING: + *val_.string_value_ = *other.val_.string_value_; + break; + case FieldDescriptor::CPPTYPE_INT64: + val_.int64_value_ = other.val_.int64_value_; + break; + case FieldDescriptor::CPPTYPE_INT32: + val_.int32_value_ = other.val_.int32_value_; + break; + case FieldDescriptor::CPPTYPE_UINT64: + val_.uint64_value_ = other.val_.uint64_value_; + break; + case FieldDescriptor::CPPTYPE_UINT32: + val_.uint32_value_ = other.val_.uint32_value_; + break; + case FieldDescriptor::CPPTYPE_BOOL: + val_.bool_value_ = other.val_.bool_value_; + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_FLOAT: + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } + } + + private: + template + friend class internal::TypeDefinedMapFieldBase; + friend class MapIterator; + friend class internal::DynamicMapField; + + union KeyValue { + KeyValue() {} + string* string_value_; + int64 int64_value_; + int32 int32_value_; + uint64 uint64_value_; + uint32 uint32_value_; + bool bool_value_; + } val_; + + void SetType(FieldDescriptor::CppType type) { + if (type_ == type) return; + if (type_ == FieldDescriptor::CPPTYPE_STRING) { + delete val_.string_value_; + } + type_ = type; + if (type_ == FieldDescriptor::CPPTYPE_STRING) { + val_.string_value_ = new string; + } + } + + // type_ is 0 or a valid FieldDescriptor::CppType. + int type_; +}; + +// MapValueRef points to a map value. +class LIBPROTOBUF_EXPORT MapValueRef { + public: + MapValueRef() : data_(NULL), type_(0) {} + + void SetInt64Value(int64 value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64, + "MapValueRef::SetInt64Value"); + *reinterpret_cast(data_) = value; + } + void SetUInt64Value(uint64 value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64, + "MapValueRef::SetUInt64Value"); + *reinterpret_cast(data_) = value; + } + void SetInt32Value(int32 value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32, + "MapValueRef::SetInt32Value"); + *reinterpret_cast(data_) = value; + } + void SetUInt32Value(uint64 value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32, + "MapValueRef::SetUInt32Value"); + *reinterpret_cast(data_) = value; + } + void SetBoolValue(bool value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, + "MapValueRef::SetBoolValue"); + *reinterpret_cast(data_) = value; + } + // TODO(jieluo) - Checks that enum is member. + void SetEnumValue(int value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_ENUM, + "MapValueRef::SetEnumValue"); + *reinterpret_cast(data_) = value; + } + void SetStringValue(const string& value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING, + "MapValueRef::SetStringValue"); + *reinterpret_cast(data_) = value; + } + void SetFloatValue(float value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_FLOAT, + "MapValueRef::SetFloatValue"); + *reinterpret_cast(data_) = value; + } + void SetDoubleValue(double value) { + TYPE_CHECK(FieldDescriptor::CPPTYPE_DOUBLE, + "MapValueRef::SetDoubleValue"); + *reinterpret_cast(data_) = value; + } + + int64 GetInt64Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT64, + "MapValueRef::GetInt64Value"); + return *reinterpret_cast(data_); + } + uint64 GetUInt64Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT64, + "MapValueRef::GetUInt64Value"); + return *reinterpret_cast(data_); + } + int32 GetInt32Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_INT32, + "MapValueRef::GetInt32Value"); + return *reinterpret_cast(data_); + } + uint32 GetUInt32Value() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_UINT32, + "MapValueRef::GetUInt32Value"); + return *reinterpret_cast(data_); + } + bool GetBoolValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_BOOL, + "MapValueRef::GetBoolValue"); + return *reinterpret_cast(data_); + } + int GetEnumValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_ENUM, + "MapValueRef::GetEnumValue"); + return *reinterpret_cast(data_); + } + const string& GetStringValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_STRING, + "MapValueRef::GetStringValue"); + return *reinterpret_cast(data_); + } + float GetFloatValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_FLOAT, + "MapValueRef::GetFloatValue"); + return *reinterpret_cast(data_); + } + double GetDoubleValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_DOUBLE, + "MapValueRef::GetDoubleValue"); + return *reinterpret_cast(data_); + } + + const Message& GetMessageValue() const { + TYPE_CHECK(FieldDescriptor::CPPTYPE_MESSAGE, + "MapValueRef::GetMessageValue"); + return *reinterpret_cast(data_); + } + + Message* MutableMessageValue() { + TYPE_CHECK(FieldDescriptor::CPPTYPE_MESSAGE, + "MapValueRef::MutableMessageValue"); + return reinterpret_cast(data_); + } + + private: + template + friend class internal::MapField; + template + friend class internal::TypeDefinedMapFieldBase; + friend class MapIterator; + friend class internal::GeneratedMessageReflection; + friend class internal::DynamicMapField; + + void SetType(FieldDescriptor::CppType type) { + type_ = type; + } + + FieldDescriptor::CppType type() const { + if (type_ == 0 || data_ == NULL) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer map usage error:\n" + << "MapValueRef::type MapValueRef is not initialized."; + } + return (FieldDescriptor::CppType)type_; + } + void SetValue(const void* val) { + data_ = const_cast(val); + } + void CopyFrom(const MapValueRef& other) { + type_ = other.type_; + data_ = other.data_; + } + // Only used in DynamicMapField + void DeleteData() { + switch (type_) { +#define HANDLE_TYPE(CPPTYPE, TYPE) \ + case google::protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE: { \ + delete reinterpret_cast(data_); \ + break; \ + } + HANDLE_TYPE(INT32, int32); + HANDLE_TYPE(INT64, int64); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE(FLOAT, float); + HANDLE_TYPE(BOOL, bool); + HANDLE_TYPE(STRING, string); + HANDLE_TYPE(ENUM, int32); + HANDLE_TYPE(MESSAGE, Message); +#undef HANDLE_TYPE + } + } + // data_ point to a map value. MapValueRef does not + // own this value. + void* data_; + // type_ is 0 or a valid FieldDescriptor::CppType. + int type_; +}; + +#undef TYPE_CHECK // This is the class for google::protobuf::Map's internal value_type. Instead of using // std::pair as value_type, we use this class which provides us more control of @@ -82,7 +451,6 @@ class MapPair { T second; private: - typedef void DestructorSkippable_; friend class ::google::protobuf::Arena; friend class Map; }; @@ -92,9 +460,6 @@ class MapPair { // interface directly to visit or change map fields. template class Map { - typedef internal::MapCppTypeHandler KeyTypeHandler; - typedef internal::MapCppTypeHandler ValueTypeHandler; - public: typedef Key key_type; typedef T mapped_type; @@ -118,7 +483,9 @@ class Map { : arena_(arena), allocator_(arena_), elements_(0, hasher(), key_equal(), allocator_), - default_enum_value_(0) {} + default_enum_value_(0) { + arena_->OwnDestructor(&elements_); + } Map(const Map& other) : arena_(NULL), @@ -127,6 +494,14 @@ class Map { default_enum_value_(other.default_enum_value_) { insert(other.begin(), other.end()); } + template + explicit Map(const InputIt& first, const InputIt& last) + : arena_(NULL), + allocator_(arena_), + elements_(0, hasher(), key_equal(), allocator_), + default_enum_value_(0) { + insert(first, last); + } ~Map() { clear(); } @@ -172,7 +547,7 @@ class Map { !defined(GOOGLE_PROTOBUF_OS_NACL) && !defined(GOOGLE_PROTOBUF_OS_ANDROID) template void construct(NodeType* p, Args&&... args) { - new ((void*)p) NodeType(std::forward(args)...); + new (static_cast(p)) NodeType(std::forward(args)...); } template @@ -206,6 +581,7 @@ class Map { } private: + typedef void DestructorSkippable_; Arena* arena_; template @@ -216,7 +592,7 @@ class Map { typedef MapAllocator*> > Allocator; // Iterators - class const_iterator + class LIBPROTOBUF_EXPORT const_iterator : public std::iterator { typedef typename hash_map, equal_to, @@ -449,6 +825,35 @@ class Map { }; } // namespace protobuf - } // namespace google + +GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_START +template<> +struct hash { + size_t + operator()(const google::protobuf::MapKey& map_key) const { + switch (map_key.type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + return hash()(map_key.GetStringValue()); + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + return hash< ::google::protobuf::int64>()(map_key.GetInt64Value()); + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + return hash< ::google::protobuf::int32>()(map_key.GetInt32Value()); + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + return hash< ::google::protobuf::uint64>()(map_key.GetUInt64Value()); + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + return hash< ::google::protobuf::uint32>()(map_key.GetUInt32Value()); + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + return hash()(map_key.GetBoolValue()); + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + return 0; + } + } +}; +GOOGLE_PROTOBUF_HASH_NAMESPACE_DECLARATION_END + #endif // GOOGLE_PROTOBUF_MAP_H__ diff --git a/src/google/protobuf/map_entry.h b/src/google/protobuf/map_entry.h index e93d0348f5..987c4e297c 100644 --- a/src/google/protobuf/map_entry.h +++ b/src/google/protobuf/map_entry.h @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -79,40 +80,46 @@ class LIBPROTOBUF_EXPORT MapEntryBase : public Message { // MapEntry is the returned google::protobuf::Message when calling AddMessage of // google::protobuf::Reflection. In order to let it work with generated message -// reflection, its internal layout is the same as generated message with the -// same fields. However, in order to decide the internal layout of key/value, we -// need to know both their cpp type in generated api and proto type. +// reflection, its in-memory type is the same as generated message with the same +// fields. However, in order to decide the in-memory type of key/value, we need +// to know both their cpp type in generated api and proto type. In +// implmentation, all in-memory types have related wire format functions to +// support except ArenaStringPtr. Therefore, we need to define another type with +// supporting wire format functions. Since this type is only used as return type +// of MapEntry accessors, it's named MapEntry accessor type. // -// cpp type | proto type | internal layout -// int32 TYPE_INT32 int32 -// int32 TYPE_FIXED32 int32 -// FooEnum TYPE_ENUM int -// FooMessage TYPE_MESSAGE FooMessage* +// cpp type: the type visible to users in public API. +// proto type: WireFormatLite::FieldType of the field. +// in-memory type: type of the data member used to stored this field. +// MapEntry accessor type: type used in MapEntry getters/mutators to access the +// field. // -// The internal layouts of primitive types can be inferred from its proto type, -// while we need to explicitly tell cpp type if proto type is TYPE_MESSAGE to -// get internal layout. -// Moreover, default_enum_value is used to initialize enum field in proto2. +// cpp type | proto type | in-memory type | MapEntry accessor type +// int32 TYPE_INT32 int32 int32 +// int32 TYPE_FIXED32 int32 int32 +// string TYPE_STRING ArenaStringPtr string +// FooEnum TYPE_ENUM int int +// FooMessage TYPE_MESSAGE FooMessage* FooMessage +// +// The in-memory types of primitive types can be inferred from its proto type, +// while we need to explicitly specify the cpp type if proto type is +// TYPE_MESSAGE to infer the in-memory type. Moreover, default_enum_value is +// used to initialize enum field in proto2. template class MapEntry : public MapEntryBase { - // Handlers for key/value wire type. Provide utilities to parse/serialize - // key/value. - typedef MapWireFieldTypeHandler KeyWireHandler; - typedef MapWireFieldTypeHandler ValueWireHandler; - - // Define key/value's internal stored type. Message is the only one whose - // internal stored type cannot be inferred from its proto type. - static const bool kIsKeyMessage = KeyWireHandler::kIsMessage; - static const bool kIsValueMessage = ValueWireHandler::kIsMessage; - typedef typename KeyWireHandler::CppType KeyInternalType; - typedef typename ValueWireHandler::CppType ValueInternalType; - typedef typename MapIf::type - KeyCppType; - typedef typename MapIf::type - ValCppType; + // Provide utilities to parse/serialize key/value. Provide utilities to + // manipulate internal stored type. + typedef MapTypeHandler KeyTypeHandler; + typedef MapTypeHandler ValueTypeHandler; + + // Enum type cannot be used for MapTypeHandler::Read. Define a type + // which will replace Enum with int. + typedef typename KeyTypeHandler::MapEntryAccessorType KeyMapEntryAccessorType; + typedef typename ValueTypeHandler::MapEntryAccessorType + ValueMapEntryAccessorType; // Abbreviation for MapEntry typedef typename google::protobuf::internal::MapEntry< @@ -132,16 +139,16 @@ class MapEntry : public MapEntryBase { // accessors ====================================================== - virtual inline const KeyCppType& key() const { + virtual inline const KeyMapEntryAccessorType& key() const { return entry_lite_.key(); } - inline KeyCppType* mutable_key() { + inline KeyMapEntryAccessorType* mutable_key() { return entry_lite_.mutable_key(); } - virtual inline const ValCppType& value() const { + virtual inline const ValueMapEntryAccessorType& value() const { return entry_lite_.value(); } - inline ValCppType* mutable_value() { + inline ValueMapEntryAccessorType* mutable_value() { return entry_lite_.mutable_value(); } @@ -240,7 +247,9 @@ class MapEntry : public MapEntryBase { GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MapEntry, entry_lite_._has_bits_), GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MapEntry, _unknown_fields_), -1, DescriptorPool::generated_pool(), - ::google::protobuf::MessageFactory::generated_factory(), sizeof(MapEntry), -1); + ::google::protobuf::MessageFactory::generated_factory(), + sizeof(MapEntry), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MapEntry, _internal_metadata_)); entry->descriptor_ = descriptor; entry->reflection_ = reflection; entry->set_default_instance(entry); @@ -250,10 +259,13 @@ class MapEntry : public MapEntryBase { } private: - MapEntry() : default_instance_(NULL), entry_lite_() {} + MapEntry() + : _internal_metadata_(NULL), default_instance_(NULL), entry_lite_() {} explicit MapEntry(Arena* arena) - : default_instance_(NULL), entry_lite_(arena) {} + : _internal_metadata_(arena), + default_instance_(NULL), + entry_lite_(arena) {} inline Arena* GetArenaNoVirtual() const { return entry_lite_.GetArenaNoVirtual(); @@ -266,6 +278,7 @@ class MapEntry : public MapEntryBase { static int offsets_[2]; UnknownFieldSet _unknown_fields_; + InternalMetadataWithArena _internal_metadata_; MapEntry* default_instance_; EntryLiteType entry_lite_; diff --git a/src/google/protobuf/map_entry_lite.h b/src/google/protobuf/map_entry_lite.h index 52746da595..0e6ee013e3 100644 --- a/src/google/protobuf/map_entry_lite.h +++ b/src/google/protobuf/map_entry_lite.h @@ -61,38 +61,21 @@ template class MapEntryLite : public MessageLite { - // Handlers for key/value wire type. Provide utilities to parse/serialize - // key/value. - typedef MapWireFieldTypeHandler KeyWireHandler; - typedef MapWireFieldTypeHandler ValueWireHandler; - - // Define key/value's internal stored type. Message is the only one whose - // internal stored type cannot be inferred from its proto type - static const bool kIsKeyMessage = KeyWireHandler::kIsMessage; - static const bool kIsValueMessage = ValueWireHandler::kIsMessage; - typedef typename KeyWireHandler::CppType KeyInternalType; - typedef typename ValueWireHandler::CppType ValueInternalType; - typedef typename MapIf::type - KeyCppType; - typedef typename MapIf::type - ValCppType; - - // Handlers for key/value's internal stored type. Provide utilities to - // manipulate internal stored type. We need it because some types are stored - // as values and others are stored as pointers (Message and string), but we - // need to keep the code in MapEntry unified instead of providing different - // codes for each type. - typedef MapCppTypeHandler KeyCppHandler; - typedef MapCppTypeHandler ValueCppHandler; + // Provide utilities to parse/serialize key/value. Provide utilities to + // manipulate internal stored type. + typedef MapTypeHandler KeyTypeHandler; + typedef MapTypeHandler ValueTypeHandler; // Define internal memory layout. Strings and messages are stored as // pointers, while other types are stored as values. - static const bool kKeyIsStringOrMessage = KeyCppHandler::kIsStringOrMessage; - static const bool kValIsStringOrMessage = ValueCppHandler::kIsStringOrMessage; - typedef typename MapIf::type - KeyBase; - typedef typename MapIf::type - ValueBase; + typedef typename KeyTypeHandler::TypeOnMemory KeyOnMemory; + typedef typename ValueTypeHandler::TypeOnMemory ValueOnMemory; + + // Enum type cannot be used for MapTypeHandler::Read. Define a type + // which will replace Enum with int. + typedef typename KeyTypeHandler::MapEntryAccessorType KeyMapEntryAccessorType; + typedef typename ValueTypeHandler::MapEntryAccessorType + ValueMapEntryAccessorType; // Constants for field number. static const int kKeyFieldNumber = 1; @@ -100,38 +83,37 @@ class MapEntryLite : public MessageLite { // Constants for field tag. static const uint8 kKeyTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG( - kKeyFieldNumber, KeyWireHandler::kWireType); + kKeyFieldNumber, KeyTypeHandler::kWireType); static const uint8 kValueTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG( - kValueFieldNumber, ValueWireHandler::kWireType); + kValueFieldNumber, ValueTypeHandler::kWireType); static const int kTagSize = 1; public: ~MapEntryLite() { if (this != default_instance_) { - KeyCppHandler::Delete(key_); - ValueCppHandler::Delete(value_); + if (GetArenaNoVirtual() != NULL) return; + KeyTypeHandler::DeleteNoArena(key_); + ValueTypeHandler::DeleteNoArena(value_); } } // accessors ====================================================== - virtual inline const KeyCppType& key() const { - return KeyCppHandler::Reference(key_); - } - inline KeyCppType* mutable_key() { - set_has_key(); - KeyCppHandler::EnsureMutable(&key_, GetArenaNoVirtual()); - return KeyCppHandler::Pointer(key_); + virtual inline const KeyMapEntryAccessorType& key() const { + return KeyTypeHandler::GetExternalReference(key_); } - virtual inline const ValCppType& value() const { + virtual inline const ValueMapEntryAccessorType& value() const { GOOGLE_CHECK(default_instance_ != NULL); - return ValueCppHandler::DefaultIfNotInitialized(value_, + return ValueTypeHandler::DefaultIfNotInitialized(value_, default_instance_->value_); } - inline ValCppType* mutable_value() { + inline KeyMapEntryAccessorType* mutable_key() { + set_has_key(); + return KeyTypeHandler::EnsureMutable(&key_, GetArenaNoVirtual()); + } + inline ValueMapEntryAccessorType* mutable_value() { set_has_value(); - ValueCppHandler::EnsureMutable(&value_, GetArenaNoVirtual()); - return ValueCppHandler::Pointer(value_); + return ValueTypeHandler::EnsureMutable(&value_, GetArenaNoVirtual()); } // implements MessageLite ========================================= @@ -159,13 +141,17 @@ class MapEntryLite : public MessageLite { tag = input->ReadTag(); switch (tag) { case kKeyTag: - if (!KeyWireHandler::Read(input, mutable_key())) return false; + if (!KeyTypeHandler::Read(input, mutable_key())) { + return false; + } set_has_key(); if (!input->ExpectTag(kValueTag)) break; GOOGLE_FALLTHROUGH_INTENDED; case kValueTag: - if (!ValueWireHandler::Read(input, mutable_value())) return false; + if (!ValueTypeHandler::Read(input, mutable_value())) { + return false; + } set_has_value(); if (input->ExpectAtEnd()) return true; break; @@ -184,32 +170,35 @@ class MapEntryLite : public MessageLite { int ByteSize() const { int size = 0; - size += has_key() ? kTagSize + KeyWireHandler::ByteSize(key()) : 0; - size += has_value() ? kTagSize + ValueWireHandler::ByteSize(value()) : 0; + size += has_key() ? kTagSize + KeyTypeHandler::ByteSize(key()) : 0; + size += has_value() ? kTagSize + ValueTypeHandler::ByteSize(value()) : 0; return size; } void SerializeWithCachedSizes(::google::protobuf::io::CodedOutputStream* output) const { - KeyWireHandler::Write(kKeyFieldNumber, key(), output); - ValueWireHandler::Write(kValueFieldNumber, value(), output); + KeyTypeHandler::Write(kKeyFieldNumber, key(), output); + ValueTypeHandler::Write(kValueFieldNumber, value(), output); } ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const { - output = KeyWireHandler::WriteToArray(kKeyFieldNumber, key(), output); - output = - ValueWireHandler::WriteToArray(kValueFieldNumber, value(), output); + output = KeyTypeHandler::WriteToArray(kKeyFieldNumber, key(), output); + output = ValueTypeHandler::WriteToArray(kValueFieldNumber, value(), output); return output; } int GetCachedSize() const { int size = 0; - size += has_key() ? kTagSize + KeyWireHandler::GetCachedSize(key()) : 0; - size += - has_value() ? kTagSize + ValueWireHandler::GetCachedSize(value()) : 0; + size += has_key() + ? kTagSize + KeyTypeHandler::GetCachedSize(key()) + : 0; + size += has_value() + ? kTagSize + ValueTypeHandler::GetCachedSize( + value()) + : 0; return size; } - bool IsInitialized() const { return ValueCppHandler::IsInitialized(value_); } + bool IsInitialized() const { return ValueTypeHandler::IsInitialized(value_); } MessageLite* New() const { MapEntryLite* entry = new MapEntryLite; @@ -225,36 +214,37 @@ class MapEntryLite : public MessageLite { int SpaceUsed() const { int size = sizeof(MapEntryLite); - size += KeyCppHandler::SpaceUsedInMapEntry(key_); - size += ValueCppHandler::SpaceUsedInMapEntry(value_); + size += KeyTypeHandler::SpaceUsedInMapEntry(key_); + size += ValueTypeHandler::SpaceUsedInMapEntry(value_); return size; } void MergeFrom(const MapEntryLite& from) { if (from._has_bits_[0]) { if (from.has_key()) { - KeyCppHandler::EnsureMutable(&key_, GetArenaNoVirtual()); - KeyCppHandler::Merge(from.key(), &key_); + KeyTypeHandler::EnsureMutable(&key_, GetArenaNoVirtual()); + KeyTypeHandler::Merge(from.key(), &key_, GetArenaNoVirtual()); set_has_key(); } if (from.has_value()) { - ValueCppHandler::EnsureMutable(&value_, GetArenaNoVirtual()); - ValueCppHandler::Merge(from.value(), &value_); + ValueTypeHandler::EnsureMutable(&value_, GetArenaNoVirtual()); + ValueTypeHandler::Merge(from.value(), &value_, GetArenaNoVirtual()); set_has_value(); } } } void Clear() { - KeyCppHandler::Clear(&key_); - ValueCppHandler::ClearMaybeByDefaultEnum(&value_, default_enum_value); + KeyTypeHandler::Clear(&key_, GetArenaNoVirtual()); + ValueTypeHandler::ClearMaybeByDefaultEnum( + &value_, GetArenaNoVirtual(), default_enum_value); clear_has_key(); clear_has_value(); } void InitAsDefaultInstance() { - KeyCppHandler::AssignDefaultValue(&key_); - ValueCppHandler::AssignDefaultValue(&value_); + KeyTypeHandler::AssignDefaultValue(&key_); + ValueTypeHandler::AssignDefaultValue(&value_); } Arena* GetArena() const { @@ -302,11 +292,11 @@ class MapEntryLite : public MessageLite { // only takes references of given key and value. template - class MapEntryWrapper + class LIBPROTOBUF_EXPORT MapEntryWrapper : public MapEntryLite { typedef MapEntryLite Base; - typedef typename Base::KeyCppType KeyCppType; - typedef typename Base::ValCppType ValCppType; + typedef typename Base::KeyMapEntryAccessorType KeyMapEntryAccessorType; + typedef typename Base::ValueMapEntryAccessorType ValueMapEntryAccessorType; public: MapEntryWrapper(Arena* arena, const K& key, const V& value) @@ -316,8 +306,8 @@ class MapEntryLite : public MessageLite { Base::set_has_key(); Base::set_has_value(); } - inline const KeyCppType& key() const { return key_; } - inline const ValCppType& value() const { return value_; } + inline const KeyMapEntryAccessorType& key() const { return key_; } + inline const ValueMapEntryAccessorType& value() const { return value_; } private: const Key& key_; @@ -336,11 +326,11 @@ class MapEntryLite : public MessageLite { // the temporary. template - class MapEnumEntryWrapper + class LIBPROTOBUF_EXPORT MapEnumEntryWrapper : public MapEntryLite { typedef MapEntryLite Base; - typedef typename Base::KeyCppType KeyCppType; - typedef typename Base::ValCppType ValCppType; + typedef typename Base::KeyMapEntryAccessorType KeyMapEntryAccessorType; + typedef typename Base::ValueMapEntryAccessorType ValueMapEntryAccessorType; public: MapEnumEntryWrapper(Arena* arena, const K& key, const V& value) @@ -350,28 +340,28 @@ class MapEntryLite : public MessageLite { Base::set_has_key(); Base::set_has_value(); } - inline const KeyCppType& key() const { return key_; } - inline const ValCppType& value() const { return value_; } + inline const KeyMapEntryAccessorType& key() const { return key_; } + inline const ValueMapEntryAccessorType& value() const { return value_; } private: - const KeyCppType& key_; - const ValCppType value_; + const KeyMapEntryAccessorType& key_; + const ValueMapEntryAccessorType value_; friend class google::protobuf::Arena; typedef void DestructorSkippable_; }; MapEntryLite() : default_instance_(NULL), arena_(NULL) { - KeyCppHandler::Initialize(&key_, NULL); - ValueCppHandler::InitializeMaybeByDefaultEnum( + KeyTypeHandler::Initialize(&key_, NULL); + ValueTypeHandler::InitializeMaybeByDefaultEnum( &value_, default_enum_value, NULL); _has_bits_[0] = 0; } explicit MapEntryLite(Arena* arena) : default_instance_(NULL), arena_(arena) { - KeyCppHandler::Initialize(&key_, arena); - ValueCppHandler::InitializeMaybeByDefaultEnum( + KeyTypeHandler::Initialize(&key_, arena); + ValueTypeHandler::InitializeMaybeByDefaultEnum( &value_, default_enum_value, arena); _has_bits_[0] = 0; } @@ -386,8 +376,8 @@ class MapEntryLite : public MessageLite { MapEntryLite* default_instance_; - KeyBase key_; - ValueBase value_; + KeyOnMemory key_; + ValueOnMemory value_; Arena* arena_; uint32 _has_bits_[1]; diff --git a/src/google/protobuf/map_field.cc b/src/google/protobuf/map_field.cc index 6ff1936eb4..d8879f24e9 100644 --- a/src/google/protobuf/map_field.cc +++ b/src/google/protobuf/map_field.cc @@ -29,6 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include @@ -145,6 +146,321 @@ void MapFieldBase::SyncMapWithRepeatedField() const { } } +// ------------------DynamicMapField------------------ +DynamicMapField::DynamicMapField(const Message* default_entry) + : default_entry_(default_entry) { +} + +DynamicMapField::DynamicMapField(const Message* default_entry, + Arena* arena) + : TypeDefinedMapFieldBase(arena), + default_entry_(default_entry) { +} + +DynamicMapField::~DynamicMapField() { + // DynamicMapField owns map values. Need to delete them before clearing + // the map. + for (typename Map::iterator iter = map_.begin(); + iter != map_.end(); ++iter) { + iter->second.DeleteData(); + } + map_.clear(); +} + +int DynamicMapField::size() const { + return GetMap().size(); +} + +bool DynamicMapField::ContainsMapKey( + const MapKey& map_key) const { + const Map& map = GetMap(); + typename Map::const_iterator iter = map.find(map_key); + return iter != map.end(); +} + +bool DynamicMapField::InsertMapValue( + const MapKey& map_key, MapValueRef* val) { + bool result = false; + + MapValueRef& map_val = (*MutableMap())[map_key]; + // If map_val.data_ is not set, it is newly inserted by map_[map_key]. + if (map_val.data_ == NULL) { + result = true; + const FieldDescriptor* val_des = + default_entry_->GetDescriptor()->FindFieldByName("value"); + map_val.SetType(val_des->cpp_type()); + // Allocate momery for the inserted MapValueRef, and initialize to + // default value. + switch (val_des->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, TYPE) \ + case google::protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE: { \ + TYPE * value = new TYPE(); \ + map_val.SetValue(value); \ + break; \ + } + HANDLE_TYPE(INT32, int32); + HANDLE_TYPE(INT64, int64); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE(FLOAT, float); + HANDLE_TYPE(BOOL, bool); + HANDLE_TYPE(STRING, string); + HANDLE_TYPE(ENUM, int32); +#undef HANDLE_TYPE + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + const Message& message = default_entry_->GetReflection()->GetMessage( + *default_entry_, val_des); + Message* value = message.New(); + map_val.SetValue(value); + break; + } + } + } + val->CopyFrom(map_val); + return result; +} + +bool DynamicMapField::DeleteMapValue(const MapKey& map_key) { + MapFieldBase::SyncMapWithRepeatedField(); + Map::iterator iter = map_.find(map_key); + if (iter == map_.end()) { + return false; + } + // Set map dirty only if the delete is successful. + MapFieldBase::SetMapDirty(); + iter->second.DeleteData(); + map_.erase(iter); + return true; +} + +const Map& DynamicMapField::GetMap() const { + MapFieldBase::SyncMapWithRepeatedField(); + return map_; +} + +Map* DynamicMapField::MutableMap() { + MapFieldBase::SyncMapWithRepeatedField(); + MapFieldBase::SetMapDirty(); + return &map_; +} + +void DynamicMapField::SetMapIteratorValue(MapIterator* map_iter) const { + typename Map::const_iterator iter = + TypeDefinedMapFieldBase::InternalGetIterator( + map_iter); + if (iter == map_.end()) return; + map_iter->key_.CopyFrom(iter->first); + map_iter->value_.CopyFrom(iter->second); +} + +void DynamicMapField::SyncRepeatedFieldWithMapNoLock() const { + const Reflection* reflection = default_entry_->GetReflection(); + const FieldDescriptor* key_des = + default_entry_->GetDescriptor()->FindFieldByName("key"); + const FieldDescriptor* val_des = + default_entry_->GetDescriptor()->FindFieldByName("value"); + if (MapFieldBase::repeated_field_ == NULL) { + if (MapFieldBase::arena_ == NULL) { + MapFieldBase::repeated_field_ = new RepeatedPtrField(); + } else { + MapFieldBase::repeated_field_ = + Arena::CreateMessage >( + MapFieldBase::arena_); + } + } + + MapFieldBase::repeated_field_->Clear(); + + for (typename Map::const_iterator it = map_.begin(); + it != map_.end(); ++it) { + Message* new_entry = default_entry_->New(); + MapFieldBase::repeated_field_->AddAllocated(new_entry); + const MapKey& map_key = it->first; + switch (key_des->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + reflection->SetString(new_entry, key_des, map_key.GetStringValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + reflection->SetInt64(new_entry, key_des, map_key.GetInt64Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + reflection->SetInt32(new_entry, key_des, map_key.GetInt32Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + reflection->SetUInt64(new_entry, key_des, map_key.GetUInt64Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + reflection->SetUInt32(new_entry, key_des, map_key.GetUInt32Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + reflection->SetBool(new_entry, key_des, map_key.GetBoolValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } + const MapValueRef& map_val = it->second; + switch (val_des->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + reflection->SetString(new_entry, val_des, map_val.GetStringValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + reflection->SetInt64(new_entry, val_des, map_val.GetInt64Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + reflection->SetInt32(new_entry, val_des, map_val.GetInt32Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + reflection->SetUInt64(new_entry, val_des, map_val.GetUInt64Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + reflection->SetUInt32(new_entry, val_des, map_val.GetUInt32Value()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + reflection->SetBool(new_entry, val_des, map_val.GetBoolValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + reflection->SetDouble(new_entry, val_des, map_val.GetDoubleValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + reflection->SetFloat(new_entry, val_des, map_val.GetFloatValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + reflection->SetEnumValue(new_entry, val_des, map_val.GetEnumValue()); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + const Message& message = map_val.GetMessageValue(); + reflection->MutableMessage(new_entry, val_des)->CopyFrom(message); + break; + } + } + } +} + +void DynamicMapField::SyncMapWithRepeatedFieldNoLock() const { + Map* map = &const_cast(this)->map_; + const Reflection* reflection = default_entry_->GetReflection(); + const FieldDescriptor* key_des = + default_entry_->GetDescriptor()->FindFieldByName("key"); + const FieldDescriptor* val_des = + default_entry_->GetDescriptor()->FindFieldByName("value"); + // DynamicMapField owns map values. Need to delete them before clearing + // the map. + for (typename Map::iterator iter = map->begin(); + iter != map->end(); ++iter) { + iter->second.DeleteData(); + } + map->clear(); + for (typename RepeatedPtrField::iterator it = + MapFieldBase::repeated_field_->begin(); + it != MapFieldBase::repeated_field_->end(); ++it) { + MapKey map_key; + switch (key_des->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + map_key.SetStringValue(reflection->GetString(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + map_key.SetInt64Value(reflection->GetInt64(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + map_key.SetInt32Value(reflection->GetInt32(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + map_key.SetUInt64Value(reflection->GetUInt64(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + map_key.SetUInt32Value(reflection->GetUInt32(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + map_key.SetBoolValue(reflection->GetBool(*it, key_des)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + break; + } + MapValueRef& map_val = (*map)[map_key]; + map_val.SetType(val_des->cpp_type()); + switch (val_des->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, TYPE, METHOD) \ + case google::protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE: { \ + TYPE * value = new TYPE; \ + *value = reflection->Get##METHOD(*it, val_des); \ + map_val.SetValue(value); \ + break; \ + } + HANDLE_TYPE(INT32, int32, Int32); + HANDLE_TYPE(INT64, int64, Int64); + HANDLE_TYPE(UINT32, uint32, UInt32); + HANDLE_TYPE(UINT64, uint64, UInt64); + HANDLE_TYPE(DOUBLE, double, Double); + HANDLE_TYPE(FLOAT, float, Float); + HANDLE_TYPE(BOOL, bool, Bool); + HANDLE_TYPE(STRING, string, String); + HANDLE_TYPE(ENUM, int32, EnumValue); +#undef HANDLE_TYPE + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + const Message& message = reflection->GetMessage(*it, val_des); + Message* value = message.New(); + value->CopyFrom(message); + map_val.SetValue(value); + break; + } + } + } +} + +int DynamicMapField::SpaceUsedExcludingSelfNoLock() const { + int size = 0; + if (MapFieldBase::repeated_field_ != NULL) { + size += MapFieldBase::repeated_field_->SpaceUsedExcludingSelf(); + } + size += sizeof(map_); + int map_size = map_.size(); + if (map_size) { + typename Map::const_iterator it = map_.begin(); + size += sizeof(it->first) * map_size; + size += sizeof(it->second) * map_size; + // If key is string, add the allocated space. + if (it->first.type() == google::protobuf::FieldDescriptor::CPPTYPE_STRING) { + size += sizeof(string) * map_size; + } + // Add the allocated space in MapValueRef. + switch (it->second.type()) { +#define HANDLE_TYPE(CPPTYPE, TYPE) \ + case google::protobuf::FieldDescriptor::CPPTYPE_##CPPTYPE: { \ + size += sizeof(TYPE) * map_size; \ + break; \ + } + HANDLE_TYPE(INT32, int32); + HANDLE_TYPE(INT64, int64); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE(FLOAT, float); + HANDLE_TYPE(BOOL, bool); + HANDLE_TYPE(STRING, string); + HANDLE_TYPE(ENUM, int32); +#undef HANDLE_TYPE + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: { + while (it != map_.end()) { + const Message& message = it->second.GetMessageValue(); + size += message.GetReflection()->SpaceUsed(message); + ++it; + } + break; + } + } + } + return size; +} + } // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 56d3d0f461..9130166b85 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -32,6 +32,7 @@ #define GOOGLE_PROTOBUF_MAP_FIELD_H__ #include +#include #include #include #include @@ -45,7 +46,8 @@ namespace google { namespace protobuf { - +class DynamicMessage; +class MapKey; namespace internal { class ContendedMapCleanTest; @@ -83,6 +85,17 @@ class LIBPROTOBUF_EXPORT MapFieldBase { // Like above. Returns mutable pointer to the internal repeated field. RepeatedPtrFieldBase* MutableRepeatedField(); + // Pure virtual map APIs for Map Reflection. + virtual bool ContainsMapKey(const MapKey& map_key) const = 0; + virtual bool InsertMapValue(const MapKey& map_key, MapValueRef* val) = 0; + virtual bool DeleteMapValue(const MapKey& map_key) = 0; + virtual bool EqualIterator(const MapIterator& a, + const MapIterator& b) const = 0; + virtual void MapBegin(MapIterator* map_iter) const = 0; + virtual void MapEnd(MapIterator* map_iter) const = 0; + // Sync Map with repeated field and returns the size of map. + virtual int size() const = 0; + // Returns the number of bytes used by the repeated field, excluding // sizeof(*this) int SpaceUsedExcludingSelf() const; @@ -141,6 +154,57 @@ class LIBPROTOBUF_EXPORT MapFieldBase { friend class ContendedMapCleanTest; friend class GeneratedMessageReflection; friend class MapFieldAccessor; + friend class ::google::protobuf::DynamicMessage; + + // Virtual helper methods for MapIterator. MapIterator doesn't have the + // type helper for key and value. Call these help methods to deal with + // different types. Real helper methods are implemented in + // TypeDefinedMapFieldBase. + friend class ::google::protobuf::MapIterator; + // Allocate map<...>::iterator for MapIterator. + virtual void InitializeIterator(MapIterator* map_iter) const = 0; + + // DeleteIterator() is called by the destructor of MapIterator only. + // It deletes map<...>::iterator for MapIterator. + virtual void DeleteIterator(MapIterator* map_iter) const = 0; + + // Copy the map<...>::iterator from other_iterator to + // this_iterator. + virtual void CopyIterator(MapIterator* this_iterator, + const MapIterator& other_iterator) const = 0; + + // IncreaseIterator() is called by operator++() of MapIterator only. + // It implements the ++ operator of MapIterator. + virtual void IncreaseIterator(MapIterator* map_iter) const = 0; +}; + +// This class provides common Map Reflection implementations for generated +// message and dynamic message. +template +class TypeDefinedMapFieldBase : public MapFieldBase { + public: + TypeDefinedMapFieldBase() {} + explicit TypeDefinedMapFieldBase(Arena* arena) : MapFieldBase(arena) {} + ~TypeDefinedMapFieldBase() {} + void MapBegin(MapIterator* map_iter) const; + void MapEnd(MapIterator* map_iter) const; + bool EqualIterator(const MapIterator& a, const MapIterator& b) const; + + virtual const Map& GetMap() const = 0; + virtual Map* MutableMap() = 0; + + protected: + typename Map::const_iterator& InternalGetIterator( + const MapIterator* map_iter) const; + + private: + void InitializeIterator(MapIterator* map_iter) const; + void DeleteIterator(MapIterator* map_iter) const; + void CopyIterator(MapIterator* this_iteratorm, + const MapIterator& that_iterator) const; + void IncreaseIterator(MapIterator* map_iter) const; + + virtual void SetMapIteratorValue(MapIterator* map_iter) const = 0; }; // This class provides accesss to map field using generated api. It is used for @@ -150,25 +214,13 @@ template -class MapField : public MapFieldBase, +class MapField : public TypeDefinedMapFieldBase, public MapFieldLite { - // Handlers for key/value wire type. Provide utilities to parse/serialize - // key/value. - typedef MapWireFieldTypeHandler KeyWireHandler; - typedef MapWireFieldTypeHandler ValueWireHandler; - - // Define key/value's internal stored type. - static const bool kIsKeyMessage = KeyWireHandler::kIsMessage; - static const bool kIsValMessage = ValueWireHandler::kIsMessage; - typedef typename KeyWireHandler::CppType KeyInternalType; - typedef typename ValueWireHandler::CppType ValueInternalType; - typedef typename MapIf::type KeyCpp; - typedef typename MapIf::type ValCpp; - - // Handlers for key/value's internal stored type. - typedef MapCppTypeHandler KeyHandler; - typedef MapCppTypeHandler ValHandler; + // Provide utilities to parse/serialize key/value. Provide utilities to + // manipulate internal stored type. + typedef MapTypeHandler KeyTypeHandler; + typedef MapTypeHandler ValueTypeHandler; // Define message type for internal repeated field. typedef MapEntry @@ -183,8 +235,8 @@ class MapField : public MapFieldBase, // Enum needs to be handled differently from other types because it has // different exposed type in google::protobuf::Map's api and repeated field's api. For // details see the comment in the implementation of - // SyncMapWithRepeatedFieldNoLocki. - static const bool kIsValueEnum = ValueWireHandler::kIsEnum; + // SyncMapWithRepeatedFieldNoLock. + static const bool kIsValueEnum = ValueTypeHandler::kIsEnum; typedef typename MapIf::type CastValueType; public: @@ -197,6 +249,11 @@ class MapField : public MapFieldBase, MapField(Arena* arena, const Message* default_entry); ~MapField(); + // Implement MapFieldBase + bool ContainsMapKey(const MapKey& map_key) const; + bool InsertMapValue(const MapKey& map_key, MapValueRef* val); + bool DeleteMapValue(const MapKey& map_key); + // Accessors const Map& GetMap() const; Map* MutableMap(); @@ -230,12 +287,109 @@ class MapField : public MapFieldBase, void SyncMapWithRepeatedFieldNoLock() const; int SpaceUsedExcludingSelfNoLock() const; + void SetMapIteratorValue(MapIterator* map_iter) const; + mutable const EntryType* default_entry_; friend class ::google::protobuf::Arena; }; +class LIBPROTOBUF_EXPORT DynamicMapField: public TypeDefinedMapFieldBase { + public: + explicit DynamicMapField(const Message* default_entry); + DynamicMapField(const Message* default_entry, Arena* arena); + ~DynamicMapField(); + + // Implement MapFieldBase + bool ContainsMapKey(const MapKey& map_key) const; + bool InsertMapValue(const MapKey& map_key, MapValueRef* val); + bool DeleteMapValue(const MapKey& map_key); + + const Map& GetMap() const; + Map* MutableMap(); + + int size() const; + + private: + Map map_; + const Message* default_entry_; + + // Implements MapFieldBase + void SyncRepeatedFieldWithMapNoLock() const; + void SyncMapWithRepeatedFieldNoLock() const; + int SpaceUsedExcludingSelfNoLock() const; + void SetMapIteratorValue(MapIterator* map_iter) const; +}; + } // namespace internal + +class LIBPROTOBUF_EXPORT MapIterator { + public: + MapIterator(Message* message, const FieldDescriptor* field) { + const Reflection* reflection = message->GetReflection(); + map_ = reflection->MapData(message, field); + key_.SetType(field->message_type()->FindFieldByName("key")->cpp_type()); + value_.SetType(field->message_type()->FindFieldByName("value")->cpp_type()); + map_->InitializeIterator(this); + } + MapIterator(const MapIterator& other) { + map_ = other.map_; + map_->InitializeIterator(this); + map_->CopyIterator(this, other); + } + ~MapIterator() { + map_->DeleteIterator(this); + } + friend bool operator==(const MapIterator& a, const MapIterator& b) { + return a.map_->EqualIterator(a, b); + } + friend bool operator!=(const MapIterator& a, const MapIterator& b) { + return !a.map_->EqualIterator(a, b); + } + MapIterator& operator++() { + map_->IncreaseIterator(this); + return *this; + } + MapIterator operator++(int) { + // iter_ is copied from Map<...>::iterator, no need to + // copy from its self again. Use the same implementation + // with operator++() + map_->IncreaseIterator(this); + return *this; + } + const MapKey& GetKey() { + return key_; + } + const MapValueRef& GetValueRef() { + return value_; + } + MapValueRef* MutableValueRef() { + map_->SetMapDirty(); + return &value_; + } + + private: + template + friend class internal::TypeDefinedMapFieldBase; + friend class internal::DynamicMapField; + template + friend class internal::MapField; + + // reinterpret_cast from heap-allocated Map<...>::iterator*. MapIterator owns + // the iterator. It is allocated by MapField<...>::InitializeIterator() called + // in constructor and deleted by MapField<...>::DeleteIterator() called in + // destructor. + void* iter_; + // Point to a MapField to call helper methods implemented in MapField. + // MapIterator does not own this object. + internal::MapFieldBase* map_; + MapKey key_; + MapValueRef value_; +}; + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/map_field_inl.h b/src/google/protobuf/map_field_inl.h index 5b4305f90e..1a4ce93768 100644 --- a/src/google/protobuf/map_field_inl.h +++ b/src/google/protobuf/map_field_inl.h @@ -36,12 +36,128 @@ #include #endif +#include #include #include namespace google { namespace protobuf { namespace internal { +// UnwrapMapKey template +template +T UnwrapMapKey(const MapKey& map_key); +template<> +inline int32 UnwrapMapKey(const MapKey& map_key) { + return map_key.GetInt32Value(); +} +template<> +inline uint32 UnwrapMapKey(const MapKey& map_key) { + return map_key.GetUInt32Value(); +} +template<> +inline int64 UnwrapMapKey(const MapKey& map_key) { + return map_key.GetInt64Value(); +} +template<> +inline uint64 UnwrapMapKey(const MapKey& map_key) { + return map_key.GetUInt64Value(); +} +template<> +inline bool UnwrapMapKey(const MapKey& map_key) { + return map_key.GetBoolValue(); +} +template<> +inline string UnwrapMapKey(const MapKey& map_key) { + return map_key.GetStringValue(); +} + +// SetMapKey template +template +inline void SetMapKey(MapKey* map_key, const T& value); +template<> +inline void SetMapKey(MapKey* map_key, const int32& value) { + map_key->SetInt32Value(value); +} +template<> +inline void SetMapKey(MapKey* map_key, const uint32& value) { + map_key->SetUInt32Value(value); +} +template<> +inline void SetMapKey(MapKey* map_key, const int64& value) { + map_key->SetInt64Value(value); +} +template<> +inline void SetMapKey(MapKey* map_key, const uint64& value) { + map_key->SetUInt64Value(value); +} +template<> +inline void SetMapKey(MapKey* map_key, const bool& value) { + map_key->SetBoolValue(value); +} +template<> +inline void SetMapKey(MapKey* map_key, const string& value) { + map_key->SetStringValue(value); +} + +// ------------------------TypeDefinedMapFieldBase--------------- +template +typename Map::const_iterator& +TypeDefinedMapFieldBase::InternalGetIterator( + const MapIterator* map_iter) const { + return *reinterpret_cast::const_iterator *>( + map_iter->iter_); +} + +template +void TypeDefinedMapFieldBase::MapBegin(MapIterator* map_iter) const { + InternalGetIterator(map_iter) = GetMap().begin(); + SetMapIteratorValue(map_iter); +} + +template +void TypeDefinedMapFieldBase::MapEnd(MapIterator* map_iter) const { + InternalGetIterator(map_iter) = GetMap().end(); +} + +template +bool TypeDefinedMapFieldBase::EqualIterator(const MapIterator& a, + const MapIterator& b) + const { + return InternalGetIterator(&a) == InternalGetIterator(&b); +} + +template +void TypeDefinedMapFieldBase::IncreaseIterator(MapIterator* map_iter) + const { + ++InternalGetIterator(map_iter); + SetMapIteratorValue(map_iter); +} + +template +void TypeDefinedMapFieldBase::InitializeIterator( + MapIterator* map_iter) const { + map_iter->iter_ = new typename Map::const_iterator; + GOOGLE_CHECK(map_iter->iter_ != NULL); +} + +template +void TypeDefinedMapFieldBase::DeleteIterator(MapIterator* map_iter) + const { + delete reinterpret_cast::const_iterator *>( + map_iter->iter_); +} + +template +void TypeDefinedMapFieldBase::CopyIterator( + MapIterator* this_iter, + const MapIterator& that_iter) const { + InternalGetIterator(this_iter) = InternalGetIterator(&that_iter); + this_iter->key_.SetType(that_iter.key_.type()); + this_iter->value_.SetType(that_iter.value_.type()); + SetMapIteratorValue(this_iter); +} + +// ---------------------------------------------------------------------- template MapField::MapField( Arena* arena) - : MapFieldBase(arena), + : TypeDefinedMapFieldBase(arena), MapFieldLite( arena), default_entry_(NULL) {} @@ -75,7 +191,7 @@ template MapField::MapField( Arena* arena, const Message* default_entry) - : MapFieldBase(arena), + : TypeDefinedMapFieldBase(arena), MapFieldLite( arena), default_entry_(down_cast(default_entry)) {} @@ -94,7 +210,7 @@ template ::size() const { - SyncMapWithRepeatedField(); + MapFieldBase::SyncMapWithRepeatedField(); return MapFieldLiteType::GetInternalMap().size(); } @@ -105,9 +221,65 @@ template ::Clear() { - SyncMapWithRepeatedField(); + MapFieldBase::SyncMapWithRepeatedField(); MapFieldLiteType::MutableInternalMap()->clear(); - SetMapDirty(); + MapFieldBase::SetMapDirty(); +} + +template +void MapField::SetMapIteratorValue( + MapIterator* map_iter) const { + const Map& map = GetMap(); + typename Map::const_iterator iter = + TypeDefinedMapFieldBase::InternalGetIterator(map_iter); + if (iter == map.end()) return; + SetMapKey(&map_iter->key_, iter->first); + map_iter->value_.SetValue(&iter->second); +} + +template +bool MapField::ContainsMapKey( + const MapKey& map_key) const { + const Map& map = GetMap(); + const Key& key = UnwrapMapKey(map_key); + typename Map::const_iterator iter = map.find(key); + return iter != map.end(); +} + +template +bool MapField::InsertMapValue(const MapKey& map_key, + MapValueRef* val) { + Map* map = MutableMap(); + bool result = false; + const Key& key = UnwrapMapKey(map_key); + if (map->end() == map->find(key)) { + result = true; + } + val->SetValue(&((*map)[key])); + return result; +} + +template +bool MapField::DeleteMapValue( + const MapKey& map_key) { + const Key& key = UnwrapMapKey(map_key); + return MutableMap()->erase(key); } template & MapField::GetMap() const { - SyncMapWithRepeatedField(); + MapFieldBase::SyncMapWithRepeatedField(); return MapFieldLiteType::GetInternalMap(); } @@ -128,9 +300,9 @@ template * MapField::MutableMap() { - SyncMapWithRepeatedField(); + MapFieldBase::SyncMapWithRepeatedField(); Map* result = MapFieldLiteType::MutableInternalMap(); - SetMapDirty(); + MapFieldBase::SetMapDirty(); return result; } @@ -143,10 +315,10 @@ MapField::MergeFrom( const MapFieldLiteType& other) { const MapField& down_other = down_cast(other); - SyncMapWithRepeatedField(); + MapFieldBase::SyncMapWithRepeatedField(); down_other.SyncMapWithRepeatedField(); MapFieldLiteType::MergeFrom(other); - SetMapDirty(); + MapFieldBase::SetMapDirty(); } template ::Swap( MapFieldLiteType* other) { MapField* down_other = down_cast(other); - std::swap(repeated_field_, down_other->repeated_field_); + std::swap(MapFieldBase::repeated_field_, down_other->repeated_field_); MapFieldLiteType::Swap(other); - std::swap(state_, down_other->state_); + std::swap(MapFieldBase::state_, down_other->state_); } template ::SetEntryDescriptor( const Descriptor** descriptor) { - entry_descriptor_ = descriptor; + MapFieldBase::entry_descriptor_ = descriptor; } template ::SetAssignDescriptorCallback(void (*callback)()) { - assign_descriptor_callback_ = callback; + MapFieldBase::assign_descriptor_callback_ = callback; } template ::SyncRepeatedFieldWithMapNoLock() const { - if (repeated_field_ == NULL) { + if (MapFieldBase::repeated_field_ == NULL) { if (MapFieldBase::arena_ == NULL) { - repeated_field_ = new RepeatedPtrField(); + MapFieldBase::repeated_field_ = new RepeatedPtrField(); } else { - repeated_field_ = Arena::CreateMessage >( - MapFieldBase::arena_); + MapFieldBase::repeated_field_ = + Arena::CreateMessage >( + MapFieldBase::arena_); } } const Map& map = GetInternalMap(); RepeatedPtrField* repeated_field = - reinterpret_cast*>(repeated_field_); + reinterpret_cast*>( + MapFieldBase::repeated_field_); repeated_field->Clear(); @@ -246,7 +420,9 @@ MapField::SyncMapWithRepeatedFieldNoLock() const { Map* map = const_cast(this)->MutableInternalMap(); RepeatedPtrField* repeated_field = - reinterpret_cast*>(repeated_field_); + reinterpret_cast*>( + MapFieldBase::repeated_field_); + GOOGLE_CHECK(MapFieldBase::repeated_field_ != NULL); map->clear(); for (typename RepeatedPtrField::iterator it = repeated_field->begin(); it != repeated_field->end(); ++it) { @@ -267,15 +443,15 @@ int MapField::SpaceUsedExcludingSelfNoLock() const { int size = 0; - if (repeated_field_ != NULL) { - size += repeated_field_->SpaceUsedExcludingSelf(); + if (MapFieldBase::repeated_field_ != NULL) { + size += MapFieldBase::repeated_field_->SpaceUsedExcludingSelf(); } Map* map = const_cast(this)->MutableInternalMap(); size += sizeof(*map); for (typename Map::iterator it = map->begin(); it != map->end(); ++it) { - size += KeyHandler::SpaceUsedInMap(it->first); - size += ValHandler::SpaceUsedInMap(it->second); + size += KeyTypeHandler::SpaceUsedInMap(it->first); + size += ValueTypeHandler::SpaceUsedInMap(it->second); } return size; } @@ -289,10 +465,11 @@ MapField::InitDefaultEntryOnce() const { if (default_entry_ == NULL) { - InitMetadataOnce(); - GOOGLE_CHECK(*entry_descriptor_ != NULL); + MapFieldBase::InitMetadataOnce(); + GOOGLE_CHECK(*MapFieldBase::entry_descriptor_ != NULL); default_entry_ = down_cast( - MessageFactory::generated_factory()->GetPrototype(*entry_descriptor_)); + MessageFactory::generated_factory()->GetPrototype( + *MapFieldBase::entry_descriptor_)); } } diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc index e3a640795d..2ff1d6bbc4 100644 --- a/src/google/protobuf/map_field_test.cc +++ b/src/google/protobuf/map_field_test.cc @@ -34,6 +34,7 @@ #include #endif +#include #include #include #include @@ -74,6 +75,28 @@ class MapFieldBaseStub : public MapFieldBase { bool IsRepeatedClean() { return state_ != 1; } void SetMapDirty() { state_ = 0; } void SetRepeatedDirty() { state_ = 1; } + bool ContainsMapKey(const MapKey& map_key) const { + return false; + } + bool InsertMapValue(const MapKey& map_key, MapValueRef* val) { + return false; + } + bool DeleteMapValue(const MapKey& map_key) { + return false; + } + bool EqualIterator(const MapIterator& a, const MapIterator& b) const { + return false; + } + int size() const { return 0; } + void MapBegin(MapIterator* map_iter) const {} + void MapEnd(MapIterator* map_iter) const {} + void InitializeIterator(MapIterator* map_iter) const {} + void DeleteIterator(MapIterator* map_iter) const {} + void CopyIterator(MapIterator* this_iterator, + const MapIterator& other_iterator) const {} + void IncreaseIterator(MapIterator* map_iter) const {} + void SetDefaultMessageEntry(const Message* message) const {} + const Message* GetDefaultMessageEntry() const { return NULL; } }; class MapFieldBasePrimitiveTest : public ::testing::Test { diff --git a/src/google/protobuf/map_lite_unittest.proto b/src/google/protobuf/map_lite_unittest.proto index c69e8d9456..0592dd7a63 100644 --- a/src/google/protobuf/map_lite_unittest.proto +++ b/src/google/protobuf/map_lite_unittest.proto @@ -34,6 +34,7 @@ option cc_enable_arenas = true; option optimize_for = LITE_RUNTIME; import "google/protobuf/unittest_lite.proto"; +import "google/protobuf/unittest_no_arena_lite.proto"; package protobuf_unittest; @@ -72,8 +73,12 @@ message TestArenaMapLite { map map_int32_float = 11; map map_int32_double = 12; map map_bool_bool = 13; - map map_int32_enum = 14; - map map_int32_foreign_message = 15; + map map_string_string = 14; + map map_int32_bytes = 15; + map map_int32_enum = 16; + map map_int32_foreign_message = 17; + map + map_int32_foreign_message_no_arena = 18; } // Test embeded message with required fields @@ -81,10 +86,6 @@ message TestRequiredMessageMapLite { map map_field = 1; } -message TestEnumStartWithNonZeroMapLite { - map map_field = 101; -} - message TestEnumMapLite { map known_map_field = 101; map unknown_map_field = 102; @@ -112,10 +113,6 @@ enum Proto2MapEnumPlusExtraLite { E_PROTO2_MAP_ENUM_EXTRA_LITE = 3; } -enum Proto2MapEnumStartWithNonZeroLite { - PROTO2_NON_ZERO_MAP_ENUM_FOO_LITE = 1; -} - enum MapEnumLite { MAP_ENUM_FOO_LITE = 0; MAP_ENUM_BAR_LITE = 1; diff --git a/src/google/protobuf/map_proto2_unittest.proto b/src/google/protobuf/map_proto2_unittest.proto index 3d4af28efe..6f9d6165c6 100644 --- a/src/google/protobuf/map_proto2_unittest.proto +++ b/src/google/protobuf/map_proto2_unittest.proto @@ -49,10 +49,6 @@ enum Proto2MapEnumPlusExtra { E_PROTO2_MAP_ENUM_EXTRA = 3; } -enum Proto2MapEnumStartWithNonZero { - PROTO2_NON_ZERO_MAP_ENUM_FOO = 1; -} - message TestEnumMap { map known_map_field = 101; map unknown_map_field = 102; @@ -62,7 +58,3 @@ message TestEnumMapPlusExtra { map known_map_field = 101; map unknown_map_field = 102; } - -message TestEnumStartWithNonZeroMap { - map map_field = 101; -} diff --git a/src/google/protobuf/map_test.cc b/src/google/protobuf/map_test.cc index d62ec85f3c..a74801d92d 100644 --- a/src/google/protobuf/map_test.cc +++ b/src/google/protobuf/map_test.cc @@ -36,7 +36,9 @@ #include #include +#include #include +#include #include #include #include @@ -70,6 +72,7 @@ namespace google { using google::protobuf::unittest::ForeignMessage; using google::protobuf::unittest::TestAllTypes; using google::protobuf::unittest::TestMap; +using google::protobuf::unittest::TestRecursiveMapMessage; namespace protobuf { namespace internal { @@ -200,6 +203,21 @@ TEST_F(MapImplTest, ImmutableAtNonExistDeathTest) { EXPECT_DEATH(const_map_.at(0), ""); } +TEST_F(MapImplTest, UsageErrors) { + MapKey key; + key.SetInt64Value(1); + EXPECT_DEATH(key.GetUInt64Value(), + "Protocol Buffer map usage error:\n" + "MapKey::GetUInt64Value type does not match\n" + " Expected : uint64\n" + " Actual : int64"); + + MapValueRef value; + EXPECT_DEATH(value.SetFloatValue(0.1), + "Protocol Buffer map usage error:\n" + "MapValueRef::type MapValueRef is not initialized."); +} + #endif // PROTOBUF_HAS_DEATH_TEST TEST_F(MapImplTest, CountNonExist) { @@ -475,6 +493,23 @@ TEST_F(MapImplTest, CopyConstructor) { EXPECT_EQ(value2, other.at(key2)); } +TEST_F(MapImplTest, IterConstructor) { + int32 key1 = 0; + int32 key2 = 1; + int32 value1 = 100; + int32 value2 = 101; + + std::map map; + map[key1] = value1; + map[key2] = value2; + + Map new_map(map.begin(), map.end()); + + EXPECT_EQ(2, new_map.size()); + EXPECT_EQ(value1, new_map.at(key1)); + EXPECT_EQ(value2, new_map.at(key2)); +} + TEST_F(MapImplTest, Assigner) { int32 key1 = 0; int32 key2 = 1; @@ -1140,6 +1175,19 @@ TEST_F(MapFieldReflectionTest, RepeatedFieldRefForRegularFields) { mmf_int32_foreign_message.Add(*entry_int32_foreign_message); EXPECT_EQ(1234, message.map_int32_foreign_message().at(4321).c()); + // Test Reflection::AddAllocatedMessage + Message* free_entry_string_string = MessageFactory::generated_factory() + ->GetPrototype(fd_map_string_string->message_type()) + ->New(); + entry_string_string->GetReflection()->SetString( + free_entry_string_string, + fd_map_string_string->message_type()->field(0), "4321"); + entry_string_string->GetReflection()->SetString( + free_entry_string_string, fd_map_string_string->message_type()->field(1), + "1234"); + refl->AddAllocatedMessage(&message, fd_map_string_string, + free_entry_string_string); + // Test MutableRepeatedFieldRef::RemoveLast() mmf_int32_int32.RemoveLast(); mmf_int32_double.RemoveLast(); @@ -1147,7 +1195,7 @@ TEST_F(MapFieldReflectionTest, RepeatedFieldRefForRegularFields) { mmf_int32_foreign_message.RemoveLast(); EXPECT_EQ(10, message.map_int32_int32().size()); EXPECT_EQ(10, message.map_int32_double().size()); - EXPECT_EQ(10, message.map_string_string().size()); + EXPECT_EQ(11, message.map_string_string().size()); EXPECT_EQ(10, message.map_int32_foreign_message().size()); // Test MutableRepeatedFieldRef::SwapElements() @@ -1398,9 +1446,9 @@ TEST(GeneratedMapFieldTest, SetMapFieldsInitialized) { } TEST(GeneratedMapFieldTest, Proto2SetMapFieldsInitialized) { - unittest::TestEnumStartWithNonZeroMap message; - EXPECT_EQ(unittest::PROTO2_NON_ZERO_MAP_ENUM_FOO, - (*message.mutable_map_field())[0]); + unittest::TestEnumMap message; + EXPECT_EQ(unittest::PROTO2_MAP_ENUM_FOO, + (*message.mutable_known_map_field())[0]); } TEST(GeneratedMapFieldTest, Clear) { @@ -1526,12 +1574,28 @@ TEST(GeneratedMapFieldTest, CopyFromDynamicMessage) { google::protobuf::scoped_ptr message1; message1.reset( factory.GetPrototype(unittest::TestMap::descriptor())->New()); - - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.SetMapFieldsViaReflection(message1.get()); reflection_tester.ExpectMapFieldsSetViaReflection(*message1); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(message1.get()); + message2.CopyFrom(*message1); + MapTestUtil::ExpectMapFieldsSet(message2); +} + +TEST(GeneratedMapFieldTest, CopyFromDynamicMessageMapReflection) { + unittest::TestMap message2; + // Construct a new version of the dynamic message via the factory. + DynamicMessageFactory factory; + google::protobuf::scoped_ptr message1; + message1.reset( + factory.GetPrototype(unittest::TestMap::descriptor())->New()); + MapReflectionTester reflection_tester( + unittest::TestMap::descriptor()); + reflection_tester.SetMapFieldsViaMapReflection(message1.get()); + reflection_tester.ExpectMapFieldsSetViaReflection(*message1); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(message1.get()); message2.CopyFrom(*message1); MapTestUtil::ExpectMapFieldsSet(message2); } @@ -1547,12 +1611,43 @@ TEST(GeneratedMapFieldTest, DynamicMessageCopyFrom) { message1.reset( factory.GetPrototype(unittest::TestMap::descriptor())->New()); - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( + unittest::TestMap::descriptor()); + message1->MergeFrom(message2); + reflection_tester.ExpectMapFieldsSetViaReflection(*message1); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(message1.get()); +} + +TEST(GeneratedMapFieldTest, DynamicMessageCopyFromMapReflection) { + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); + unittest::TestMap message2; + reflection_tester.SetMapFieldsViaMapReflection(&message2); + + // Construct a dynamic message via the factory. + DynamicMessageFactory factory; + google::protobuf::scoped_ptr message1; + message1.reset( + factory.GetPrototype(unittest::TestMap::descriptor())->New()); + message1->MergeFrom(message2); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(message1.get()); reflection_tester.ExpectMapFieldsSetViaReflection(*message1); } +TEST(GeneratedMapFieldTest, SyncDynamicMapWithRepeatedField) { + // Construct a dynamic message via the factory. + MapReflectionTester reflection_tester( + unittest::TestMap::descriptor()); + DynamicMessageFactory factory; + google::protobuf::scoped_ptr message; + message.reset( + factory.GetPrototype(unittest::TestMap::descriptor())->New()); + reflection_tester.SetMapFieldsViaReflection(message.get()); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(message.get()); + reflection_tester.ExpectMapFieldsSetViaReflection(*message); +} + #endif // !PROTOBUF_TEST_NO_DESCRIPTORS TEST(GeneratedMapFieldTest, NonEmptyMergeFrom) { @@ -1779,7 +1874,7 @@ TEST(GeneratedMapFieldTest, IsInitialized) { TEST(GeneratedMapFieldReflectionTest, SpaceUsed) { unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.SetMapFieldsViaReflection(&message); @@ -1790,11 +1885,12 @@ TEST(GeneratedMapFieldReflectionTest, Accessors) { // Set every field to a unique value then go back and check all those // values. unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.SetMapFieldsViaReflection(&message); MapTestUtil::ExpectMapFieldsSet(message); reflection_tester.ExpectMapFieldsSetViaReflection(message); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(&message); reflection_tester.ModifyMapFieldsViaReflection(&message); MapTestUtil::ExpectMapFieldsModified(message); @@ -1848,15 +1944,16 @@ TEST(GeneratedMapFieldReflectionTest, ClearField) { MapTestUtil::SetMapFields(&message); MapTestUtil::ExpectMapFieldsSet(message); - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.ClearMapFieldsViaReflection(&message); - MapTestUtil::ExpectClear(message); + reflection_tester.ExpectClearViaReflection(message); + reflection_tester.ExpectClearViaReflectionIterator(&message); } TEST(GeneratedMapFieldReflectionTest, RemoveLast) { unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); MapTestUtil::SetMapFields(&message); @@ -1875,7 +1972,7 @@ TEST(GeneratedMapFieldReflectionTest, RemoveLast) { TEST(GeneratedMapFieldReflectionTest, ReleaseLast) { unittest::TestMap message; const Descriptor* descriptor = message.GetDescriptor(); - MapTestUtil::MapReflectionTester reflection_tester(descriptor); + MapReflectionTester reflection_tester(descriptor); MapTestUtil::SetMapFields(&message); @@ -1904,7 +2001,7 @@ TEST(GeneratedMapFieldReflectionTest, ReleaseLast) { TEST(GeneratedMapFieldReflectionTest, SwapElements) { unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); MapTestUtil::SetMapFields(&message); @@ -1944,7 +2041,7 @@ TEST(GeneratedMapFieldReflectionTest, SwapElements) { TEST(GeneratedMapFieldReflectionTest, MutableUnknownFields) { unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.MutableUnknownFieldsOfMapFieldsViaReflection(&message); } @@ -2000,24 +2097,35 @@ TEST(GeneratedMapFieldReflectionTest, MergeFromClearMapEntry) { TEST(GeneratedMapFieldReflectionTest, MapEntryClear) { unittest::TestMap message; - MapTestUtil::MapReflectionTester reflection_tester( + MapReflectionTester reflection_tester( unittest::TestMap::descriptor()); reflection_tester.MutableUnknownFieldsOfMapFieldsViaReflection(&message); } TEST(GeneratedMapFieldReflectionTest, Proto2MapEntryClear) { - unittest::TestEnumStartWithNonZeroMap message; + unittest::TestEnumMap message; const Descriptor* descriptor = message.GetDescriptor(); const FieldDescriptor* field_descriptor = - descriptor->FindFieldByName("map_field"); + descriptor->FindFieldByName("known_map_field"); const FieldDescriptor* value_descriptor = field_descriptor->message_type()->FindFieldByName("value"); Message* sub_message = message.GetReflection()->AddMessage(&message, field_descriptor); - EXPECT_EQ(1, sub_message->GetReflection()->GetEnumValue(*sub_message, + EXPECT_EQ(0, sub_message->GetReflection()->GetEnumValue(*sub_message, value_descriptor)); } +// Map Reflection API Test ========================================= + +TEST(GeneratedMapFieldReflectionTest, SetViaMapReflection) { + unittest::TestMap message; + MapReflectionTester reflection_tester( + unittest::TestMap::descriptor()); + reflection_tester.SetMapFieldsViaMapReflection(&message); + reflection_tester.ExpectMapFieldsSetViaReflection(message); + reflection_tester.ExpectMapFieldsSetViaReflectionIterator(&message); +} + // Dynamic Message Test ============================================= class MapFieldInDynamicMessageTest : public testing::Test { @@ -2025,6 +2133,7 @@ class MapFieldInDynamicMessageTest : public testing::Test { const DescriptorPool* pool_; DynamicMessageFactory factory_; const Descriptor* map_descriptor_; + const Descriptor* recursive_map_descriptor_; const Message* map_prototype_; MapFieldInDynamicMessageTest() @@ -2033,7 +2142,10 @@ class MapFieldInDynamicMessageTest : public testing::Test { virtual void SetUp() { map_descriptor_ = pool_->FindMessageTypeByName("protobuf_unittest.TestMap"); + recursive_map_descriptor_ = + pool_->FindMessageTypeByName("protobuf_unittest.TestRecursiveMapMessage"); ASSERT_TRUE(map_descriptor_ != NULL); + ASSERT_TRUE(recursive_map_descriptor_ != NULL); map_prototype_ = factory_.GetPrototype(map_descriptor_); } }; @@ -2043,19 +2155,19 @@ TEST_F(MapFieldInDynamicMessageTest, MapIndependentOffsets) { // one to a unique value then checking that they all still have those // unique values (i.e. they don't stomp each other). scoped_ptr message(map_prototype_->New()); - MapTestUtil::MapReflectionTester reflection_tester(map_descriptor_); + MapReflectionTester reflection_tester(map_descriptor_); reflection_tester.SetMapFieldsViaReflection(message.get()); reflection_tester.ExpectMapFieldsSetViaReflection(*message); } -TEST_F(MapFieldInDynamicMessageTest, Map) { +TEST_F(MapFieldInDynamicMessageTest, DynamicMapReflection) { // Check that map fields work properly. scoped_ptr message(map_prototype_->New()); // Check set functions. - MapTestUtil::MapReflectionTester reflection_tester(map_descriptor_); - reflection_tester.SetMapFieldsViaReflection(message.get()); + MapReflectionTester reflection_tester(map_descriptor_); + reflection_tester.SetMapFieldsViaMapReflection(message.get()); reflection_tester.ExpectMapFieldsSetViaReflection(*message); } @@ -2066,7 +2178,7 @@ TEST_F(MapFieldInDynamicMessageTest, MapSpaceUsed) { // to test very much here. Just make sure it appears to be working. scoped_ptr message(map_prototype_->New()); - MapTestUtil::MapReflectionTester reflection_tester(map_descriptor_); + MapReflectionTester reflection_tester(map_descriptor_); int initial_space_used = message->SpaceUsed(); @@ -2074,6 +2186,15 @@ TEST_F(MapFieldInDynamicMessageTest, MapSpaceUsed) { EXPECT_LT(initial_space_used, message->SpaceUsed()); } +TEST_F(MapFieldInDynamicMessageTest, RecursiveMap) { + TestRecursiveMapMessage from; + (*from.mutable_a())[0]; + string data = from.SerializeAsString(); + google::protobuf::scoped_ptr to( + factory_.GetPrototype(recursive_map_descriptor_)->New()); + ASSERT_TRUE(to->ParseFromString(data)); +} + // ReflectionOps Test =============================================== TEST(ReflectionOpsForMapFieldTest, MapSanityCheck) { @@ -2260,7 +2381,7 @@ TEST(TextFormatMapTest, SerializeAndParse) { TEST(TextFormatMapTest, Sorted) { unittest::TestMap message; - MapTestUtil::MapReflectionTester tester(message.GetDescriptor()); + MapReflectionTester tester(message.GetDescriptor()); tester.SetMapFieldsViaReflection(&message); string expected_text; @@ -2292,7 +2413,8 @@ TEST(ArenaTest, ParsingAndSerializingNoHeapAllocation) { data.reserve(128 * 1024); { - NoHeapChecker no_heap; + // TODO(teboring): Enable no heap check when ArenaStringPtr is used in map. + // NoHeapChecker no_heap; unittest::TestArenaMap* from = Arena::CreateMessage(&arena); @@ -2326,6 +2448,22 @@ TEST(ArenaTest, RelfectionInTextFormat) { MapTestUtil::ExpectArenaMapFieldsSet(*to); } +// Make sure the memory allocated for string in map is deallocated. +TEST(ArenaTest, StringMapNoLeak) { + Arena arena; + unittest::TestArenaMap* message = + Arena::CreateMessage(&arena); + string data; + // String with length less than 16 will not be allocated from heap. + int original_capacity = data.capacity(); + while (data.capacity() <= original_capacity) { + data.append("a"); + } + (*message->mutable_map_string_string())[data] = data; + // We rely on heap checkers to detect memory leak for us. + ASSERT_FALSE(message == NULL); +} + } // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/map_test_util.cc b/src/google/protobuf/map_test_util.cc index 1713e37364..ae094647bb 100644 --- a/src/google/protobuf/map_test_util.cc +++ b/src/google/protobuf/map_test_util.cc @@ -209,7 +209,7 @@ std::vector MapTestUtil::GetMapEntriesFromRelease( return result; } -MapTestUtil::MapReflectionTester::MapReflectionTester( +MapReflectionTester::MapReflectionTester( const Descriptor* base_descriptor) : base_descriptor_(base_descriptor) { const DescriptorPool* pool = base_descriptor->file()->pool(); @@ -329,14 +329,14 @@ MapTestUtil::MapReflectionTester::MapReflectionTester( } // Shorthand to get a FieldDescriptor for a field of unittest::TestMap. -const FieldDescriptor* MapTestUtil::MapReflectionTester::F(const string& name) { +const FieldDescriptor* MapReflectionTester::F(const string& name) { const FieldDescriptor* result = NULL; result = base_descriptor_->FindFieldByName(name); GOOGLE_CHECK(result != NULL); return result; } -void MapTestUtil::MapReflectionTester::SetMapFieldsViaReflection( +void MapReflectionTester::SetMapFieldsViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); Message* sub_message = NULL; @@ -555,7 +555,196 @@ void MapTestUtil::MapReflectionTester::SetMapFieldsViaReflection( SetInt32(sub_foreign_message, foreign_c_, 1); } -void MapTestUtil::MapReflectionTester::ClearMapFieldsViaReflection( +void MapReflectionTester::SetMapFieldsViaMapReflection( + Message* message) { + const Reflection* reflection = message->GetReflection(); + + Message* sub_foreign_message = NULL; + MapValueRef map_val; + + // Add first element. + MapKey map_key; + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_int32"), map_key, &map_val)); + map_val.SetInt32Value(0); + + map_key.SetInt64Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int64_int64"), map_key, &map_val)); + map_val.SetInt64Value(0); + + map_key.SetUInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_uint32_uint32"), map_key, &map_val)); + map_val.SetUInt32Value(0); + + map_key.SetUInt64Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_uint64_uint64"), map_key, &map_val)); + map_val.SetUInt64Value(0); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_sint32_sint32"), map_key, &map_val)); + map_val.SetInt32Value(0); + + map_key.SetInt64Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_sint64_sint64"), map_key, &map_val)); + map_val.SetInt64Value(0); + + map_key.SetUInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_fixed32_fixed32"), map_key, &map_val)); + map_val.SetUInt32Value(0); + + map_key.SetUInt64Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_fixed64_fixed64"), map_key, &map_val)); + map_val.SetUInt64Value(0); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_sfixed32_sfixed32"), map_key, &map_val)); + map_val.SetInt32Value(0); + + map_key.SetInt64Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_sfixed64_sfixed64"), map_key, &map_val)); + map_val.SetInt64Value(0); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_float"), map_key, &map_val)); + map_val.SetFloatValue(0.0); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_double"), map_key, &map_val)); + map_val.SetDoubleValue(0.0); + + map_key.SetBoolValue(false); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_bool_bool"), map_key, &map_val)); + map_val.SetBoolValue(false); + + map_key.SetStringValue("0"); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_string_string"), map_key, &map_val)); + map_val.SetStringValue("0"); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_bytes"), map_key, &map_val)); + map_val.SetStringValue("0"); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_enum"), map_key, &map_val)); + map_val.SetEnumValue(map_enum_bar_->number()); + + map_key.SetInt32Value(0); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_foreign_message"), map_key, &map_val)); + sub_foreign_message = map_val.MutableMessageValue(); + sub_foreign_message->GetReflection()->SetInt32( + sub_foreign_message, foreign_c_, 0); + + // Add second element + map_key.SetInt32Value(1); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_int32"), map_key, &map_val)); + map_val.SetInt32Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_int32_int32"), map_key, &map_val)); + + map_key.SetInt64Value(1); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int64_int64"), map_key, &map_val)); + map_val.SetInt64Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_int64_int64"), map_key, &map_val)); + + map_key.SetUInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_uint32_uint32"), map_key, &map_val); + map_val.SetUInt32Value(1); + + map_key.SetUInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_uint64_uint64"), map_key, &map_val); + map_val.SetUInt64Value(1); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sint32_sint32"), map_key, &map_val); + map_val.SetInt32Value(1); + + map_key.SetInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sint64_sint64"), map_key, &map_val); + map_val.SetInt64Value(1); + + map_key.SetUInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_fixed32_fixed32"), map_key, &map_val); + map_val.SetUInt32Value(1); + + map_key.SetUInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_fixed64_fixed64"), map_key, &map_val); + map_val.SetUInt64Value(1); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sfixed32_sfixed32"), map_key, &map_val); + map_val.SetInt32Value(1); + + map_key.SetInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sfixed64_sfixed64"), map_key, &map_val); + map_val.SetInt64Value(1); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_float"), map_key, &map_val); + map_val.SetFloatValue(1.0); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_double"), map_key, &map_val); + map_val.SetDoubleValue(1.0); + + map_key.SetBoolValue(true); + reflection->InsertOrLookupMapValue( + message, F("map_bool_bool"), map_key, &map_val); + map_val.SetBoolValue(true); + + map_key.SetStringValue("1"); + reflection->InsertOrLookupMapValue( + message, F("map_string_string"), map_key, &map_val); + map_val.SetStringValue("1"); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_bytes"), map_key, &map_val); + map_val.SetStringValue("1"); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_enum"), map_key, &map_val); + map_val.SetEnumValue(map_enum_baz_->number()); + + map_key.SetInt32Value(1); + EXPECT_TRUE(reflection->InsertOrLookupMapValue( + message, F("map_int32_foreign_message"), map_key, &map_val)); + sub_foreign_message = map_val.MutableMessageValue(); + sub_foreign_message->GetReflection()->SetInt32( + sub_foreign_message, foreign_c_, 1); +} + +void MapReflectionTester::ClearMapFieldsViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); @@ -578,97 +767,103 @@ void MapTestUtil::MapReflectionTester::ClearMapFieldsViaReflection( reflection->ClearField(message, F("map_int32_foreign_message")); } -void MapTestUtil::MapReflectionTester::ModifyMapFieldsViaReflection( +void MapReflectionTester::ModifyMapFieldsViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); - Message* sub_message; + MapValueRef map_val; Message* sub_foreign_message; - // Find out which one's key is 0. - int size = reflection->FieldSize(*message, F("map_int32_int32")); - int target = 0; - for (int i = 0; i < size; i++) { - const Message& temp_message = reflection - ->GetRepeatedMessage(*message, F("map_int32_int32"), i); - if (temp_message.GetReflection() - ->GetInt32(temp_message, map_int32_int32_key_) == 1) { - target = i; - } - } - - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_int32"), target); - sub_message->GetReflection() - ->SetInt32(sub_message, map_int32_int32_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int64_int64"), target); - sub_message->GetReflection() - ->SetInt64(sub_message, map_int64_int64_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_uint32_uint32"), target); - sub_message->GetReflection() - ->SetUInt32(sub_message, map_uint32_uint32_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_uint64_uint64"), target); - sub_message->GetReflection() - ->SetUInt64(sub_message, map_uint64_uint64_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_sint32_sint32"), target); - sub_message->GetReflection() - ->SetInt32(sub_message, map_sint32_sint32_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_sint64_sint64"), target); - sub_message->GetReflection() - ->SetInt64(sub_message, map_sint64_sint64_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_fixed32_fixed32"), target); - sub_message->GetReflection() - ->SetUInt32(sub_message, map_fixed32_fixed32_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_fixed64_fixed64"), target); - sub_message->GetReflection() - ->SetUInt64(sub_message, map_fixed64_fixed64_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_sfixed32_sfixed32"), target); - sub_message->GetReflection() - ->SetInt32(sub_message, map_sfixed32_sfixed32_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_sfixed64_sfixed64"), target); - sub_message->GetReflection() - ->SetInt64(sub_message, map_sfixed64_sfixed64_val_, 2); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_float"), target); - sub_message->GetReflection() - ->SetFloat(sub_message, map_int32_float_val_, 2.0); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_double"), target); - sub_message->GetReflection() - ->SetDouble(sub_message, map_int32_double_val_, 2.0); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_bool_bool"), target); - sub_message->GetReflection() - ->SetBool(sub_message, map_bool_bool_val_, false); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_string_string"), target); - sub_message->GetReflection() - ->SetString(sub_message, map_string_string_val_, "2"); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_bytes"), target); - sub_message->GetReflection() - ->SetString(sub_message, map_int32_bytes_val_, "2"); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_enum"), target); - sub_message->GetReflection() - ->SetEnum(sub_message, map_int32_enum_val_, map_enum_foo_); - sub_message = reflection - ->MutableRepeatedMessage(message, F("map_int32_foreign_message"), target); - sub_foreign_message = sub_message->GetReflection()-> - MutableMessage(sub_message, map_int32_foreign_message_val_, NULL); - sub_foreign_message->GetReflection()-> - SetInt32(sub_foreign_message, foreign_c_, 2); + // Modify the second element + MapKey map_key; + map_key.SetInt32Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_int32_int32"), map_key, &map_val)); + map_val.SetInt32Value(2); + + map_key.SetInt64Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_int64_int64"), map_key, &map_val)); + map_val.SetInt64Value(2); + + map_key.SetUInt32Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_uint32_uint32"), map_key, &map_val)); + map_val.SetUInt32Value(2); + + map_key.SetUInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_uint64_uint64"), map_key, &map_val); + map_val.SetUInt64Value(2); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sint32_sint32"), map_key, &map_val); + map_val.SetInt32Value(2); + + map_key.SetInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sint64_sint64"), map_key, &map_val); + map_val.SetInt64Value(2); + + map_key.SetUInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_fixed32_fixed32"), map_key, &map_val); + map_val.SetUInt32Value(2); + + map_key.SetUInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_fixed64_fixed64"), map_key, &map_val); + map_val.SetUInt64Value(2); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sfixed32_sfixed32"), map_key, &map_val); + map_val.SetInt32Value(2); + + map_key.SetInt64Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_sfixed64_sfixed64"), map_key, &map_val); + map_val.SetInt64Value(2); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_float"), map_key, &map_val); + map_val.SetFloatValue(2.0); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_double"), map_key, &map_val); + map_val.SetDoubleValue(2.0); + + map_key.SetBoolValue(true); + reflection->InsertOrLookupMapValue( + message, F("map_bool_bool"), map_key, &map_val); + map_val.SetBoolValue(false); + + map_key.SetStringValue("1"); + reflection->InsertOrLookupMapValue( + message, F("map_string_string"), map_key, &map_val); + map_val.SetStringValue("2"); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_bytes"), map_key, &map_val); + map_val.SetStringValue("2"); + + map_key.SetInt32Value(1); + reflection->InsertOrLookupMapValue( + message, F("map_int32_enum"), map_key, &map_val); + map_val.SetEnumValue(map_enum_foo_->number()); + + map_key.SetInt32Value(1); + EXPECT_FALSE(reflection->InsertOrLookupMapValue( + message, F("map_int32_foreign_message"), map_key, &map_val)); + sub_foreign_message = map_val.MutableMessageValue(); + sub_foreign_message->GetReflection()->SetInt32( + sub_foreign_message, foreign_c_, 2); } -void MapTestUtil::MapReflectionTester::RemoveLastMapsViaReflection( +void MapReflectionTester::RemoveLastMapsViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); @@ -681,7 +876,7 @@ void MapTestUtil::MapReflectionTester::RemoveLastMapsViaReflection( } } -void MapTestUtil::MapReflectionTester::ReleaseLastMapsViaReflection( +void MapReflectionTester::ReleaseLastMapsViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); @@ -699,7 +894,7 @@ void MapTestUtil::MapReflectionTester::ReleaseLastMapsViaReflection( } } -void MapTestUtil::MapReflectionTester::SwapMapsViaReflection(Message* message) { +void MapReflectionTester::SwapMapsViaReflection(Message* message) { const Reflection* reflection = message->GetReflection(); vector output; reflection->ListFields(*message, &output); @@ -710,7 +905,7 @@ void MapTestUtil::MapReflectionTester::SwapMapsViaReflection(Message* message) { } } -void MapTestUtil::MapReflectionTester:: +void MapReflectionTester:: MutableUnknownFieldsOfMapFieldsViaReflection(Message* message) { const Reflection* reflection = message->GetReflection(); Message* sub_message = NULL; @@ -768,11 +963,12 @@ void MapTestUtil::MapReflectionTester:: NULL); } -void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( +void MapReflectionTester::ExpectMapFieldsSetViaReflection( const Message& message) { string scratch; const Reflection* reflection = message.GetReflection(); const Message* sub_message; + MapKey map_key; // ----------------------------------------------------------------- @@ -799,6 +995,7 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( map[0] = 0; map[1] = 1; for (int i = 0; i < 2; i++) { + // Check with RepeatedField Reflection sub_message = &reflection->GetRepeatedMessage(message, F("map_int32_int32"), i); int32 key = sub_message->GetReflection()->GetInt32( @@ -806,6 +1003,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int32 val = sub_message->GetReflection()->GetInt32( *sub_message, map_int32_int32_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_TRUE(reflection->ContainsMapKey( + message, F("map_int32_int32"), map_key)); } } { @@ -813,6 +1014,7 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( map[0] = 0; map[1] = 1; for (int i = 0; i < 2; i++) { + // Check with RepeatedField Reflection sub_message = &reflection->GetRepeatedMessage(message, F("map_int64_int64"), i); int64 key = sub_message->GetReflection()->GetInt64( @@ -820,6 +1022,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int64 val = sub_message->GetReflection()->GetInt64( *sub_message, map_int64_int64_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt64Value(key); + EXPECT_TRUE(reflection->ContainsMapKey( + message, F("map_int64_int64"), map_key)); } } { @@ -827,6 +1033,7 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( map[0] = 0; map[1] = 1; for (int i = 0; i < 2; i++) { + // Check with RepeatedField Reflection sub_message = &reflection->GetRepeatedMessage(message, F("map_uint32_uint32"), i); uint32 key = sub_message->GetReflection()->GetUInt32( @@ -834,6 +1041,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( uint32 val = sub_message->GetReflection()->GetUInt32( *sub_message, map_uint32_uint32_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetUInt32Value(key); + EXPECT_TRUE(reflection->ContainsMapKey( + message, F("map_uint32_uint32"), map_key)); } } { @@ -848,6 +1059,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( uint64 val = sub_message->GetReflection()->GetUInt64( *sub_message, map_uint64_uint64_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetUInt64Value(key); + EXPECT_TRUE(reflection->ContainsMapKey( + message, F("map_uint64_uint64"), map_key)); } } { @@ -862,6 +1077,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int32 val = sub_message->GetReflection()->GetInt32( *sub_message, map_sint32_sint32_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_sint32_sint32"), map_key)); } } { @@ -876,6 +1095,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int64 val = sub_message->GetReflection()->GetInt64( *sub_message, map_sint64_sint64_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt64Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_sint64_sint64"), map_key)); } } { @@ -890,6 +1113,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( uint32 val = sub_message->GetReflection()->GetUInt32( *sub_message, map_fixed32_fixed32_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetUInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_fixed32_fixed32"), map_key)); } } { @@ -904,6 +1131,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( uint64 val = sub_message->GetReflection()->GetUInt64( *sub_message, map_fixed64_fixed64_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetUInt64Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_fixed64_fixed64"), map_key)); } } { @@ -918,6 +1149,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int32 val = sub_message->GetReflection()->GetInt32( *sub_message, map_sfixed32_sfixed32_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_sfixed32_sfixed32"), map_key)); } } { @@ -932,6 +1167,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int64 val = sub_message->GetReflection()->GetInt64( *sub_message, map_sfixed64_sfixed64_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt64Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_sfixed64_sfixed64"), map_key)); } } { @@ -946,6 +1185,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( float val = sub_message->GetReflection()->GetFloat( *sub_message, map_int32_float_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_int32_float"), map_key)); } } { @@ -960,6 +1203,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( double val = sub_message->GetReflection()->GetDouble( *sub_message, map_int32_double_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_int32_double"), map_key)); } } { @@ -974,6 +1221,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( bool val = sub_message->GetReflection()->GetBool( *sub_message, map_bool_bool_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetBoolValue(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_bool_bool"), map_key)); } } { @@ -988,6 +1239,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( string val = sub_message->GetReflection()->GetString( *sub_message, map_string_string_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetStringValue(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_string_string"), map_key)); } } { @@ -1002,6 +1257,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( string val = sub_message->GetReflection()->GetString( *sub_message, map_int32_bytes_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_int32_bytes"), map_key)); } } { @@ -1016,6 +1275,10 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( const EnumValueDescriptor* val = sub_message->GetReflection()->GetEnum( *sub_message, map_int32_enum_val_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_int32_enum"), map_key)); } } { @@ -1032,11 +1295,245 @@ void MapTestUtil::MapReflectionTester::ExpectMapFieldsSetViaReflection( int32 val = foreign_message.GetReflection()->GetInt32( foreign_message, foreign_c_); EXPECT_EQ(map[key], val); + // Check with Map Reflection + map_key.SetInt32Value(key); + EXPECT_EQ(true, reflection->ContainsMapKey( + message, F("map_int32_foreign_message"), map_key)); } } } -void MapTestUtil::MapReflectionTester::ExpectClearViaReflection( +void MapReflectionTester::ExpectMapFieldsSetViaReflectionIterator( + Message* message) { + string scratch; + string serialized; + const Reflection* reflection = message->GetReflection(); + + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_int32"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int64_int64"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_uint32_uint32"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_uint64_uint64"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_sint32_sint32"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_sint64_sint64"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_fixed32_fixed32"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_fixed64_fixed64"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_sfixed32_sfixed32"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_sfixed64_sfixed64"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_float"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_double"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_bool_bool"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_string_string"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_bytes"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_enum"))); + ASSERT_EQ(2, reflection->FieldSize(*message, F("map_int32_foreign_message"))); + + { + std::map map; + map[0] = 0; + map[1] = 1; + int size = 0; + for (MapIterator iter = reflection->MapBegin(message, F("map_int32_int32")); + iter != reflection->MapEnd(message, F("map_int32_int32")); + ++iter, ++size) { + // Check const methods do not invalidate map. + message->DebugString(); + message->ShortDebugString(); + message->SerializeToString(&serialized); + message->SpaceUsed(); + message->ByteSize(); + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetInt32Value()); + } + EXPECT_EQ(size, 2); + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin(message, F("map_int64_int64")); + iter != reflection->MapEnd(message, F("map_int64_int64")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt64Value()], + iter.GetValueRef().GetInt64Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_uint32_uint32")); + iter != reflection->MapEnd(message, F("map_uint32_uint32")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetUInt32Value()], + iter.GetValueRef().GetUInt32Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_uint64_uint64")); + iter != reflection->MapEnd(message, F("map_uint64_uint64")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetUInt64Value()], + iter.GetValueRef().GetUInt64Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_sint32_sint32")); + iter != reflection->MapEnd(message, F("map_sint32_sint32")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetInt32Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_sint64_sint64")); + iter != reflection->MapEnd(message, F("map_sint64_sint64")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt64Value()], + iter.GetValueRef().GetInt64Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_fixed32_fixed32")); + iter != reflection->MapEnd(message, F("map_fixed32_fixed32")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetUInt32Value()], + iter.GetValueRef().GetUInt32Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_fixed64_fixed64")); + iter != reflection->MapEnd(message, F("map_fixed64_fixed64")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetUInt64Value()], + iter.GetValueRef().GetUInt64Value()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + for (MapIterator iter = reflection->MapBegin( + message, F("map_sfixed32_sfixed32")); + iter != reflection->MapEnd(message, F("map_sfixed32_sfixed32")); + ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetInt32Value()); + } + } + { + std::map map; + map[0] = 0.0; + map[1] = 1.0; + for (MapIterator iter = reflection->MapBegin(message, F("map_int32_float")); + iter != reflection->MapEnd(message, F("map_int32_float")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetFloatValue()); + } + } + { + std::map map; + map[0] = 0.0; + map[1] = 1.0; + for (MapIterator iter = reflection->MapBegin( + message, F("map_int32_double")); + iter != reflection->MapEnd(message, F("map_int32_double")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetDoubleValue()); + } + } + { + std::map map; + map[false] = false; + map[true] = true; + for (MapIterator iter = reflection->MapBegin(message, F("map_bool_bool")); + iter != reflection->MapEnd(message, F("map_bool_bool")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetBoolValue()], + iter.GetValueRef().GetBoolValue()); + } + } + { + std::map map; + map["0"] = "0"; + map["1"] = "1"; + int size = 0; + for (MapIterator iter = reflection->MapBegin( + message, F("map_string_string")); + iter != reflection->MapEnd(message, F("map_string_string")); + ++iter, ++size) { + // Check const methods do not invalidate map. + message->DebugString(); + message->ShortDebugString(); + message->SerializeToString(&serialized); + message->SpaceUsed(); + message->ByteSize(); + EXPECT_EQ(map[iter.GetKey().GetStringValue()], + iter.GetValueRef().GetStringValue()); + } + EXPECT_EQ(size, 2); + } + { + std::map map; + map[0] = "0"; + map[1] = "1"; + for (MapIterator iter = reflection->MapBegin(message, F("map_int32_bytes")); + iter != reflection->MapEnd(message, F("map_int32_bytes")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + iter.GetValueRef().GetStringValue()); + } + } + { + std::map map; + map[0] = map_enum_bar_; + map[1] = map_enum_baz_; + for (MapIterator iter = reflection->MapBegin(message, F("map_int32_enum")); + iter != reflection->MapEnd(message, F("map_int32_enum")); ++iter) { + EXPECT_EQ(map[iter.GetKey().GetInt32Value()]->number(), + iter.GetValueRef().GetEnumValue()); + } + } + { + std::map map; + map[0] = 0; + map[1] = 1; + int size = 0; + for (MapIterator iter = reflection->MapBegin( + message, F("map_int32_foreign_message")); + iter != reflection->MapEnd(message, F("map_int32_foreign_message")); + ++iter, ++size) { + // Check const methods do not invalidate map. + message->DebugString(); + message->ShortDebugString(); + message->SerializeToString(&serialized); + message->SpaceUsed(); + message->ByteSize(); + const Message& sub_message = iter.GetValueRef().GetMessageValue(); + EXPECT_EQ(map[iter.GetKey().GetInt32Value()], + sub_message.GetReflection()->GetInt32(sub_message, foreign_c_)); + } + EXPECT_EQ(size, 2); + } +} + +void MapReflectionTester::ExpectClearViaReflection( const Message& message) { const Reflection* reflection = message.GetReflection(); // Map fields are empty. @@ -1059,7 +1556,46 @@ void MapTestUtil::MapReflectionTester::ExpectClearViaReflection( EXPECT_EQ(0, reflection->FieldSize(message, F("map_int32_foreign_message"))); } -void MapTestUtil::MapReflectionTester::ExpectMapEntryClearViaReflection( +void MapReflectionTester::ExpectClearViaReflectionIterator( + Message* message) { + const Reflection* reflection = message->GetReflection(); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_int32")) == + reflection->MapEnd(message, F("map_int32_int32"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int64_int64")) == + reflection->MapEnd(message, F("map_int64_int64"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_uint32_uint32")) == + reflection->MapEnd(message, F("map_uint32_uint32"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_uint64_uint64")) == + reflection->MapEnd(message, F("map_uint64_uint64"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_sint32_sint32")) == + reflection->MapEnd(message, F("map_sint32_sint32"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_sint64_sint64")) == + reflection->MapEnd(message, F("map_sint64_sint64"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_fixed32_fixed32")) == + reflection->MapEnd(message, F("map_fixed32_fixed32"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_fixed64_fixed64")) == + reflection->MapEnd(message, F("map_fixed64_fixed64"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_sfixed32_sfixed32")) == + reflection->MapEnd(message, F("map_sfixed32_sfixed32"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_sfixed64_sfixed64")) == + reflection->MapEnd(message, F("map_sfixed64_sfixed64"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_float")) == + reflection->MapEnd(message, F("map_int32_float"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_double")) == + reflection->MapEnd(message, F("map_int32_double"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_bool_bool")) == + reflection->MapEnd(message, F("map_bool_bool"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_string_string")) == + reflection->MapEnd(message, F("map_string_string"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_bytes")) == + reflection->MapEnd(message, F("map_int32_bytes"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_enum")) == + reflection->MapEnd(message, F("map_int32_enum"))); + EXPECT_TRUE(reflection->MapBegin(message, F("map_int32_foreign_message")) == + reflection->MapEnd(message, F("map_int32_foreign_message"))); +} + +void MapReflectionTester::ExpectMapEntryClearViaReflection( Message* message) { const Reflection* reflection = message->GetReflection(); const Message* sub_message; diff --git a/src/google/protobuf/map_test_util.h b/src/google/protobuf/map_test_util.h index f437e33ed8..107a639d81 100644 --- a/src/google/protobuf/map_test_util.h +++ b/src/google/protobuf/map_test_util.h @@ -83,71 +83,74 @@ class MapTestUtil { // Get pointers of map entries from release. static std::vector GetMapEntriesFromRelease( unittest::TestMap* message); +}; - // Like above, but use the reflection interface. - class MapReflectionTester { - public: - // base_descriptor must be a descriptor for TestMap, which is used for - // MapReflectionTester to fetch the FieldDescriptors needed to use the - // reflection interface. - explicit MapReflectionTester(const Descriptor* base_descriptor); - - void SetMapFieldsViaReflection(Message* message); - void ClearMapFieldsViaReflection(Message* message); - void ModifyMapFieldsViaReflection(Message* message); - void RemoveLastMapsViaReflection(Message* message); - void ReleaseLastMapsViaReflection(Message* message); - void SwapMapsViaReflection(Message* message); - void MutableUnknownFieldsOfMapFieldsViaReflection(Message* message); - void ExpectMapFieldsSetViaReflection(const Message& message); - void ExpectClearViaReflection(const Message& message); - void ExpectMapEntryClearViaReflection(Message* message); - - private: - const FieldDescriptor* F(const string& name); - - const Descriptor* base_descriptor_; - - const EnumValueDescriptor* map_enum_bar_; - const EnumValueDescriptor* map_enum_baz_; - const EnumValueDescriptor* map_enum_foo_; - - const FieldDescriptor* foreign_c_; - const FieldDescriptor* map_int32_int32_key_; - const FieldDescriptor* map_int32_int32_val_; - const FieldDescriptor* map_int64_int64_key_; - const FieldDescriptor* map_int64_int64_val_; - const FieldDescriptor* map_uint32_uint32_key_; - const FieldDescriptor* map_uint32_uint32_val_; - const FieldDescriptor* map_uint64_uint64_key_; - const FieldDescriptor* map_uint64_uint64_val_; - const FieldDescriptor* map_sint32_sint32_key_; - const FieldDescriptor* map_sint32_sint32_val_; - const FieldDescriptor* map_sint64_sint64_key_; - const FieldDescriptor* map_sint64_sint64_val_; - const FieldDescriptor* map_fixed32_fixed32_key_; - const FieldDescriptor* map_fixed32_fixed32_val_; - const FieldDescriptor* map_fixed64_fixed64_key_; - const FieldDescriptor* map_fixed64_fixed64_val_; - const FieldDescriptor* map_sfixed32_sfixed32_key_; - const FieldDescriptor* map_sfixed32_sfixed32_val_; - const FieldDescriptor* map_sfixed64_sfixed64_key_; - const FieldDescriptor* map_sfixed64_sfixed64_val_; - const FieldDescriptor* map_int32_float_key_; - const FieldDescriptor* map_int32_float_val_; - const FieldDescriptor* map_int32_double_key_; - const FieldDescriptor* map_int32_double_val_; - const FieldDescriptor* map_bool_bool_key_; - const FieldDescriptor* map_bool_bool_val_; - const FieldDescriptor* map_string_string_key_; - const FieldDescriptor* map_string_string_val_; - const FieldDescriptor* map_int32_bytes_key_; - const FieldDescriptor* map_int32_bytes_val_; - const FieldDescriptor* map_int32_enum_key_; - const FieldDescriptor* map_int32_enum_val_; - const FieldDescriptor* map_int32_foreign_message_key_; - const FieldDescriptor* map_int32_foreign_message_val_; - }; +// Like above, but use the reflection interface. +class MapReflectionTester { + public: + // base_descriptor must be a descriptor for TestMap, which is used for + // MapReflectionTester to fetch the FieldDescriptors needed to use the + // reflection interface. + explicit MapReflectionTester(const Descriptor* base_descriptor); + + void SetMapFieldsViaReflection(Message* message); + void SetMapFieldsViaMapReflection(Message* message); + void ClearMapFieldsViaReflection(Message* message); + void ModifyMapFieldsViaReflection(Message* message); + void RemoveLastMapsViaReflection(Message* message); + void ReleaseLastMapsViaReflection(Message* message); + void SwapMapsViaReflection(Message* message); + void MutableUnknownFieldsOfMapFieldsViaReflection(Message* message); + void ExpectMapFieldsSetViaReflection(const Message& message); + void ExpectMapFieldsSetViaReflectionIterator(Message* message); + void ExpectClearViaReflection(const Message& message); + void ExpectClearViaReflectionIterator(Message* message); + void ExpectMapEntryClearViaReflection(Message* message); + + private: + const FieldDescriptor* F(const string& name); + + const Descriptor* base_descriptor_; + + const EnumValueDescriptor* map_enum_bar_; + const EnumValueDescriptor* map_enum_baz_; + const EnumValueDescriptor* map_enum_foo_; + + const FieldDescriptor* foreign_c_; + const FieldDescriptor* map_int32_int32_key_; + const FieldDescriptor* map_int32_int32_val_; + const FieldDescriptor* map_int64_int64_key_; + const FieldDescriptor* map_int64_int64_val_; + const FieldDescriptor* map_uint32_uint32_key_; + const FieldDescriptor* map_uint32_uint32_val_; + const FieldDescriptor* map_uint64_uint64_key_; + const FieldDescriptor* map_uint64_uint64_val_; + const FieldDescriptor* map_sint32_sint32_key_; + const FieldDescriptor* map_sint32_sint32_val_; + const FieldDescriptor* map_sint64_sint64_key_; + const FieldDescriptor* map_sint64_sint64_val_; + const FieldDescriptor* map_fixed32_fixed32_key_; + const FieldDescriptor* map_fixed32_fixed32_val_; + const FieldDescriptor* map_fixed64_fixed64_key_; + const FieldDescriptor* map_fixed64_fixed64_val_; + const FieldDescriptor* map_sfixed32_sfixed32_key_; + const FieldDescriptor* map_sfixed32_sfixed32_val_; + const FieldDescriptor* map_sfixed64_sfixed64_key_; + const FieldDescriptor* map_sfixed64_sfixed64_val_; + const FieldDescriptor* map_int32_float_key_; + const FieldDescriptor* map_int32_float_val_; + const FieldDescriptor* map_int32_double_key_; + const FieldDescriptor* map_int32_double_val_; + const FieldDescriptor* map_bool_bool_key_; + const FieldDescriptor* map_bool_bool_val_; + const FieldDescriptor* map_string_string_key_; + const FieldDescriptor* map_string_string_val_; + const FieldDescriptor* map_int32_bytes_key_; + const FieldDescriptor* map_int32_bytes_val_; + const FieldDescriptor* map_int32_enum_key_; + const FieldDescriptor* map_int32_enum_val_; + const FieldDescriptor* map_int32_foreign_message_key_; + const FieldDescriptor* map_int32_foreign_message_val_; }; } // namespace protobuf diff --git a/src/google/protobuf/map_test_util_impl.h b/src/google/protobuf/map_test_util_impl.h index 5e7882a1d1..7e8757ed08 100644 --- a/src/google/protobuf/map_test_util_impl.h +++ b/src/google/protobuf/map_test_util_impl.h @@ -31,6 +31,7 @@ #ifndef GOOGLE_PROTOBUF_MAP_TEST_UTIL_IMPL_H__ #define GOOGLE_PROTOBUF_MAP_TEST_UTIL_IMPL_H__ +#include #include @@ -167,8 +168,11 @@ void MapTestUtilImpl::SetArenaMapFields(MapMessage* message) { (*message->mutable_map_int32_float())[0] = 0.0; (*message->mutable_map_int32_double())[0] = 0.0; (*message->mutable_map_bool_bool())[0] = false; + (*message->mutable_map_string_string())["0"] = "0"; + (*message->mutable_map_int32_bytes())[0] = "0"; (*message->mutable_map_int32_enum())[0] = enum_value0; (*message->mutable_map_int32_foreign_message())[0].set_c(0); + (*message->mutable_map_int32_foreign_message_no_arena())[0].set_c(0); // Add second element (*message->mutable_map_int32_int32())[1] = 1; @@ -184,8 +188,11 @@ void MapTestUtilImpl::SetArenaMapFields(MapMessage* message) { (*message->mutable_map_int32_float())[1] = 1.0; (*message->mutable_map_int32_double())[1] = 1.0; (*message->mutable_map_bool_bool())[1] = true; + (*message->mutable_map_string_string())["1"] = "1"; + (*message->mutable_map_int32_bytes())[1] = "1"; (*message->mutable_map_int32_enum())[1] = enum_value1; (*message->mutable_map_int32_foreign_message())[1].set_c(1); + (*message->mutable_map_int32_foreign_message_no_arena())[1].set_c(1); } template @@ -329,8 +336,11 @@ void MapTestUtilImpl::ExpectArenaMapFieldsSet(const MapMessage& message) { EXPECT_EQ(2, message.map_int32_float().size()); EXPECT_EQ(2, message.map_int32_double().size()); EXPECT_EQ(2, message.map_bool_bool().size()); + EXPECT_EQ(2, message.map_string_string().size()); + EXPECT_EQ(2, message.map_int32_bytes().size()); EXPECT_EQ(2, message.map_int32_enum().size()); EXPECT_EQ(2, message.map_int32_foreign_message().size()); + EXPECT_EQ(2, message.map_int32_foreign_message_no_arena().size()); EXPECT_EQ(0, message.map_int32_int32().at(0)); EXPECT_EQ(0, message.map_int64_int64().at(0)); @@ -345,8 +355,11 @@ void MapTestUtilImpl::ExpectArenaMapFieldsSet(const MapMessage& message) { EXPECT_EQ(0, message.map_int32_float().at(0)); EXPECT_EQ(0, message.map_int32_double().at(0)); EXPECT_EQ(false, message.map_bool_bool().at(0)); + EXPECT_EQ("0", message.map_string_string().at("0")); + EXPECT_EQ("0", message.map_int32_bytes().at(0)); EXPECT_EQ(enum_value0, message.map_int32_enum().at(0)); EXPECT_EQ(0, message.map_int32_foreign_message().at(0).c()); + EXPECT_EQ(0, message.map_int32_foreign_message_no_arena().at(0).c()); EXPECT_EQ(1, message.map_int32_int32().at(1)); EXPECT_EQ(1, message.map_int64_int64().at(1)); @@ -361,8 +374,11 @@ void MapTestUtilImpl::ExpectArenaMapFieldsSet(const MapMessage& message) { EXPECT_EQ(1, message.map_int32_float().at(1)); EXPECT_EQ(1, message.map_int32_double().at(1)); EXPECT_EQ(true, message.map_bool_bool().at(1)); + EXPECT_EQ("1", message.map_string_string().at("1")); + EXPECT_EQ("1", message.map_int32_bytes().at(1)); EXPECT_EQ(enum_value1, message.map_int32_enum().at(1)); EXPECT_EQ(1, message.map_int32_foreign_message().at(1).c()); + EXPECT_EQ(1, message.map_int32_foreign_message_no_arena().at(1).c()); } template diff --git a/src/google/protobuf/map_type_handler.h b/src/google/protobuf/map_type_handler.h index ffdb6dfbac..5040e60527 100644 --- a/src/google/protobuf/map_type_handler.h +++ b/src/google/protobuf/map_type_handler.h @@ -54,22 +54,6 @@ struct MapIf { typedef FalseType type; }; -// In MapField, string and message are stored as pointer while others are stored -// as object. However, google::protobuf::Map has unified api. Functions in this class -// convert key/value to type wanted in api regardless how it's stored -// internally. -template -class MapCommonTypeHandler { - public: - static inline Type& Reference(Type* x) { return *x; } - static inline Type& Reference(Type& x) { return x; } - static inline const Type& Reference(const Type& x) { return x; } - static inline Type* Pointer(Type* x) { return x; } - static inline Type* Pointer(Type& x) { return &x; } - static inline const Type* Pointer(const Type* x) { return x; } - static inline const Type* Pointer(const Type& x) { return &x; } -}; - // In proto2 Map, enum needs to be initialized to given default value, while // other types' default value can be inferred from the type. template @@ -110,174 +94,29 @@ template class MapArenaMessageCreator { public: static inline Type* CreateMessage(Arena* arena) { - return new Type; - } -}; - -// Handlers for key/value stored type in MapField. ================== - -// Handler for message -template -class MapCppTypeHandler : public MapCommonTypeHandler { - public: - static const bool kIsStringOrMessage = true; - // SpaceUsedInMapEntry: Return bytes used by value in MapEntry, excluding - // those already calculate in sizeof(MapField). - static int SpaceUsedInMapEntry(const Type* value) { - return value->SpaceUsed(); - } - // Return bytes used by value in Map. - static int SpaceUsedInMap(const Type& value) { return value.SpaceUsed(); } - static inline void Clear(Type** value) { - if (*value != NULL) (*value)->Clear(); - } - static inline void ClearMaybeByDefaultEnum(Type** value, - int default_enum_value) { - if (*value != NULL) (*value)->Clear(); - } - static inline void Merge(const Type& from, Type** to) { - (*to)->MergeFrom(from); - } - - static void Delete(const Type* ptr) { delete ptr; } - - // Assign default value to given instance. - static inline void AssignDefaultValue(Type** value) { - *value = const_cast(&Type::default_instance()); - } - // Initialize value when constructing MapEntry - static inline void Initialize(Type** x, Arena* arena) { *x = NULL; } - // Same as above, but use default_enum_value to initialize enum type value. - static inline void InitializeMaybeByDefaultEnum( - Type** x, int default_enum_value, Arena* arena) { - *x = NULL; - } - // Initialize value for the first time mutable accessor is called. - static inline void EnsureMutable(Type** value, Arena* arena) { - if (*value == NULL) { - *value = - MapArenaMessageCreator:: - type::value>::CreateMessage(arena); - } - } - // Return default instance if value is not initialized when calling const - // reference accessor. - static inline const Type& DefaultIfNotInitialized(const Type* value, - const Type* default_value) { - return value != NULL ? *value : *default_value; - } - // Check if all required fields have values set. - static inline bool IsInitialized(Type* value) { - return value->IsInitialized(); - } -}; - -// Handler for string. -template <> -class MapCppTypeHandler : public MapCommonTypeHandler { - public: - static const bool kIsStringOrMessage = true; - static inline void Merge(const string& from, string** to) { **to = from; } - static inline void Clear(string** value) { (*value)->clear(); } - static inline void ClearMaybeByDefaultEnum(string** value, int default_enum) { - (*value)->clear(); - } - static inline int SpaceUsedInMapEntry(const string* value) { - return sizeof(*value) + StringSpaceUsedExcludingSelf(*value); - } - static inline int SpaceUsedInMap(const string& value) { - return sizeof(value) + StringSpaceUsedExcludingSelf(value); - } - static void Delete(const string* ptr) { - if (ptr != &::google::protobuf::internal::GetEmptyString()) delete ptr; - } - static inline void AssignDefaultValue(string** value) {} - static inline void Initialize(string** value, Arena* arena) { - *value = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); - if (arena != NULL) arena->Own(*value); - } - static inline void InitializeMaybeByDefaultEnum( - string** value, int default_enum_value, Arena* arena) { - *value = const_cast< ::std::string*>(&::google::protobuf::internal::GetEmptyString()); - if (arena != NULL) arena->Own(*value); + return Arena::Create(arena); } - static inline void EnsureMutable(string** value, Arena* arena) { - if (*value == &::google::protobuf::internal::GetEmptyString()) { - *value = Arena::Create(arena); - } - } - static inline const string& DefaultIfNotInitialized( - const string* value, - const string* default_value) { - return value != default_value ? *value : *default_value; - } - static inline bool IsInitialized(string* value) { return true; } }; -// Base class for primitive type handlers. -template -class MapPrimitiveTypeHandler : public MapCommonTypeHandler { - public: - static const bool kIsStringOrMessage = false; - static inline void Delete(const Type& x) {} - static inline void Merge(const Type& from, Type* to) { *to = from; } - static inline int SpaceUsedInMapEntry(const Type& value) { return 0; } - static inline int SpaceUsedInMap(const Type& value) { return sizeof(Type); } - static inline void AssignDefaultValue(Type* value) {} - static inline const Type& DefaultIfNotInitialized( - const Type& value, const Type& default_value) { - return value; - } - static inline bool IsInitialized(const Type& value) { return true; } -}; - -// Handlers for primitive types. -#define PRIMITIVE_HANDLER(CType) \ - template <> \ - class MapCppTypeHandler : public MapPrimitiveTypeHandler { \ - public: \ - static inline void Clear(CType* value) { *value = 0; } \ - static inline void ClearMaybeByDefaultEnum(CType* value, \ - int default_enum_value) { \ - *value = static_cast(default_enum_value); \ - } \ - static inline void Initialize(CType* value, Arena* arena) { *value = 0; } \ - static inline void InitializeMaybeByDefaultEnum(CType* value, \ - int default_enum_value, \ - Arena* arena) { \ - *value = static_cast(default_enum_value); \ - } \ - static inline void EnsureMutable(CType* value, Arena* arena) {} \ - }; - -PRIMITIVE_HANDLER(int32 ) -PRIMITIVE_HANDLER(int64 ) -PRIMITIVE_HANDLER(uint32) -PRIMITIVE_HANDLER(uint64) -PRIMITIVE_HANDLER(double) -PRIMITIVE_HANDLER(float ) -PRIMITIVE_HANDLER(bool ) - -#undef PRIMITIVE_HANDLER - // Define constants for given wire field type -template +template class MapWireFieldTypeTraits {}; -#define TYPE_TRAITS(FieldType, CType, WireFormatType, IsMessage, IsEnum) \ - template <> \ - class MapWireFieldTypeTraits { \ - public: \ - typedef CType CppType; \ - static const bool kIsMessage = IsMessage; \ - static const bool kIsEnum = IsEnum; \ - static const WireFormatLite::WireType kWireType = \ - WireFormatLite::WIRETYPE_##WireFormatType; \ +#define TYPE_TRAITS(FieldType, CType, WireFormatType, IsMessage, IsEnum) \ + template \ + class MapWireFieldTypeTraits { \ + public: \ + static const bool kIsMessage = IsMessage; \ + static const bool kIsEnum = IsEnum; \ + typedef typename MapIf::type TypeOnMemory; \ + typedef typename MapIf::type MapEntryAccessorType; \ + static const WireFormatLite::WireType kWireType = \ + WireFormatLite::WIRETYPE_##WireFormatType; \ }; -TYPE_TRAITS(MESSAGE , MessageLite, LENGTH_DELIMITED, true, false) -TYPE_TRAITS(STRING , string , LENGTH_DELIMITED, false, false) -TYPE_TRAITS(BYTES , string , LENGTH_DELIMITED, false, false) +TYPE_TRAITS(MESSAGE , Type, LENGTH_DELIMITED, true, false) +TYPE_TRAITS(STRING , ArenaStringPtr, LENGTH_DELIMITED, false, false) +TYPE_TRAITS(BYTES , ArenaStringPtr , LENGTH_DELIMITED, false, false) TYPE_TRAITS(INT64 , int64 , VARINT , false, false) TYPE_TRAITS(UINT64 , uint64 , VARINT , false, false) TYPE_TRAITS(INT32 , int32 , VARINT , false, false) @@ -295,46 +134,149 @@ TYPE_TRAITS(BOOL , bool , VARINT , false, false) #undef TYPE_TRAITS -template -class MapWireFieldTypeHandler { +template +class MapTypeHandler {}; + +template +class MapTypeHandler { public: + // Enum type cannot be used for MapTypeHandler::Read. Define a type which will + // replace Enum with int. + typedef typename MapWireFieldTypeTraits::MapEntryAccessorType MapEntryAccessorType; // Internal stored type in MapEntryLite for given wire field type. - typedef typename MapWireFieldTypeTraits::CppType CppType; + typedef typename MapWireFieldTypeTraits::TypeOnMemory TypeOnMemory; // Corresponding wire type for field type. static const WireFormatLite::WireType kWireType = - MapWireFieldTypeTraits::kWireType; + MapWireFieldTypeTraits::kWireType; // Whether wire type is for message. - static const bool kIsMessage = MapWireFieldTypeTraits::kIsMessage; + static const bool kIsMessage = + MapWireFieldTypeTraits::kIsMessage; // Whether wire type is for enum. - static const bool kIsEnum = MapWireFieldTypeTraits::kIsEnum; + static const bool kIsEnum = + MapWireFieldTypeTraits::kIsEnum; // Functions used in parsing and serialization. =================== - template - static inline int ByteSize(const ValueType& value); - template - static inline int GetCachedSize(const ValueType& value); - template - static inline bool Read(io::CodedInputStream* input, ValueType* value); - static inline void Write(int field, const CppType& value, + static inline int ByteSize(const MapEntryAccessorType& value); + static inline int GetCachedSize(const MapEntryAccessorType& value); + static inline bool Read(io::CodedInputStream* input, + MapEntryAccessorType* value); + static inline void Write(int field, const MapEntryAccessorType& value, io::CodedOutputStream* output); - static inline uint8* WriteToArray(int field, const CppType& value, + static inline uint8* WriteToArray(int field, + const MapEntryAccessorType& value, uint8* output); + + // Functions to manipulate data on memory. ======================== + static inline const Type& GetExternalReference(const Type* value); + static inline void DeleteNoArena(const Type* x); + static inline void Merge(const Type& from, Type** to, Arena* arena); + static inline void Clear(Type** value, Arena* arena); + static inline void ClearMaybeByDefaultEnum(Type** value, Arena* arena, + int default_enum_value); + static inline void Initialize(Type** x, Arena* arena); + + static inline void InitializeMaybeByDefaultEnum(Type** x, + int default_enum_value, + Arena* arena); + static inline Type* EnsureMutable(Type** value, Arena* arena); + // SpaceUsedInMapEntry: Return bytes used by value in MapEntry, excluding + // those already calculate in sizeof(MapField). + static inline int SpaceUsedInMapEntry(const Type* value); + // Return bytes used by value in Map. + static inline int SpaceUsedInMap(const Type& value); + // Assign default value to given instance. + static inline void AssignDefaultValue(Type** value); + // Return default instance if value is not initialized when calling const + // reference accessor. + static inline const Type& DefaultIfNotInitialized( + const Type* value, const Type* default_value); + // Check if all required fields have values set. + static inline bool IsInitialized(Type* value); }; -template <> -template -inline int MapWireFieldTypeHandler::ByteSize( - const ValueType& value) { +#define MAP_HANDLER(FieldType) \ + template \ + class MapTypeHandler { \ + public: \ + typedef typename MapWireFieldTypeTraits::MapEntryAccessorType \ + MapEntryAccessorType; \ + typedef typename MapWireFieldTypeTraits::TypeOnMemory TypeOnMemory; \ + static const WireFormatLite::WireType kWireType = \ + MapWireFieldTypeTraits::kWireType; \ + static const bool kIsMessage = \ + MapWireFieldTypeTraits::kIsMessage; \ + static const bool kIsEnum = \ + MapWireFieldTypeTraits::kIsEnum; \ + static inline int ByteSize(const MapEntryAccessorType& value); \ + static inline int GetCachedSize(const MapEntryAccessorType& value); \ + static inline bool Read(io::CodedInputStream* input, \ + MapEntryAccessorType* value); \ + static inline void Write(int field, const MapEntryAccessorType& value, \ + io::CodedOutputStream* output); \ + static inline uint8* WriteToArray(int field, \ + const MapEntryAccessorType& value, \ + uint8* output); \ + static inline const MapEntryAccessorType& GetExternalReference( \ + const TypeOnMemory& value); \ + static inline void DeleteNoArena(const TypeOnMemory& x); \ + static inline void Merge(const MapEntryAccessorType& from, \ + TypeOnMemory* to, Arena* arena); \ + static inline void Clear(TypeOnMemory* value, Arena* arena); \ + static inline void ClearMaybeByDefaultEnum(TypeOnMemory* value, \ + Arena* arena, \ + int default_enum); \ + static inline int SpaceUsedInMapEntry(const TypeOnMemory& value); \ + static inline int SpaceUsedInMap(const TypeOnMemory& value); \ + static inline int SpaceUsedInMap(const string& value); \ + static inline void AssignDefaultValue(TypeOnMemory* value); \ + static inline const MapEntryAccessorType& DefaultIfNotInitialized( \ + const TypeOnMemory& value, const TypeOnMemory& default_value); \ + static inline bool IsInitialized(const TypeOnMemory& value); \ + static void DeleteNoArena(TypeOnMemory& value); \ + static inline void Initialize(TypeOnMemory* value, Arena* arena); \ + static inline void InitializeMaybeByDefaultEnum(TypeOnMemory* value, \ + int default_enum_value, \ + Arena* arena); \ + static inline MapEntryAccessorType* EnsureMutable(TypeOnMemory* value, \ + Arena* arena); \ + }; +MAP_HANDLER(STRING) +MAP_HANDLER(BYTES) +MAP_HANDLER(INT64) +MAP_HANDLER(UINT64) +MAP_HANDLER(INT32) +MAP_HANDLER(UINT32) +MAP_HANDLER(SINT64) +MAP_HANDLER(SINT32) +MAP_HANDLER(ENUM) +MAP_HANDLER(DOUBLE) +MAP_HANDLER(FLOAT) +MAP_HANDLER(FIXED64) +MAP_HANDLER(FIXED32) +MAP_HANDLER(SFIXED64) +MAP_HANDLER(SFIXED32) +MAP_HANDLER(BOOL) +#undef MAP_HANDLER + +template +inline int +MapTypeHandler::ByteSize( + const MapEntryAccessorType& value) { return WireFormatLite::MessageSizeNoVirtual(value); } -#define BYTE_SIZE(FieldType, DeclaredType) \ - template <> \ - template \ - inline int \ - MapWireFieldTypeHandler::ByteSize( \ - const ValueType& value) { \ - return WireFormatLite::DeclaredType##Size(value); \ +#define BYTE_SIZE(FieldType, DeclaredType) \ + template \ + inline int MapTypeHandler::ByteSize( \ + const MapEntryAccessorType& value) { \ + return WireFormatLite::DeclaredType##Size(value); \ } BYTE_SIZE(STRING, String) @@ -349,13 +291,11 @@ BYTE_SIZE(ENUM , Enum) #undef BYTE_SIZE -#define FIXED_BYTE_SIZE(FieldType, DeclaredType) \ - template <> \ - template \ - inline int \ - MapWireFieldTypeHandler::ByteSize( \ - const ValueType& value) { \ - return WireFormatLite::k##DeclaredType##Size; \ +#define FIXED_BYTE_SIZE(FieldType, DeclaredType) \ + template \ + inline int MapTypeHandler::ByteSize( \ + const MapEntryAccessorType& value) { \ + return WireFormatLite::k##DeclaredType##Size; \ } FIXED_BYTE_SIZE(DOUBLE , Double) @@ -368,20 +308,19 @@ FIXED_BYTE_SIZE(BOOL , Bool) #undef FIXED_BYTE_SIZE -template <> -template -inline int MapWireFieldTypeHandler< - WireFormatLite::TYPE_MESSAGE>::GetCachedSize(const ValueType& value) { +template +inline int +MapTypeHandler::GetCachedSize( + const MapEntryAccessorType& value) { return WireFormatLite::LengthDelimitedSize(value.GetCachedSize()); } -#define GET_CACHED_SIZE(FieldType, DeclaredType) \ - template <> \ - template \ - inline int \ - MapWireFieldTypeHandler::GetCachedSize( \ - const ValueType& value) { \ - return WireFormatLite::DeclaredType##Size(value); \ +#define GET_CACHED_SIZE(FieldType, DeclaredType) \ + template \ + inline int \ + MapTypeHandler::GetCachedSize( \ + const MapEntryAccessorType& value) { \ + return WireFormatLite::DeclaredType##Size(value); \ } GET_CACHED_SIZE(STRING, String) @@ -396,13 +335,12 @@ GET_CACHED_SIZE(ENUM , Enum) #undef GET_CACHED_SIZE -#define GET_FIXED_CACHED_SIZE(FieldType, DeclaredType) \ - template <> \ - template \ - inline int \ - MapWireFieldTypeHandler::GetCachedSize( \ - const ValueType& value) { \ - return WireFormatLite::k##DeclaredType##Size; \ +#define GET_FIXED_CACHED_SIZE(FieldType, DeclaredType) \ + template \ + inline int \ + MapTypeHandler::GetCachedSize( \ + const MapEntryAccessorType& value) { \ + return WireFormatLite::k##DeclaredType##Size; \ } GET_FIXED_CACHED_SIZE(DOUBLE , Double) @@ -415,30 +353,31 @@ GET_FIXED_CACHED_SIZE(BOOL , Bool) #undef GET_FIXED_CACHED_SIZE -template <> -inline void MapWireFieldTypeHandler::Write( - int field, const MessageLite& value, io::CodedOutputStream* output) { +template +inline void MapTypeHandler::Write( + int field, const MapEntryAccessorType& value, + io::CodedOutputStream* output) { WireFormatLite::WriteMessageMaybeToArray(field, value, output); } -template <> +template inline uint8* -MapWireFieldTypeHandler::WriteToArray( - int field, const MessageLite& value, uint8* output) { +MapTypeHandler::WriteToArray( + int field, const MapEntryAccessorType& value, uint8* output) { return WireFormatLite::WriteMessageToArray(field, value, output); } #define WRITE_METHOD(FieldType, DeclaredType) \ - template <> \ - inline void \ - MapWireFieldTypeHandler::Write( \ - int field, const CppType& value, io::CodedOutputStream* output) { \ + template \ + inline void MapTypeHandler::Write( \ + int field, const MapEntryAccessorType& value, \ + io::CodedOutputStream* output) { \ return WireFormatLite::Write##DeclaredType(field, value, output); \ } \ - template <> \ + template \ inline uint8* \ - MapWireFieldTypeHandler::WriteToArray( \ - int field, const CppType& value, uint8* output) { \ + MapTypeHandler::WriteToArray( \ + int field, const MapEntryAccessorType& value, uint8* output) { \ return WireFormatLite::Write##DeclaredType##ToArray(field, value, output); \ } @@ -461,35 +400,31 @@ WRITE_METHOD(BOOL , Bool) #undef WRITE_METHOD -template <> -template -inline bool MapWireFieldTypeHandler::Read( - io::CodedInputStream* input, ValueType* value) { +template +inline bool MapTypeHandler::Read( + io::CodedInputStream* input, MapEntryAccessorType* value) { return WireFormatLite::ReadMessageNoVirtual(input, value); } -template <> -template -inline bool MapWireFieldTypeHandler::Read( - io::CodedInputStream* input, ValueType* value) { +template +inline bool MapTypeHandler::Read( + io::CodedInputStream* input, MapEntryAccessorType* value) { return WireFormatLite::ReadString(input, value); } -template <> -template -inline bool MapWireFieldTypeHandler::Read( - io::CodedInputStream* input, ValueType* value) { +template +inline bool MapTypeHandler::Read( + io::CodedInputStream* input, MapEntryAccessorType* value) { return WireFormatLite::ReadBytes(input, value); } -#define READ_METHOD(FieldType) \ - template <> \ - template \ - inline bool MapWireFieldTypeHandler::Read( \ - io::CodedInputStream* input, ValueType* value) { \ - return WireFormatLite::ReadPrimitive( \ - input, value); \ +#define READ_METHOD(FieldType) \ + template \ + inline bool MapTypeHandler::Read( \ + io::CodedInputStream* input, MapEntryAccessorType* value) { \ + return WireFormatLite::ReadPrimitive( \ + input, value); \ } READ_METHOD(INT64) @@ -509,6 +444,282 @@ READ_METHOD(BOOL) #undef READ_METHOD +// Definition for message handler + +template +inline const Type& +MapTypeHandler::GetExternalReference(const Type* value) { + return *value; +} + +template +inline int +MapTypeHandler::SpaceUsedInMapEntry(const Type* value) { + return value->SpaceUsed(); +} + +template +int MapTypeHandler::SpaceUsedInMap( + const Type& value) { + return value.SpaceUsed(); +} + +template +inline void MapTypeHandler::Clear( + Type** value, Arena* arena) { + if (*value != NULL) (*value)->Clear(); +} +template +inline void +MapTypeHandler::ClearMaybeByDefaultEnum(Type** value, + Arena* arena, + int default_enum_value) { + if (*value != NULL) (*value)->Clear(); +} +template +inline void MapTypeHandler::Merge( + const Type& from, Type** to, Arena* arena) { + (*to)->MergeFrom(from); +} + +template +void MapTypeHandler::DeleteNoArena( + const Type* ptr) { + delete ptr; +} + +template +inline void MapTypeHandler::AssignDefaultValue(Type** value) { + *value = const_cast(&Type::default_instance()); +} + +template +inline void MapTypeHandler::Initialize(Type** x, + Arena* arena) { + *x = NULL; +} + +template +inline void MapTypeHandler:: + InitializeMaybeByDefaultEnum(Type** x, int default_enum_value, + Arena* arena) { + *x = NULL; +} + +template +inline Type* MapTypeHandler::EnsureMutable(Type** value, + Arena* arena) { + if (*value == NULL) { + *value = + MapArenaMessageCreator:: + type::value>::CreateMessage(arena); + } + return *value; +} + +template +inline const Type& MapTypeHandler:: + DefaultIfNotInitialized(const Type* value, const Type* default_value) { + return value != NULL ? *value : *default_value; +} + +template +inline bool MapTypeHandler::IsInitialized(Type* value) { + return value->IsInitialized(); +} + +// Definition for string/bytes handler + +#define STRING_OR_BYTES_HANDLER_FUNCTIONS(FieldType) \ + template \ + inline const typename MapTypeHandler::MapEntryAccessorType& \ + MapTypeHandler::GetExternalReference(const TypeOnMemory& value) { \ + return value.Get(&::google::protobuf::internal::GetEmptyString()); \ + } \ + template \ + inline int \ + MapTypeHandler::SpaceUsedInMapEntry( \ + const TypeOnMemory& value) { \ + return sizeof(value); \ + } \ + template \ + inline int MapTypeHandler::SpaceUsedInMap(const TypeOnMemory& value) { \ + return sizeof(value); \ + } \ + template \ + inline int MapTypeHandler::SpaceUsedInMap(const string& value) { \ + return sizeof(value); \ + } \ + template \ + inline void MapTypeHandler::Clear( \ + TypeOnMemory* value, Arena* arena) { \ + value->ClearToEmpty(&::google::protobuf::internal::GetEmptyString(), arena); \ + } \ + template \ + inline void \ + MapTypeHandler::ClearMaybeByDefaultEnum(TypeOnMemory* value, \ + Arena* arena, \ + int default_enum) { \ + Clear(value, arena); \ + } \ + template \ + inline void MapTypeHandler::Merge( \ + const MapEntryAccessorType& from, TypeOnMemory* to, Arena* arena) { \ + to->Set(&::google::protobuf::internal::GetEmptyString(), from, arena); \ + } \ + template \ + void MapTypeHandler::DeleteNoArena( \ + TypeOnMemory& value) { \ + value.DestroyNoArena(&::google::protobuf::internal::GetEmptyString()); \ + } \ + template \ + inline void MapTypeHandler::AssignDefaultValue(TypeOnMemory* value) {} \ + template \ + inline void \ + MapTypeHandler::Initialize( \ + TypeOnMemory* value, Arena* arena) { \ + value->UnsafeSetDefault(&::google::protobuf::internal::GetEmptyString()); \ + } \ + template \ + inline void \ + MapTypeHandler::InitializeMaybeByDefaultEnum(TypeOnMemory* value, \ + int default_enum_value, \ + Arena* arena) { \ + Initialize(value, arena); \ + } \ + template \ + inline typename MapTypeHandler::MapEntryAccessorType* \ + MapTypeHandler::EnsureMutable( \ + TypeOnMemory* value, Arena* arena) { \ + return value->Mutable(&::google::protobuf::internal::GetEmptyString(), arena); \ + } \ + template \ + inline const typename MapTypeHandler::MapEntryAccessorType& \ + MapTypeHandler::DefaultIfNotInitialized(const TypeOnMemory& value, \ + const TypeOnMemory& \ + default_value) { \ + return value.Get(&::google::protobuf::internal::GetEmptyString()); \ + } \ + template \ + inline bool MapTypeHandler::IsInitialized(const TypeOnMemory& value) { \ + return true; \ + } +STRING_OR_BYTES_HANDLER_FUNCTIONS(STRING) +STRING_OR_BYTES_HANDLER_FUNCTIONS(BYTES) +#undef STRING_OR_BYTES_HANDLER_FUNCTIONS + +#define PRIMITIVE_HANDLER_FUNCTIONS(FieldType) \ + template \ + inline const typename MapTypeHandler::MapEntryAccessorType& \ + MapTypeHandler::GetExternalReference(const TypeOnMemory& value) { \ + return value; \ + } \ + template \ + inline int \ + MapTypeHandler::SpaceUsedInMapEntry( \ + const TypeOnMemory& value) { \ + return 0; \ + } \ + template \ + inline int MapTypeHandler::SpaceUsedInMap(const TypeOnMemory& value) { \ + return sizeof(Type); \ + } \ + template \ + inline void MapTypeHandler::Clear( \ + TypeOnMemory* value, Arena* arena) { \ + *value = 0; \ + } \ + template \ + inline void \ + MapTypeHandler::ClearMaybeByDefaultEnum(TypeOnMemory* value, \ + Arena* arena, \ + int default_enum_value) { \ + *value = static_cast(default_enum_value); \ + } \ + template \ + inline void MapTypeHandler::Merge( \ + const MapEntryAccessorType& from, TypeOnMemory* to, Arena* arena) { \ + *to = from; \ + } \ + template \ + inline void MapTypeHandler::DeleteNoArena(TypeOnMemory& x) {} \ + template \ + inline void MapTypeHandler::AssignDefaultValue(TypeOnMemory* value) {} \ + template \ + inline void \ + MapTypeHandler::Initialize( \ + TypeOnMemory* value, Arena* arena) { \ + *value = 0; \ + } \ + template \ + inline void \ + MapTypeHandler::InitializeMaybeByDefaultEnum(TypeOnMemory* value, \ + int default_enum_value, \ + Arena* arena) { \ + *value = static_cast(default_enum_value); \ + } \ + template \ + inline typename MapTypeHandler::MapEntryAccessorType* \ + MapTypeHandler::EnsureMutable( \ + TypeOnMemory* value, Arena* arena) { \ + return value; \ + } \ + template \ + inline const typename MapTypeHandler::MapEntryAccessorType& \ + MapTypeHandler::DefaultIfNotInitialized(const TypeOnMemory& value, \ + const TypeOnMemory& \ + default_value) { \ + return value; \ + } \ + template \ + inline bool MapTypeHandler::IsInitialized(const TypeOnMemory& value) { \ + return true; \ + } +PRIMITIVE_HANDLER_FUNCTIONS(INT64) +PRIMITIVE_HANDLER_FUNCTIONS(UINT64) +PRIMITIVE_HANDLER_FUNCTIONS(INT32) +PRIMITIVE_HANDLER_FUNCTIONS(UINT32) +PRIMITIVE_HANDLER_FUNCTIONS(SINT64) +PRIMITIVE_HANDLER_FUNCTIONS(SINT32) +PRIMITIVE_HANDLER_FUNCTIONS(ENUM) +PRIMITIVE_HANDLER_FUNCTIONS(DOUBLE) +PRIMITIVE_HANDLER_FUNCTIONS(FLOAT) +PRIMITIVE_HANDLER_FUNCTIONS(FIXED64) +PRIMITIVE_HANDLER_FUNCTIONS(FIXED32) +PRIMITIVE_HANDLER_FUNCTIONS(SFIXED64) +PRIMITIVE_HANDLER_FUNCTIONS(SFIXED32) +PRIMITIVE_HANDLER_FUNCTIONS(BOOL) +#undef PRIMITIVE_HANDLER_FUNCTIONS + } // namespace internal } // namespace protobuf diff --git a/src/google/protobuf/map_unittest.proto b/src/google/protobuf/map_unittest.proto index b308c7ff52..d3b525a056 100644 --- a/src/google/protobuf/map_unittest.proto +++ b/src/google/protobuf/map_unittest.proto @@ -33,6 +33,7 @@ syntax = "proto3"; option cc_enable_arenas = true; import "google/protobuf/unittest.proto"; +import "google/protobuf/unittest_no_arena.proto"; // We don't put this in a package within proto2 because we need to make sure // that the generated code doesn't depend on being in the proto2 namespace. @@ -58,6 +59,7 @@ message TestMap { map map_int32_bytes = 15; map map_int32_enum = 16; map map_int32_foreign_message = 17; + map map_string_foreign_message = 18; } message TestMapSubmessage { @@ -100,8 +102,12 @@ message TestArenaMap { map map_int32_float = 11; map map_int32_double = 12; map map_bool_bool = 13; - map map_int32_enum = 14; - map map_int32_foreign_message = 15; + map map_string_string = 14; + map map_int32_bytes = 15; + map map_int32_enum = 16; + map map_int32_foreign_message = 17; + map + map_int32_foreign_message_no_arena = 18; } // Previously, message containing enum called Type cannot be used as value of @@ -117,3 +123,7 @@ message MessageContainingEnumCalledType { message MessageContainingMapCalledEntry { map entry = 1; } + +message TestRecursiveMapMessage { + map a = 1; +} diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc index 276d7de579..7d69c57abf 100644 --- a/src/google/protobuf/message.cc +++ b/src/google/protobuf/message.cc @@ -38,12 +38,15 @@ #include +#include #include +#include #include #include #include #include #include +#include #include #include #include @@ -256,6 +259,22 @@ void Reflection::AddEnumValue(Message* message, GOOGLE_LOG(FATAL) << "Unimplemented EnumValue API."; } +MapIterator Reflection::MapBegin( + Message* message, + const FieldDescriptor* field) const { + GOOGLE_LOG(FATAL) << "Unimplemented Map Reflection API."; + MapIterator iter(message, field); + return iter; +} + +MapIterator Reflection::MapEnd( + Message* message, + const FieldDescriptor* field) const { + GOOGLE_LOG(FATAL) << "Unimplemented Map Reflection API."; + MapIterator iter(message, field); + return iter; +} + // ============================================================================= // MessageFactory diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index 18c092d051..348e7c7fc3 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -134,12 +134,23 @@ class Reflection; class MessageFactory; // Defined in other files. +class MapKey; +class MapValueRef; +class MapIterator; +class MapReflectionTester; + +namespace internal { +class MapFieldBase; +} class UnknownFieldSet; // unknown_field_set.h namespace io { - class ZeroCopyInputStream; // zero_copy_stream.h - class ZeroCopyOutputStream; // zero_copy_stream.h - class CodedInputStream; // coded_stream.h - class CodedOutputStream; // coded_stream.h +class ZeroCopyInputStream; // zero_copy_stream.h +class ZeroCopyOutputStream; // zero_copy_stream.h +class CodedInputStream; // coded_stream.h +class CodedOutputStream; // coded_stream.h +} +namespace python { +class MapReflectionFriend; // scalar_map_container.h } @@ -724,6 +735,14 @@ class LIBPROTOBUF_EXPORT Reflection { const FieldDescriptor* field, MessageFactory* factory = NULL) const = 0; + // Appends an already-allocated object 'new_entry' to the repeated field + // specifyed by 'field' passing ownership to the message. + // TODO(tmarek): Make virtual after all subclasses have been + // updated. + virtual void AddAllocatedMessage(Message* message, + const FieldDescriptor* field, + Message* new_entry) const {} + // Get a RepeatedFieldRef object that can be used to read the underlying // repeated field. The type parameter T must be set according to the @@ -868,11 +887,20 @@ class LIBPROTOBUF_EXPORT Reflection { // on field->cpp_type(), // on field->field_option().ctype() (if ctype >= 0) // of field->message_type() (if message_type != NULL). - // We use 1 routine rather than 4 (const vs mutable) x (scalar vs pointer). + // We use 2 routine rather than 4 (const vs mutable) x (scalar vs pointer). virtual void* MutableRawRepeatedField( Message* message, const FieldDescriptor* field, FieldDescriptor::CppType, int ctype, const Descriptor* message_type) const = 0; + // TODO(jieluo) - make it pure virtual after updating all the subclasses. + virtual const void* GetRawRepeatedField( + const Message& message, const FieldDescriptor* field, + FieldDescriptor::CppType cpptype, int ctype, + const Descriptor* message_type) const { + return MutableRawRepeatedField( + const_cast(&message), field, cpptype, ctype, message_type); + } + // The following methods are used to implement (Mutable)RepeatedFieldRef. // A Ref object will store a raw pointer to the repeated field data (obtained // from RepeatedFieldData()) and a pointer to a Accessor (obtained from @@ -887,6 +915,8 @@ class LIBPROTOBUF_EXPORT Reflection { // "message_type" should be set to its descriptor. Otherwise "message_type" // should be set to NULL. Implementations of this method should check whether // "cpp_type"/"message_type" is consistent with the actual type of the field. + // We use 1 routine rather than 2 (const vs mutable) because it is protected + // and it doesn't change the message. virtual void* RepeatedFieldData( Message* message, const FieldDescriptor* field, FieldDescriptor::CppType cpp_type, @@ -902,14 +932,73 @@ class LIBPROTOBUF_EXPORT Reflection { friend class RepeatedFieldRef; template friend class MutableRepeatedFieldRef; + friend class ::google::protobuf::python::MapReflectionFriend; // Special version for specialized implementations of string. We can't call // MutableRawRepeatedField directly here because we don't have access to // FieldOptions::* which are defined in descriptor.pb.h. Including that // file here is not possible because it would cause a circular include cycle. + // We use 1 routine rather than 2 (const vs mutable) because it is private + // and mutable a repeated string field doesn't change the message. void* MutableRawRepeatedString( Message* message, const FieldDescriptor* field, bool is_string) const; + friend class MapReflectionTester; + // TODO(jieluo) - make the map APIs pure virtual after updating + // all the subclasses. + // Returns true if key is in map. Returns false if key is not in map field. + virtual bool ContainsMapKey(const Message& message, + const FieldDescriptor* field, + const MapKey& key) const { + return false; + } + + // If key is in map field: Saves the value pointer to val and returns + // false. If key in not in map field: Insert the key into map, saves + // value pointer to val and retuns true. + virtual bool InsertOrLookupMapValue(Message* message, + const FieldDescriptor* field, + const MapKey& key, + MapValueRef* val) const { + return false; + } + + // Delete and returns true if key is in the map field. Returns false + // otherwise. + virtual bool DeleteMapValue(Message* message, + const FieldDescriptor* field, + const MapKey& key) const { + return false; + } + + // Returns a MaIterator referring to the first element in the map field. + // If the map field is empty, this function returns the same as + // reflection::MapEnd. Mutation to the field may invalidate the iterator. + virtual MapIterator MapBegin( + Message* message, + const FieldDescriptor* field) const; + + // Returns a MapIterator referring to the theoretical element that would + // follow the last element in the map field. It does not point to any + // real element. Mutation to the field may invalidate the iterator. + virtual MapIterator MapEnd( + Message* message, + const FieldDescriptor* field) const; + + // Get the number of pair of a map field. The result may be + // different from FieldSize which can have duplicate keys. + virtual int MapSize(const Message& message, + const FieldDescriptor* field) const { + return 0; + } + + // Help method for MapIterator. + friend class MapIterator; + virtual internal::MapFieldBase* MapData( + Message* message, const FieldDescriptor* field) const { + return NULL; + } + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reflection); }; @@ -1025,10 +1114,9 @@ inline RepeatedPtrField* Reflection::MutableRepeatedPtrField( template<> inline const RepeatedPtrField& Reflection::GetRepeatedPtrField( const Message& message, const FieldDescriptor* field) const { - return *static_cast* >( - MutableRawRepeatedField(const_cast(&message), field, - FieldDescriptor::CPPTYPE_MESSAGE, -1, - NULL)); + return *static_cast* >( + GetRawRepeatedField(message, field, FieldDescriptor::CPPTYPE_MESSAGE, + -1, NULL)); } template<> @@ -1043,10 +1131,9 @@ inline RepeatedPtrField* Reflection::MutableRepeatedPtrField( template inline const RepeatedPtrField& Reflection::GetRepeatedPtrField( const Message& message, const FieldDescriptor* field) const { - return *static_cast* >( - MutableRawRepeatedField(const_cast(&message), field, - FieldDescriptor::CPPTYPE_MESSAGE, -1, - PB::default_instance().GetDescriptor())); + return *static_cast* >( + GetRawRepeatedField(message, field, FieldDescriptor::CPPTYPE_MESSAGE, + -1, PB::default_instance().GetDescriptor())); } template diff --git a/src/google/protobuf/message_lite.cc b/src/google/protobuf/message_lite.cc index 4f63ad2bf9..5bd8bcfb12 100644 --- a/src/google/protobuf/message_lite.cc +++ b/src/google/protobuf/message_lite.cc @@ -35,7 +35,9 @@ #include #include +#include #include +#include #include #include #include @@ -98,27 +100,19 @@ string InitializationErrorMessage(const char* action, // call MergePartialFromCodedStream(). However, when parsing very small // messages, every function call introduces significant overhead. To avoid // this without reproducing code, we use these forced-inline helpers. -// -// Note: GCC only allows GOOGLE_ATTRIBUTE_ALWAYS_INLINE on declarations, not -// definitions. +GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InlineMergeFromCodedStream( + io::CodedInputStream* input, MessageLite* message); +GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InlineParseFromCodedStream( + io::CodedInputStream* input, MessageLite* message); +GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InlineParsePartialFromCodedStream( + io::CodedInputStream* input, MessageLite* message); +GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InlineParseFromArray( + const void* data, int size, MessageLite* message); +GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool InlineParsePartialFromArray( + const void* data, int size, MessageLite* message); + inline bool InlineMergeFromCodedStream(io::CodedInputStream* input, - MessageLite* message) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline bool InlineParseFromCodedStream(io::CodedInputStream* input, - MessageLite* message) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline bool InlineParsePartialFromCodedStream(io::CodedInputStream* input, - MessageLite* message) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline bool InlineParseFromArray(const void* data, int size, - MessageLite* message) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -inline bool InlineParsePartialFromArray(const void* data, int size, - MessageLite* message) - GOOGLE_ATTRIBUTE_ALWAYS_INLINE; - -bool InlineMergeFromCodedStream(io::CodedInputStream* input, - MessageLite* message) { + MessageLite* message) { if (!message->MergePartialFromCodedStream(input)) return false; if (!message->IsInitialized()) { GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *message); @@ -127,26 +121,27 @@ bool InlineMergeFromCodedStream(io::CodedInputStream* input, return true; } -bool InlineParseFromCodedStream(io::CodedInputStream* input, - MessageLite* message) { +inline bool InlineParseFromCodedStream(io::CodedInputStream* input, + MessageLite* message) { message->Clear(); return InlineMergeFromCodedStream(input, message); } -bool InlineParsePartialFromCodedStream(io::CodedInputStream* input, - MessageLite* message) { +inline bool InlineParsePartialFromCodedStream(io::CodedInputStream* input, + MessageLite* message) { message->Clear(); return message->MergePartialFromCodedStream(input); } -bool InlineParseFromArray(const void* data, int size, MessageLite* message) { +inline bool InlineParseFromArray( + const void* data, int size, MessageLite* message) { io::CodedInputStream input(reinterpret_cast(data), size); return InlineParseFromCodedStream(&input, message) && input.ConsumedEntireMessage(); } -bool InlineParsePartialFromArray(const void* data, int size, - MessageLite* message) { +inline bool InlineParsePartialFromArray( + const void* data, int size, MessageLite* message) { io::CodedInputStream input(reinterpret_cast(data), size); return InlineParsePartialFromCodedStream(&input, message) && input.ConsumedEntireMessage(); @@ -353,5 +348,18 @@ string MessageLite::SerializePartialAsString() const { return output; } +namespace internal { +template<> +MessageLite* GenericTypeHandler::NewFromPrototype( + const MessageLite* prototype, google::protobuf::Arena* arena) { + return prototype->New(arena); +} +template <> +void GenericTypeHandler::Merge(const MessageLite& from, + MessageLite* to) { + to->CheckTypeAndMergeFrom(from); +} +} // namespace internal + } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc index 75d60b8b4a..2d4780fe6d 100644 --- a/src/google/protobuf/message_unittest.cc +++ b/src/google/protobuf/message_unittest.cc @@ -53,6 +53,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/metadata.h b/src/google/protobuf/metadata.h index 30b2a6eee5..fdee150b44 100644 --- a/src/google/protobuf/metadata.h +++ b/src/google/protobuf/metadata.h @@ -69,8 +69,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { ptr_ = NULL; } - inline const UnknownFieldSet& unknown_fields() const - GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE const UnknownFieldSet& unknown_fields() const { if (GOOGLE_PREDICT_FALSE(have_unknown_fields())) { return PtrValue()->unknown_fields_; } else { @@ -78,7 +77,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { } } - inline UnknownFieldSet* mutable_unknown_fields() GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE UnknownFieldSet* mutable_unknown_fields() { if (GOOGLE_PREDICT_TRUE(have_unknown_fields())) { return &PtrValue()->unknown_fields_; } else { @@ -86,7 +85,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { } } - inline Arena* arena() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE Arena* arena() const { if (GOOGLE_PREDICT_FALSE(have_unknown_fields())) { return PtrValue()->arena_; } else { @@ -94,11 +93,11 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { } } - inline bool have_unknown_fields() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE bool have_unknown_fields() const { return PtrTag() == kTagContainer; } - inline void Swap(InternalMetadataWithArena* other) GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE void Swap(InternalMetadataWithArena* other) { // Semantics here are that we swap only the unknown fields, not the arena // pointer. We cannot simply swap ptr_ with other->ptr_ because we need to // maintain our own arena ptr. Also, our ptr_ and other's ptr_ may be in @@ -110,7 +109,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { } } - inline void* raw_arena_ptr() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE void* raw_arena_ptr() const { return ptr_; } @@ -128,7 +127,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { static const intptr_t kPtrValueMask = ~kPtrTagMask; // Accessors for pointer tag and pointer value. - inline int PtrTag() const GOOGLE_ATTRIBUTE_ALWAYS_INLINE { + GOOGLE_ATTRIBUTE_ALWAYS_INLINE int PtrTag() const { return reinterpret_cast(ptr_) & kPtrTagMask; } @@ -143,7 +142,7 @@ class LIBPROTOBUF_EXPORT InternalMetadataWithArena { Arena* arena_; }; - UnknownFieldSet* mutable_unknown_fields_slow() GOOGLE_ATTRIBUTE_NOINLINE { + GOOGLE_ATTRIBUTE_NOINLINE UnknownFieldSet* mutable_unknown_fields_slow() { Arena* my_arena = arena(); Container* container = Arena::Create(my_arena); ptr_ = reinterpret_cast( diff --git a/src/google/protobuf/no_field_presence_test.cc b/src/google/protobuf/no_field_presence_test.cc index 4b7b31d979..bc41beec81 100644 --- a/src/google/protobuf/no_field_presence_test.cc +++ b/src/google/protobuf/no_field_presence_test.cc @@ -341,6 +341,46 @@ TEST(NoFieldPresenceTest, ReflectionHasFieldTest) { EXPECT_EQ(false, r->HasField(message, field_string)); } +TEST(NoFieldPresenceTest, ReflectionClearFieldTest) { + proto2_nofieldpresence_unittest::TestAllTypes message; + + const google::protobuf::Reflection* r = message.GetReflection(); + const google::protobuf::Descriptor* desc = message.GetDescriptor(); + + const google::protobuf::FieldDescriptor* field_int32 = desc->FindFieldByName( + "optional_int32"); + const google::protobuf::FieldDescriptor* field_double = desc->FindFieldByName( + "optional_double"); + const google::protobuf::FieldDescriptor* field_string = desc->FindFieldByName( + "optional_string"); + const google::protobuf::FieldDescriptor* field_message = desc->FindFieldByName( + "optional_nested_message"); + const google::protobuf::FieldDescriptor* field_lazy = desc->FindFieldByName( + "optional_lazy_message"); + + message.set_optional_int32(42); + r->ClearField(&message, field_int32); + EXPECT_EQ(0, message.optional_int32()); + + message.set_optional_double(42.0); + r->ClearField(&message, field_double); + EXPECT_EQ(0.0, message.optional_double()); + + message.set_optional_string("test"); + r->ClearField(&message, field_string); + EXPECT_EQ("", message.optional_string()); + + message.mutable_optional_nested_message()->set_bb(1234); + r->ClearField(&message, field_message); + EXPECT_FALSE(message.has_optional_nested_message()); + EXPECT_EQ(0, message.optional_nested_message().bb()); + + message.mutable_optional_lazy_message()->set_bb(42); + r->ClearField(&message, field_lazy); + EXPECT_FALSE(message.has_optional_lazy_message()); + EXPECT_EQ(0, message.optional_lazy_message().bb()); +} + TEST(NoFieldPresenceTest, HasFieldOneofsTest) { // check that HasField behaves properly for oneofs. proto2_nofieldpresence_unittest::TestAllTypes message; diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc index da4be67368..2838e0fc64 100644 --- a/src/google/protobuf/proto3_arena_unittest.cc +++ b/src/google/protobuf/proto3_arena_unittest.cc @@ -119,7 +119,7 @@ void ExpectAllFieldsSet(const TestAllTypes& m) { // proto3 and expect the arena support to be fully tested in proto2 unittests // because proto3 shares most code with proto2. -TEST(ArenaTest, Parsing) { +TEST(Proto3ArenaTest, Parsing) { TestAllTypes original; SetAllFields(&original); @@ -129,7 +129,7 @@ TEST(ArenaTest, Parsing) { ExpectAllFieldsSet(*arena_message); } -TEST(ArenaTest, UnknownFields) { +TEST(Proto3ArenaTest, UnknownFields) { TestAllTypes original; SetAllFields(&original); @@ -150,7 +150,7 @@ TEST(ArenaTest, UnknownFields) { arena_message->GetReflection()->GetUnknownFields(*arena_message).empty()); } -TEST(ArenaTest, Swap) { +TEST(Proto3ArenaTest, Swap) { Arena arena1; Arena arena2; @@ -162,7 +162,7 @@ TEST(ArenaTest, Swap) { EXPECT_EQ(&arena2, arena2_message->GetArena()); } -TEST(ArenaTest, SetAllocatedMessage) { +TEST(Proto3ArenaTest, SetAllocatedMessage) { Arena arena; TestAllTypes *arena_message = Arena::CreateMessage(&arena); TestAllTypes::NestedMessage* nested = new TestAllTypes::NestedMessage; @@ -171,7 +171,7 @@ TEST(ArenaTest, SetAllocatedMessage) { EXPECT_EQ(118, arena_message->optional_nested_message().bb()); } -TEST(ArenaTest, ReleaseMessage) { +TEST(Proto3ArenaTest, ReleaseMessage) { Arena arena; TestAllTypes* arena_message = Arena::CreateMessage(&arena); arena_message->mutable_optional_nested_message()->set_bb(118); @@ -180,7 +180,7 @@ TEST(ArenaTest, ReleaseMessage) { EXPECT_EQ(118, nested->bb()); } -TEST(ArenaTest, MessageFieldClear) { +TEST(Proto3ArenaTest, MessageFieldClear) { // GitHub issue #310: https://github.com/google/protobuf/issues/310 Arena arena; TestAllTypes* arena_message = Arena::CreateMessage(&arena); @@ -190,6 +190,20 @@ TEST(ArenaTest, MessageFieldClear) { arena_message->Clear(); } +TEST(Proto3ArenaTest, MessageFieldClearViaReflection) { + Arena arena; + TestAllTypes* message = Arena::CreateMessage(&arena); + const Reflection* r = message->GetReflection(); + const Descriptor* d = message->GetDescriptor(); + const FieldDescriptor* msg_field = d->FindFieldByName( + "optional_nested_message"); + + message->mutable_optional_nested_message()->set_bb(1); + r->ClearField(message, msg_field); + EXPECT_FALSE(message->has_optional_nested_message()); + EXPECT_EQ(0, message->optional_nested_message().bb()); +} + } // namespace } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/proto_cast.h b/src/google/protobuf/proto_cast.h index e25c219ffe..dc0e9aca90 100644 --- a/src/google/protobuf/proto_cast.h +++ b/src/google/protobuf/proto_cast.h @@ -33,6 +33,7 @@ #include +#include #include // proto_cast<> is used to simulate over-the-wire conversion of one diff --git a/src/google/protobuf/proto_cast_test.cc b/src/google/protobuf/proto_cast_test.cc index eb101eb633..a8f43ae424 100644 --- a/src/google/protobuf/proto_cast_test.cc +++ b/src/google/protobuf/proto_cast_test.cc @@ -32,7 +32,7 @@ #include #include -#include +#include namespace google { using google::protobuf::util::UpRevision; diff --git a/src/google/protobuf/reflection.h b/src/google/protobuf/reflection.h index 4ff0f6b40e..671aafdc4e 100755 --- a/src/google/protobuf/reflection.h +++ b/src/google/protobuf/reflection.h @@ -553,7 +553,7 @@ struct RefTypeTraits< template struct RefTypeTraits< - T, typename internal::enable_if::value>::type> { + T, typename internal::enable_if< ::google::protobuf::internal::is_same::value>::type> { typedef RepeatedFieldRefIterator iterator; typedef RepeatedFieldAccessor AccessorType; typedef string AccessorValueType; diff --git a/src/google/protobuf/reflection_ops_unittest.cc b/src/google/protobuf/reflection_ops_unittest.cc index 32740ea469..88d6bfb610 100644 --- a/src/google/protobuf/reflection_ops_unittest.cc +++ b/src/google/protobuf/reflection_ops_unittest.cc @@ -37,6 +37,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc index e5aedadc83..949e0a235d 100644 --- a/src/google/protobuf/repeated_field.cc +++ b/src/google/protobuf/repeated_field.cc @@ -35,6 +35,7 @@ #include #include +#include #include namespace google { @@ -53,13 +54,17 @@ void** RepeatedPtrFieldBase::InternalExtend(int extend_amount) { Arena* arena = GetArenaNoVirtual(); new_size = max(kMinRepeatedFieldAllocationSize, max(total_size_ * 2, new_size)); + GOOGLE_CHECK_LE(new_size, + (std::numeric_limits::max() - kRepHeaderSize) / + sizeof(old_rep->elements[0])) + << "Requested size is too large to fit into size_t."; if (arena == NULL) { rep_ = reinterpret_cast( - new char[kRepHeaderSize + sizeof(old_rep->elements[0])*new_size]); + new char[kRepHeaderSize + sizeof(old_rep->elements[0]) * new_size]); } else { rep_ = reinterpret_cast( ::google::protobuf::Arena::CreateArray(arena, - kRepHeaderSize + sizeof(old_rep->elements[0])*new_size)); + kRepHeaderSize + sizeof(old_rep->elements[0]) * new_size)); } total_size_ = new_size; if (old_rep && old_rep->allocated_size > 0) { diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index 14f46298d4..b42d47904a 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -361,7 +362,7 @@ class LIBPROTOBUF_EXPORT RepeatedPtrFieldBase { // To parse directly into a proto2 generated class, the upb class GMR_Handlers // needs to be able to modify a RepeatedPtrFieldBase directly. - friend class LIBPROTOBUF_EXPORT upb::google_opensource::GMR_Handlers; + friend class upb::google_opensource::GMR_Handlers; RepeatedPtrFieldBase(); explicit RepeatedPtrFieldBase(::google::protobuf::Arena* arena); @@ -408,7 +409,7 @@ class LIBPROTOBUF_EXPORT RepeatedPtrFieldBase { const typename TypeHandler::Type* const* data() const; template - inline void Swap(RepeatedPtrFieldBase* other) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + GOOGLE_ATTRIBUTE_ALWAYS_INLINE void Swap(RepeatedPtrFieldBase* other); void SwapElements(int index1, int index2); @@ -458,22 +459,20 @@ class LIBPROTOBUF_EXPORT RepeatedPtrFieldBase { void AddAllocatedInternal(typename TypeHandler::Type* value, google::protobuf::internal::false_type); - template + template GOOGLE_ATTRIBUTE_NOINLINE void AddAllocatedSlowWithCopy(typename TypeHandler::Type* value, Arena* value_arena, - Arena* my_arena) - GOOGLE_ATTRIBUTE_NOINLINE; - template - void AddAllocatedSlowWithoutCopy(typename TypeHandler::Type* value) - GOOGLE_ATTRIBUTE_NOINLINE; + Arena* my_arena); + template GOOGLE_ATTRIBUTE_NOINLINE + void AddAllocatedSlowWithoutCopy(typename TypeHandler::Type* value); template typename TypeHandler::Type* ReleaseLastInternal(google::protobuf::internal::true_type); template typename TypeHandler::Type* ReleaseLastInternal(google::protobuf::internal::false_type); - template - inline void SwapFallback(RepeatedPtrFieldBase* other) GOOGLE_ATTRIBUTE_NOINLINE; + template GOOGLE_ATTRIBUTE_NOINLINE + void SwapFallback(RepeatedPtrFieldBase* other); inline Arena* GetArenaNoVirtual() const { return arena_; @@ -542,20 +541,10 @@ class GenericTypeHandler { } // We force NewFromPrototype() and Delete() to be non-inline to reduce code // size: else, several other methods get inlined copies of message types' - // constructors and destructors. Note that the GOOGLE_ATTRIBUTE_NOINLINE macro - // requires the 'inline' storage class here, which is somewhat confusing, but - // the compiler does the right thing. - static inline GenericType* NewFromPrototype(const GenericType* prototype, - ::google::protobuf::Arena* arena = NULL) - GOOGLE_ATTRIBUTE_NOINLINE { - return New(arena); - } - static inline void Delete(GenericType* value, Arena* arena) - GOOGLE_ATTRIBUTE_NOINLINE { - if (arena == NULL) { - delete value; - } - } + // constructors and destructors. + GOOGLE_ATTRIBUTE_NOINLINE static GenericType* NewFromPrototype( + const GenericType* prototype, ::google::protobuf::Arena* arena = NULL); + GOOGLE_ATTRIBUTE_NOINLINE static void Delete(GenericType* value, Arena* arena); static inline ::google::protobuf::Arena* GetArena(GenericType* value) { return ::google::protobuf::Arena::GetArena(value); } @@ -564,10 +553,8 @@ class GenericTypeHandler { } static inline void Clear(GenericType* value) { value->Clear(); } - static inline void Merge(const GenericType& from, GenericType* to) - GOOGLE_ATTRIBUTE_NOINLINE { - to->MergeFrom(from); - } + GOOGLE_ATTRIBUTE_NOINLINE static void Merge(const GenericType& from, + GenericType* to); static inline int SpaceUsed(const GenericType& value) { return value.SpaceUsed(); } @@ -576,11 +563,31 @@ class GenericTypeHandler { } }; -template<> -inline MessageLite* GenericTypeHandler::NewFromPrototype( - const MessageLite* prototype, google::protobuf::Arena* arena) { - return prototype->New(arena); +template +GenericType* GenericTypeHandler::NewFromPrototype( + const GenericType* prototype, ::google::protobuf::Arena* arena) { + return New(arena); +} +template +void GenericTypeHandler::Delete(GenericType* value, Arena* arena) { + if (arena == NULL) { + delete value; + } } +template +void GenericTypeHandler::Merge(const GenericType& from, + GenericType* to) { + to->MergeFrom(from); +} + +// NewFromPrototype() and Merge() cannot be defined here; if they're declared +// inline the compiler will complain about not matching GOOGLE_ATTRIBUTE_NOINLINE +// above, and if not, compilation will result in multiple definitions. These +// are therefore declared as specializations here and defined in +// message_lite.cc. +template<> +MessageLite* GenericTypeHandler::NewFromPrototype( + const MessageLite* prototype, google::protobuf::Arena* arena); template<> inline google::protobuf::Arena* GenericTypeHandler::GetArena( MessageLite* value) { @@ -591,14 +598,9 @@ inline void* GenericTypeHandler::GetMaybeArenaPointer( MessageLite* value) { return value->GetMaybeArenaPointer(); } - -// Implements GenericTypeHandler specialization required by RepeatedPtrFields -// to work with MessageLite type. template <> -inline void GenericTypeHandler::Merge( - const MessageLite& from, MessageLite* to) { - to->CheckTypeAndMergeFrom(from); -} +void GenericTypeHandler::Merge(const MessageLite& from, + MessageLite* to); // Declarations of the specialization as we cannot define them here, as the // header that defines ProtocolMessage depends on types defined in this header. @@ -1221,13 +1223,17 @@ void RepeatedField::Reserve(int new_size) { Arena* arena = GetArenaNoVirtual(); new_size = max(google::protobuf::internal::kMinRepeatedFieldAllocationSize, max(total_size_ * 2, new_size)); + GOOGLE_CHECK_LE(new_size, + (std::numeric_limits::max() - kRepHeaderSize) / + sizeof(Element)) + << "Requested size is too large to fit into size_t."; if (arena == NULL) { rep_ = reinterpret_cast( - new char[kRepHeaderSize + sizeof(Element)*new_size]); + new char[kRepHeaderSize + sizeof(Element) * new_size]); } else { rep_ = reinterpret_cast( ::google::protobuf::Arena::CreateArray(arena, - kRepHeaderSize + sizeof(Element)*new_size)); + kRepHeaderSize + sizeof(Element) * new_size)); } rep_->arena = arena; int old_total_size = total_size_; @@ -1342,7 +1348,7 @@ inline void RepeatedPtrFieldBase::Swap(RepeatedPtrFieldBase* other) { } template -inline void RepeatedPtrFieldBase::SwapFallback(RepeatedPtrFieldBase* other) { +void RepeatedPtrFieldBase::SwapFallback(RepeatedPtrFieldBase* other) { GOOGLE_DCHECK(other->GetArenaNoVirtual() != GetArenaNoVirtual()); // Copy semantics in this case. We try to improve efficiency by placing the diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index af39793223..b45664b0bc 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -42,6 +42,7 @@ #include +#include #include #include #include @@ -1429,7 +1430,6 @@ class RepeatedFieldInsertionIteratorsTest : public testing::Test { std::copy(nested_ptrs.begin(), nested_ptrs.end(), RepeatedFieldBackInserter( protobuffer.mutable_repeated_nested_message())); - } virtual void TearDown() { diff --git a/src/google/protobuf/service.h b/src/google/protobuf/service.h index cc0b45d410..ad6f968548 100644 --- a/src/google/protobuf/service.h +++ b/src/google/protobuf/service.h @@ -74,12 +74,12 @@ // // To call a remote MyServiceImpl, first you need an RpcChannel connected to it. // How to construct a channel depends, again, on your RPC implementation. -// Here we use a hypothentical "MyRpcChannel" as an example: +// Here we use a hypothetical "MyRpcChannel" as an example: // MyRpcChannel channel("rpc:hostname:1234/myservice"); // MyRpcController controller; // MyServiceImpl::Stub stub(&channel); // FooRequest request; -// FooRespnose response; +// FooResponse response; // // // ... fill in request ... // @@ -102,6 +102,7 @@ #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/test_util.cc b/src/google/protobuf/test_util.cc index be1c90e069..07aa1d7781 100644 --- a/src/google/protobuf/test_util.cc +++ b/src/google/protobuf/test_util.cc @@ -42,6 +42,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/test_util_lite.cc b/src/google/protobuf/test_util_lite.cc index 88eca0ad8d..388c0cbde2 100644 --- a/src/google/protobuf/test_util_lite.cc +++ b/src/google/protobuf/test_util_lite.cc @@ -33,6 +33,7 @@ // Sanjay Ghemawat, Jeff Dean, and others. #include +#include #include diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc index 76ce987560..1aafd8e6e7 100644 --- a/src/google/protobuf/text_format_unittest.cc +++ b/src/google/protobuf/text_format_unittest.cc @@ -38,11 +38,13 @@ #include #include +#include #include #include #include #include #include +#include #include #include #include diff --git a/src/google/protobuf/unittest_mset.proto b/src/google/protobuf/unittest_mset.proto index 3aa31fa9dd..49d9adad0b 100644 --- a/src/google/protobuf/unittest_mset.proto +++ b/src/google/protobuf/unittest_mset.proto @@ -32,33 +32,31 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. // -// This file contains messages for testing message_set_wire_format. +// This file is similar to unittest_mset_wire_format.proto, but does not +// have a TestMessageSet, so it can be downgraded to proto1. syntax = "proto2"; + +import "google/protobuf/unittest_mset_wire_format.proto"; + package protobuf_unittest; option cc_enable_arenas = true; option optimize_for = SPEED; -// A message with message_set_wire_format. -message TestMessageSet { - option message_set_wire_format = true; - extensions 4 to max; -} - message TestMessageSetContainer { - optional TestMessageSet message_set = 1; + optional proto2_wireformat_unittest.TestMessageSet message_set = 1; } message TestMessageSetExtension1 { - extend TestMessageSet { + extend proto2_wireformat_unittest.TestMessageSet { optional TestMessageSetExtension1 message_set_extension = 1545008; } optional int32 i = 15; } message TestMessageSetExtension2 { - extend TestMessageSet { + extend proto2_wireformat_unittest.TestMessageSet { optional TestMessageSetExtension2 message_set_extension = 1547769; } optional string str = 25; @@ -82,4 +80,3 @@ message RawMessageSet { required bytes message = 3; } } - diff --git a/src/google/protobuf/unknown_enum_impl.h b/src/google/protobuf/unknown_enum_impl.h index 39c10cbc6f..7c68ad6c4f 100644 --- a/src/google/protobuf/unknown_enum_impl.h +++ b/src/google/protobuf/unknown_enum_impl.h @@ -34,7 +34,6 @@ #include #include -#include "net/proto/tagmapper.h" #include namespace google { @@ -59,10 +58,10 @@ namespace util { // In proto2, invalid enum values will be treated as unknown fields. This // function checks that case. bool HasUnknownEnum(const Message& message, int32 field_number, - int32* unknown_value = nullptr); + int32* unknown_value = NULL); // Same as above, but returns all unknown enums. bool GetRepeatedEnumUnknowns(const Message& message, int32 field_number, - vector* unknown_values = nullptr); + vector* unknown_values = NULL); // In proto1, invalue enum values are stored in the same way as valid enum // values. // TODO(karner): Delete this once the migration to proto2 is complete. @@ -75,7 +74,7 @@ bool GetRepeatedEnumUnknownsProto1(const Message& message, int32 field_number, // or proto2. template bool HasUnknownEnum_Template(const T& message, int32 field_number, - int32* unknown_value = nullptr) { + int32* unknown_value = NULL) { if (internal::is_base_of::value || !internal::is_base_of::value) { return HasUnknownEnum(message, field_number, unknown_value); @@ -88,7 +87,7 @@ bool HasUnknownEnum_Template(const T& message, int32 field_number, template bool GetRepeatedEnumUnknowns_Template( const T& message, int32 field_number, - vector* unknown_values = nullptr) { + vector* unknown_values = NULL) { if (internal::is_base_of::value || !internal::is_base_of::value) { return GetRepeatedEnumUnknowns(message, field_number, unknown_values); diff --git a/src/google/protobuf/unknown_enum_test.proto b/src/google/protobuf/unknown_enum_test.proto index 0ea1ede3ff..3c549cc780 100644 --- a/src/google/protobuf/unknown_enum_test.proto +++ b/src/google/protobuf/unknown_enum_test.proto @@ -36,6 +36,8 @@ syntax = "proto2"; package google.protobuf.util; +option csharp_namespace = "Google.ProtocolBuffers.TestProtos"; + message DownRevision { enum Enum { DEFAULT_VALUE = 2; diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc index 93f0f206ad..d4e383da23 100644 --- a/src/google/protobuf/unknown_field_set.cc +++ b/src/google/protobuf/unknown_field_set.cc @@ -34,6 +34,7 @@ #include +#include #include #include #include diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h index 6781cd0f12..612a942a77 100644 --- a/src/google/protobuf/unknown_field_set.h +++ b/src/google/protobuf/unknown_field_set.h @@ -42,6 +42,7 @@ #include #include #include +#include namespace google { namespace protobuf { diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc index 9b02f0b040..5de72630a6 100644 --- a/src/google/protobuf/unknown_field_set_unittest.cc +++ b/src/google/protobuf/unknown_field_set_unittest.cc @@ -43,7 +43,10 @@ #include #include +#include #include +#include +#include #include #include #include diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index 944fb2e362..ea36079895 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -79,7 +79,9 @@ StatusOr NumberConvertAndCheck(From before) { // For conversion between double and float only. template StatusOr FloatingPointConvertAndCheck(From before) { - if (MathLimits::IsNaN(before)) return std::numeric_limits::quiet_NaN(); + if (MathLimits::IsNaN(before)) { + return std::numeric_limits::quiet_NaN(); + } To after = static_cast(before); if (MathUtil::AlmostEquals(after, before)) { @@ -167,7 +169,7 @@ StatusOr DataPiece::ToString() const { return str_.ToString(); case TYPE_BYTES: { string base64; - WebSafeBase64Escape(str_, &base64); + Base64Escape(str_, &base64); return base64; } default: diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index 267e2cd3a0..97b248fff0 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -46,6 +46,7 @@ DefaultValueObjectWriter::DefaultValueObjectWriter( TypeResolver* type_resolver, const google::protobuf::Type& type, ObjectWriter* ow) : typeinfo_(TypeInfo::NewTypeInfo(type_resolver)), + own_typeinfo_(true), type_(type), disable_normalize_(false), current_(NULL), @@ -56,6 +57,9 @@ DefaultValueObjectWriter::~DefaultValueObjectWriter() { for (int i = 0; i < string_values_.size(); ++i) { delete string_values_[i]; } + if (own_typeinfo_) { + delete typeinfo_; + } } DefaultValueObjectWriter* DefaultValueObjectWriter::RenderBool(StringPiece name, @@ -197,33 +201,47 @@ void DefaultValueObjectWriter::Node::WriteTo(ObjectWriter* ow) { if (disable_normalize_) { ow->DisableCaseNormalizationForNextKey(); } + if (kind_ == PRIMITIVE) { ObjectWriter::RenderDataPieceTo(data_, name_, ow); return; } - if (is_placeholder_) { - // If is_placeholder_ = true, we didn't see this node in the response, so - // skip output. + + // Render maps. Empty maps are rendered as "{}". + if (kind_ == MAP) { + ow->StartObject(name_); + WriteChildren(ow); + ow->EndObject(); return; } + + // Write out lists. If we didn't have any list in response, write out empty + // list. if (kind_ == LIST) { ow->StartList(name_); - } else { - ow->StartObject(name_); + WriteChildren(ow); + ow->EndList(); + return; } + + // If is_placeholder_ = true, we didn't see this node in the response, so + // skip output. + if (is_placeholder_) return; + + ow->StartObject(name_); + WriteChildren(ow); + ow->EndObject(); +} + +void DefaultValueObjectWriter::Node::WriteChildren(ObjectWriter* ow) { for (int i = 0; i < children_.size(); ++i) { Node* child = children_[i]; child->WriteTo(ow); } - if (kind_ == LIST) { - ow->EndList(); - } else { - ow->EndObject(); - } } const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( - const google::protobuf::Type& found_type, TypeInfo* typeinfo) { + const google::protobuf::Type& found_type, const TypeInfo* typeinfo) { // If this field is a map, we should use the type of its "Value" as // the type of the child node. for (int i = 0; i < found_type.fields_size(); ++i) { @@ -248,7 +266,8 @@ const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( return NULL; } -void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) { +void DefaultValueObjectWriter::Node::PopulateChildren( + const TypeInfo* typeinfo) { // Ignores well known types that don't require automatically populating their // primitive children. For type "Any", we only populate its children when the // "@type" field is set. @@ -310,15 +329,17 @@ void DefaultValueObjectWriter::Node::PopulateChildren(TypeInfo* typeinfo) { google::protobuf::Field_Cardinality_CARDINALITY_REPEATED) { kind = LIST; } - // If the child field is of primitive type, sets its data to the default - // value of its type. + // If oneof_index() != 0, the child field is part of a "oneof", which means // the child field is optional and we shouldn't populate its default value. + if (field.oneof_index() != 0) continue; + + // If the child field is of primitive type, sets its data to the default + // value of its type. google::protobuf::scoped_ptr child( - new Node(field.name(), field_type, kind, - ((kind == PRIMITIVE && field.oneof_index() == 0) - ? CreateDefaultDataPieceForField(field) - : DataPiece::NullData()), + new Node(field.json_name(), field_type, kind, + kind == PRIMITIVE ? CreateDefaultDataPieceForField(field) + : DataPiece::NullData(), true)); new_children.push_back(child.release()); } @@ -338,7 +359,7 @@ void DefaultValueObjectWriter::MaybePopulateChildrenOfAny(Node* node) { // have been added, populates its children. if (node != NULL && node->is_any() && node->type() != NULL && node->type()->name() != kAnyType && node->number_of_children() == 1) { - node->PopulateChildren(typeinfo_.get()); + node->PopulateChildren(typeinfo_); } } @@ -388,7 +409,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( root_.reset(new Node(name.ToString(), &type_, OBJECT, DataPiece::NullData(), false)); root_->set_disable_normalize(GetAndResetDisableNormalize()); - root_->PopulateChildren(typeinfo_.get()); + root_->PopulateChildren(typeinfo_); current_ = root_.get(); return this; } @@ -409,7 +430,7 @@ DefaultValueObjectWriter* DefaultValueObjectWriter::StartObject( child->set_is_placeholder(false); child->set_disable_normalize(GetAndResetDisableNormalize()); if (child->kind() == OBJECT && child->number_of_children() == 0) { - child->PopulateChildren(typeinfo_.get()); + child->PopulateChildren(typeinfo_); } stack_.push(current_); @@ -492,12 +513,11 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, // first value field is rendered before we populate the children, because // the "value" field of a Any message could be omitted. if (current_->number_of_children() > 1 && current_->type() != NULL) { - current_->PopulateChildren(typeinfo_.get()); + current_->PopulateChildren(typeinfo_); } } Node* child = current_->FindChild(name); if (child == NULL || child->kind() != PRIMITIVE) { - GOOGLE_LOG(WARNING) << "Cannot find primitive field '" << name << "'."; // No children are found, creates a new child. google::protobuf::scoped_ptr node( new Node(name.ToString(), NULL, PRIMITIVE, data, false)); diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.h b/src/google/protobuf/util/internal/default_value_objectwriter.h index 759ba91b5d..2468c8d913 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.h +++ b/src/google/protobuf/util/internal/default_value_objectwriter.h @@ -57,7 +57,7 @@ namespace converter { // ObjectWriter when EndObject() is called on the root object. It also writes // out all non-repeated primitive fields that haven't been explicitly rendered // with their default values (0 for numbers, "" for strings, etc). -class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { +class DefaultValueObjectWriter : public ObjectWriter { public: DefaultValueObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, @@ -129,7 +129,7 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Populates children of this Node based on its type. If there are already // children created, they will be merged to the result. Caller should pass // in TypeInfo for looking up types of the children. - void PopulateChildren(TypeInfo* typeinfo); + void PopulateChildren(const TypeInfo* typeinfo); // If this node is a leaf (has data), writes the current node to the // ObjectWriter; if not, then recursively writes the children to the @@ -165,7 +165,10 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Returns the Value Type of a map given the Type of the map entry and a // TypeInfo instance. const google::protobuf::Type* GetMapValueType( - const google::protobuf::Type& entry_type, TypeInfo* typeinfo); + const google::protobuf::Type& entry_type, const TypeInfo* typeinfo); + + // Calls WriteTo() on every child in children_. + void WriteChildren(ObjectWriter* ow); // The name of this node. string name_; @@ -210,7 +213,9 @@ class LIBPROTOBUF_EXPORT DefaultValueObjectWriter : public ObjectWriter { // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. - google::protobuf::scoped_ptr typeinfo_; + const TypeInfo* typeinfo_; + // Whether the TypeInfo object is owned by this class. + bool own_typeinfo_; // google::protobuf::Type of the root message type. const google::protobuf::Type& type_; // Holds copies of strings passed to RenderString. diff --git a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc index 593c7105d0..237d0722ac 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter_test.cc @@ -73,15 +73,15 @@ INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, TEST_P(DefaultValueObjectWriterTest, Empty) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 0.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") - ->RenderBytes("bytes_value", "") + ->RenderDouble("doubleValue", 0.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") + ->RenderBytes("bytesValue", "") ->EndObject(); // Actual testing @@ -91,42 +91,42 @@ TEST_P(DefaultValueObjectWriterTest, Empty) { TEST_P(DefaultValueObjectWriterTest, NonDefaultDouble) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 1.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") + ->RenderDouble("doubleValue", 1.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") ->EndObject(); // Actual testing - testing_->StartObject("")->RenderDouble("double_value", 1.0)->EndObject(); + testing_->StartObject("")->RenderDouble("doubleValue", 1.0)->EndObject(); } TEST_P(DefaultValueObjectWriterTest, ShouldRetainUnknownField) { // Set expectation expects_.StartObject("") - ->RenderDouble("double_value", 1.0) - ->RenderFloat("float_value", 0.0) - ->RenderInt64("int64_value", 0) - ->RenderUint64("uint64_value", 0) - ->RenderInt32("int32_value", 0) - ->RenderUint32("uint32_value", 0) - ->RenderBool("bool_value", false) - ->RenderString("string_value", "") + ->RenderDouble("doubleValue", 1.0) + ->RenderFloat("floatValue", 0.0) + ->RenderInt64("int64Value", 0) + ->RenderUint64("uint64Value", 0) + ->RenderInt32("int32Value", 0) + ->RenderUint32("uint32Value", 0) + ->RenderBool("boolValue", false) + ->RenderString("stringValue", "") ->RenderString("unknown", "abc") - ->StartObject("unknown_object") + ->StartObject("unknownObject") ->RenderString("unknown", "def") ->EndObject() ->EndObject(); // Actual testing testing_->StartObject("") - ->RenderDouble("double_value", 1.0) + ->RenderDouble("doubleValue", 1.0) ->RenderString("unknown", "abc") - ->StartObject("unknown_object") + ->StartObject("unknownObject") ->RenderString("unknown", "def") ->EndObject() ->EndObject(); diff --git a/src/google/protobuf/util/internal/error_listener.h b/src/google/protobuf/util/internal/error_listener.h index 9b907df505..2699684d41 100644 --- a/src/google/protobuf/util/internal/error_listener.h +++ b/src/google/protobuf/util/internal/error_listener.h @@ -37,7 +37,9 @@ #endif #include +#include #include +#include #include #include diff --git a/src/google/protobuf/util/internal/field_mask_utility.cc b/src/google/protobuf/util/internal/field_mask_utility.cc index 92468959a7..f0e8fc88a4 100644 --- a/src/google/protobuf/util/internal/field_mask_utility.cc +++ b/src/google/protobuf/util/internal/field_mask_utility.cc @@ -34,7 +34,6 @@ #include namespace google { - namespace protobuf { namespace util { namespace converter { @@ -138,7 +137,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, } // Un-escaped '"' must be followed with a ']'. if (i >= length - 1 || paths[i + 1] != ']') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be represented as [\"some_key\"].")); @@ -150,7 +149,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // Checks whether the key ends at the end of a path segment. if (i < length - 1 && paths[i + 1] != '.' && paths[i + 1] != ',' && paths[i + 1] != ')' && paths[i + 1] != '(') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be at the end of a path segment.")); @@ -162,7 +161,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // We are not in a map key, look for the start of one. if (paths[i] == '[') { if (i >= length - 1 || paths[i + 1] != '\"') { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Map keys should be represented as [\"some_key\"].")); @@ -198,7 +197,7 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, // Removes the last prefix after seeing a ')'. if (i < length && paths[i] == ')') { if (prefix.empty()) { - return CreatePublicError( + return util::Status( util::error::INVALID_ARGUMENT, StrCat("Invalid FieldMask '", paths, "'. Cannot find matching '(' for all ')'.")); @@ -208,16 +207,14 @@ util::Status DecodeCompactFieldMaskPaths(StringPiece paths, previous_position = i + 1; } if (in_map_key) { - return CreatePublicError( - util::error::INVALID_ARGUMENT, - StrCat("Invalid FieldMask '", paths, - "'. Cannot find matching ']' for all '['.")); + return util::Status(util::error::INVALID_ARGUMENT, + StrCat("Invalid FieldMask '", paths, + "'. Cannot find matching ']' for all '['.")); } if (!prefix.empty()) { - return CreatePublicError( - util::error::INVALID_ARGUMENT, - StrCat("Invalid FieldMask '", paths, - "'. Cannot find matching ')' for all '('.")); + return util::Status(util::error::INVALID_ARGUMENT, + StrCat("Invalid FieldMask '", paths, + "'. Cannot find matching ')' for all '('.")); } return util::Status::OK; } diff --git a/src/google/protobuf/util/internal/json_escaping.cc b/src/google/protobuf/util/internal/json_escaping.cc index 5ac23421ca..36dc8ef912 100644 --- a/src/google/protobuf/util/internal/json_escaping.cc +++ b/src/google/protobuf/util/internal/json_escaping.cc @@ -30,6 +30,7 @@ #include +#include #include namespace google { diff --git a/src/google/protobuf/util/internal/json_objectwriter.cc b/src/google/protobuf/util/internal/json_objectwriter.cc index 0c41515f9e..d88a81f9d3 100644 --- a/src/google/protobuf/util/internal/json_objectwriter.cc +++ b/src/google/protobuf/util/internal/json_objectwriter.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -142,7 +143,7 @@ JsonObjectWriter* JsonObjectWriter::RenderBytes(StringPiece name, StringPiece value) { WritePrefix(name); string base64; - WebSafeBase64EscapeWithPadding(value, &base64); + Base64Escape(value, &base64); WriteChar('"'); // TODO(wpoon): Consider a ByteSink solution that writes the base64 bytes // directly to the stream, rather than first putting them diff --git a/src/google/protobuf/util/internal/json_objectwriter_test.cc b/src/google/protobuf/util/internal/json_objectwriter_test.cc index df9a133e02..dcd6060113 100644 --- a/src/google/protobuf/util/internal/json_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/json_objectwriter_test.cc @@ -47,7 +47,8 @@ class JsonObjectWriterTest : public ::testing::Test { JsonObjectWriterTest() : str_stream_(new StringOutputStream(&output_)), out_stream_(new CodedOutputStream(str_stream_)), - ow_(NULL) {} + ow_(NULL) { + } virtual ~JsonObjectWriterTest() { delete ow_; @@ -63,34 +64,36 @@ class JsonObjectWriterTest : public ::testing::Test { TEST_F(JsonObjectWriterTest, EmptyRootObject) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("")->EndObject(); + ow_->StartObject("") + ->EndObject(); EXPECT_EQ("{}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartObject("empty") - ->EndObject() - ->EndObject(); + ->RenderString("test", "value") + ->StartObject("empty") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":{}}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyRootList) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartList("")->EndList(); + ow_->StartList("") + ->EndList(); EXPECT_EQ("[]", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, EmptyList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderString("test", "value") - ->StartList("empty") - ->EndList() - ->EndObject(); + ->RenderString("test", "value") + ->StartList("empty") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"test\":\"value\",\"empty\":[]}", output_.substr(0, out_stream_->ByteCount())); } @@ -98,10 +101,10 @@ TEST_F(JsonObjectWriterTest, EmptyList) { TEST_F(JsonObjectWriterTest, ObjectInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartObject("nested") - ->RenderString("field", "value") - ->EndObject() - ->EndObject(); + ->StartObject("nested") + ->RenderString("field", "value") + ->EndObject() + ->EndObject(); EXPECT_EQ("{\"nested\":{\"field\":\"value\"}}", output_.substr(0, out_stream_->ByteCount())); } @@ -109,10 +112,10 @@ TEST_F(JsonObjectWriterTest, ObjectInObject) { TEST_F(JsonObjectWriterTest, ListInObject) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->StartList("nested") - ->RenderString("", "value") - ->EndList() - ->EndObject(); + ->StartList("nested") + ->RenderString("", "value") + ->EndList() + ->EndObject(); EXPECT_EQ("{\"nested\":[\"value\"]}", output_.substr(0, out_stream_->ByteCount())); } @@ -120,10 +123,10 @@ TEST_F(JsonObjectWriterTest, ListInObject) { TEST_F(JsonObjectWriterTest, ObjectInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartObject("") - ->RenderString("field", "value") - ->EndObject() - ->EndList(); + ->StartObject("") + ->RenderString("field", "value") + ->EndObject() + ->EndList(); EXPECT_EQ("[{\"field\":\"value\"}]", output_.substr(0, out_stream_->ByteCount())); } @@ -131,10 +134,10 @@ TEST_F(JsonObjectWriterTest, ObjectInList) { TEST_F(JsonObjectWriterTest, ListInList) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartList("") - ->StartList("") - ->RenderString("", "value") - ->EndList() - ->EndList(); + ->StartList("") + ->RenderString("", "value") + ->EndList() + ->EndList(); EXPECT_EQ("[[\"value\"]]", output_.substr(0, out_stream_->ByteCount())); } @@ -164,97 +167,95 @@ TEST_F(JsonObjectWriterTest, RenderPrimitives) { output_.substr(0, out_stream_->ByteCount())); } -TEST_F(JsonObjectWriterTest, BytesEncodesAsWebSafeBase64) { +TEST_F(JsonObjectWriterTest, BytesEncodesAsNonWebSafeBase64) { string s; s.push_back('\377'); s.push_back('\357'); ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("")->RenderBytes("bytes", s)->EndObject(); // Non-web-safe would encode this as "/+8=" - EXPECT_EQ("{\"bytes\":\"_-8=\"}", + EXPECT_EQ("{\"bytes\":\"/+8=\"}", output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("items") - ->RenderString("", "item1") - ->RenderString("", "item2") - ->RenderString("", "item3") - ->EndList() - ->StartList("empty") - ->EndList() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"items\": [\n" - " \"item1\",\n" - " \"item2\",\n" - " \"item3\"\n" - " ],\n" - " \"empty\": []\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("items") + ->RenderString("", "item1") + ->RenderString("", "item2") + ->RenderString("", "item3") + ->EndList() + ->StartList("empty") + ->EndList() + ->EndObject(); + EXPECT_EQ("{\n" + " \"items\": [\n" + " \"item1\",\n" + " \"item2\",\n" + " \"item3\"\n" + " ],\n" + " \"empty\": []\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintObject) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartObject("items") - ->RenderString("key1", "item1") - ->RenderString("key2", "item2") - ->RenderString("key3", "item3") - ->EndObject() - ->StartObject("empty") - ->EndObject() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"items\": {\n" - " \"key1\": \"item1\",\n" - " \"key2\": \"item2\",\n" - " \"key3\": \"item3\"\n" - " },\n" - " \"empty\": {}\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartObject("items") + ->RenderString("key1", "item1") + ->RenderString("key2", "item2") + ->RenderString("key3", "item3") + ->EndObject() + ->StartObject("empty") + ->EndObject() + ->EndObject(); + EXPECT_EQ("{\n" + " \"items\": {\n" + " \"key1\": \"item1\",\n" + " \"key2\": \"item2\",\n" + " \"key3\": \"item3\"\n" + " },\n" + " \"empty\": {}\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintEmptyObjectInEmptyList) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->StartList("list") - ->StartObject("") - ->EndObject() - ->EndList() - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"list\": [\n" - " {}\n" - " ]\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->StartList("list") + ->StartObject("") + ->EndObject() + ->EndList() + ->EndObject(); + EXPECT_EQ("{\n" + " \"list\": [\n" + " {}\n" + " ]\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, PrettyPrintDoubleIndent) { ow_ = new JsonObjectWriter(" ", out_stream_); ow_->StartObject("") - ->RenderBool("bool", true) - ->RenderInt32("int", 42) - ->EndObject(); - EXPECT_EQ( - "{\n" - " \"bool\": true,\n" - " \"int\": 42\n" - "}\n", - output_.substr(0, out_stream_->ByteCount())); + ->RenderBool("bool", true) + ->RenderInt32("int", 42) + ->EndObject(); + EXPECT_EQ("{\n" + " \"bool\": true,\n" + " \"int\": 42\n" + "}\n", + output_.substr(0, out_stream_->ByteCount())); } TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { ow_ = new JsonObjectWriter("", out_stream_); - ow_->StartObject("")->RenderString("string", "'<>&\\\"\r\n")->EndObject(); + ow_->StartObject("") + ->RenderString("string", "'<>&\\\"\r\n") + ->EndObject(); EXPECT_EQ("{\"string\":\"'\\u003c\\u003e&\\\\\\\"\\r\\n\"}", output_.substr(0, out_stream_->ByteCount())); } @@ -262,13 +263,13 @@ TEST_F(JsonObjectWriterTest, StringsEscapedAndEnclosedInDoubleQuotes) { TEST_F(JsonObjectWriterTest, Stringification) { ow_ = new JsonObjectWriter("", out_stream_); ow_->StartObject("") - ->RenderDouble("double_nan", std::numeric_limits::quiet_NaN()) - ->RenderFloat("float_nan", std::numeric_limits::quiet_NaN()) - ->RenderDouble("double_pos", std::numeric_limits::infinity()) - ->RenderFloat("float_pos", std::numeric_limits::infinity()) - ->RenderDouble("double_neg", -std::numeric_limits::infinity()) - ->RenderFloat("float_neg", -std::numeric_limits::infinity()) - ->EndObject(); + ->RenderDouble("double_nan", std::numeric_limits::quiet_NaN()) + ->RenderFloat("float_nan", std::numeric_limits::quiet_NaN()) + ->RenderDouble("double_pos", std::numeric_limits::infinity()) + ->RenderFloat("float_pos", std::numeric_limits::infinity()) + ->RenderDouble("double_neg", -std::numeric_limits::infinity()) + ->RenderFloat("float_neg", -std::numeric_limits::infinity()) + ->EndObject(); EXPECT_EQ( "{\"double_nan\":\"NaN\"," "\"float_nan\":\"NaN\"," diff --git a/src/google/protobuf/util/internal/json_stream_parser.cc b/src/google/protobuf/util/internal/json_stream_parser.cc index d439a221d8..a7ef7fe290 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.cc +++ b/src/google/protobuf/util/internal/json_stream_parser.cc @@ -40,6 +40,7 @@ #include #endif +#include #include #include #include @@ -104,16 +105,42 @@ JsonStreamParser::JsonStreamParser(ObjectWriter* ow) parsed_(), parsed_storage_(), string_open_(0), - utf8_storage_(), - utf8_length_(0) { + chunk_storage_(), + coerce_to_utf8_(false) { // Initialize the stack with a single value to be parsed. stack_.push(VALUE); } JsonStreamParser::~JsonStreamParser() {} + util::Status JsonStreamParser::Parse(StringPiece json) { - return ParseChunk(json); + StringPiece chunk = json; + // If we have leftovers from a previous chunk, append the new chunk to it + // and create a new StringPiece pointing at the string's data. This could + // be large but we rely on the chunks to be small, assuming they are + // fragments of a Cord. + if (!leftover_.empty()) { + // Don't point chunk to leftover_ because leftover_ will be updated in + // ParseChunk(chunk). + chunk_storage_.swap(leftover_); + json.AppendToString(&chunk_storage_); + chunk = StringPiece(chunk_storage_); + } + + // Find the structurally valid UTF8 prefix and parse only that. + int n = internal::UTF8SpnStructurallyValid(chunk); + if (n > 0) { + util::Status status = ParseChunk(chunk.substr(0, n)); + + // Any leftover characters are stashed in leftover_ for later parsing when + // there is more data available. + chunk.substr(n).AppendToString(&leftover_); + return status; + } else { + chunk.CopyToString(&leftover_); + return util::Status::OK; + } } util::Status JsonStreamParser::FinishParse() { @@ -122,9 +149,22 @@ util::Status JsonStreamParser::FinishParse() { if (stack_.empty() && leftover_.empty()) { return util::Status::OK; } + + // Storage for UTF8-coerced string. + google::protobuf::scoped_array utf8; + if (coerce_to_utf8_) { + utf8.reset(new char[leftover_.size()]); + char* coerced = internal::UTF8CoerceToStructurallyValid(leftover_, utf8.get(), ' '); + p_ = json_ = StringPiece(coerced, leftover_.size()); + } else { + if (!internal::IsStructurallyValidUTF8(leftover_)) { + return ReportFailure("Encountered non UTF-8 code points."); + } + p_ = json_ = leftover_; + } + // Parse the remainder in finishing mode, which reports errors for things like // unterminated strings or unknown tokens that would normally be retried. - p_ = json_ = StringPiece(leftover_); finishing_ = true; util::Status result = RunParser(); if (result.ok()) { @@ -137,16 +177,10 @@ util::Status JsonStreamParser::FinishParse() { } util::Status JsonStreamParser::ParseChunk(StringPiece chunk) { - // If we have leftovers from a previous chunk, append the new chunk to it and - // create a new StringPiece pointing at the string's data. This could be - // large but we rely on the chunks to be small, assuming they are fragments - // of a Cord. - if (!leftover_.empty()) { - chunk.AppendToString(&leftover_); - p_ = json_ = StringPiece(leftover_); - } else { - p_ = json_ = chunk; - } + // Do not do any work if the chunk is empty. + if (chunk.empty()) return util::Status::OK; + + p_ = json_ = chunk; finishing_ = false; util::Status result = RunParser(); diff --git a/src/google/protobuf/util/internal/json_stream_parser.h b/src/google/protobuf/util/internal/json_stream_parser.h index 17b094ae11..0278c28fbc 100644 --- a/src/google/protobuf/util/internal/json_stream_parser.h +++ b/src/google/protobuf/util/internal/json_stream_parser.h @@ -75,12 +75,14 @@ class LIBPROTOBUF_EXPORT JsonStreamParser { explicit JsonStreamParser(ObjectWriter* ow); virtual ~JsonStreamParser(); - // Parse a JSON string (UTF-8 encoded). + // Parses a UTF-8 encoded JSON string from a StringPiece. util::Status Parse(StringPiece json); + // Finish parsing the JSON string. util::Status FinishParse(); + private: enum TokenType { BEGIN_STRING, // " or ' @@ -239,11 +241,11 @@ class LIBPROTOBUF_EXPORT JsonStreamParser { // A value of 0 indicates that string parsing is not in process. char string_open_; - // Storage for utf8-coerced bytes. - google::protobuf::scoped_array utf8_storage_; + // Storage for the chunk that are being parsed in ParseChunk(). + string chunk_storage_; - // Length of the storage for utf8-coerced bytes. - int utf8_length_; + // Whether to allow non UTF-8 encoded input and replace invalid code points. + bool coerce_to_utf8_; GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(JsonStreamParser); }; diff --git a/src/google/protobuf/util/internal/json_stream_parser_test.cc b/src/google/protobuf/util/internal/json_stream_parser_test.cc index b0775a2fe2..c833ed1fd3 100644 --- a/src/google/protobuf/util/internal/json_stream_parser_test.cc +++ b/src/google/protobuf/util/internal/json_stream_parser_test.cc @@ -30,6 +30,7 @@ #include +#include #include #include #include @@ -85,7 +86,7 @@ class JsonStreamParserTest : public ::testing::Test { JsonStreamParserTest() : mock_(), ow_(&mock_) {} virtual ~JsonStreamParserTest() {} - util::Status RunTest(StringPiece json, int split) { + util::Status RunTest(StringPiece json, int split, bool coerce_utf8 = false) { JsonStreamParser parser(&mock_); // Special case for split == length, test parsing one character at a time. @@ -115,8 +116,8 @@ class JsonStreamParserTest : public ::testing::Test { return result; } - void DoTest(StringPiece json, int split) { - util::Status result = RunTest(json, split); + void DoTest(StringPiece json, int split, bool coerce_utf8 = false) { + util::Status result = RunTest(json, split, coerce_utf8); if (!result.ok()) { GOOGLE_LOG(WARNING) << result; } @@ -337,14 +338,26 @@ TEST_F(JsonStreamParserTest, ObjectValues) { } } + +TEST_F(JsonStreamParserTest, RejectNonUtf8WhenNotCoerced) { + StringPiece json = "{\"address\":\xFF\"חרושת 23, רעננה, ישראל\"}"; + for (int i = 0; i <= json.length(); ++i) { + DoErrorTest(json, i, "Encountered non UTF-8 code points."); + } + json = "{\"address\": \"חרושת 23,\xFFרעננה, ישראל\"}"; + for (int i = 0; i <= json.length(); ++i) { + DoErrorTest(json, i, "Encountered non UTF-8 code points."); + } +} + #ifndef _MSC_VER // - unicode handling in strings TEST_F(JsonStreamParserTest, UnicodeEscaping) { StringPiece str = "[\"\\u0639\\u0631\\u0628\\u0649\"]"; for (int i = 0; i <= str.length(); ++i) { // TODO(xiaofeng): Figure out what default encoding to use for JSON strings. - // In protobuf we use UTF-8 for strings, but for JSON we probably should allow - // different encodings? + // In protobuf we use UTF-8 for strings, but for JSON we probably should + // allow different encodings? ow_.StartList("")->RenderString("", "\u0639\u0631\u0628\u0649")->EndList(); DoTest(str, i); } diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 53a0e47acc..18bb27729f 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -96,7 +97,7 @@ ProtoStreamObjectSource::ProtoStreamObjectSource( } ProtoStreamObjectSource::ProtoStreamObjectSource( - google::protobuf::io::CodedInputStream* stream, TypeInfo* typeinfo, + google::protobuf::io::CodedInputStream* stream, const TypeInfo* typeinfo, const google::protobuf::Type& type) : stream_(stream), typeinfo_(typeinfo), own_typeinfo_(false), type_(type) { GOOGLE_LOG_IF(DFATAL, stream == NULL) << "Input stream is NULL."; @@ -156,7 +157,7 @@ Status ProtoStreamObjectSource::WriteMessage(const google::protobuf::Type& type, last_tag = tag; field = FindAndVerifyField(type, tag); if (field != NULL) { - field_name = field->name(); + field_name = field->json_name(); } } if (field == NULL) { @@ -214,7 +215,7 @@ StatusOr ProtoStreamObjectSource::RenderMap( const google::protobuf::Field* field, StringPiece name, uint32 list_tag, ObjectWriter* ow) const { const google::protobuf::Type* field_type = - typeinfo_->GetType(field->type_url()); + typeinfo_->GetTypeByTypeUrl(field->type_url()); uint32 tag_to_return = 0; if (IsPackable(*field) && list_tag == @@ -784,7 +785,8 @@ Status ProtoStreamObjectSource::RenderField( // Get the nested enum type for this field. // TODO(skarvaje): Avoid string manipulation. Find ways to speed this // up. - const google::protobuf::Enum* en = typeinfo_->GetEnum(field->type_url()); + const google::protobuf::Enum* en = + typeinfo_->GetEnumByTypeUrl(field->type_url()); // Lookup the name of the enum, and render that. Skips unknown enums. if (en != NULL) { const google::protobuf::EnumValue* enum_value = @@ -819,7 +821,7 @@ Status ProtoStreamObjectSource::RenderField( int old_limit = stream_->PushLimit(buffer32); // Get the nested message type for this field. const google::protobuf::Type* type = - typeinfo_->GetType(field->type_url()); + typeinfo_->GetTypeByTypeUrl(field->type_url()); if (type == NULL) { return Status(util::error::INTERNAL, StrCat("Invalid configuration. Could not find the type: ", @@ -928,7 +930,8 @@ const string ProtoStreamObjectSource::ReadFieldValueAsString( // Get the nested enum type for this field. // TODO(skarvaje): Avoid string manipulation. Find ways to speed this // up. - const google::protobuf::Enum* en = typeinfo_->GetEnum(field.type_url()); + const google::protobuf::Enum* en = + typeinfo_->GetEnumByTypeUrl(field.type_url()); // Lookup the name of the enum, and render that. Skips unknown enums. if (en != NULL) { const google::protobuf::EnumValue* enum_value = @@ -962,7 +965,7 @@ const string ProtoStreamObjectSource::ReadFieldValueAsString( bool ProtoStreamObjectSource::IsMap( const google::protobuf::Field& field) const { const google::protobuf::Type* field_type = - typeinfo_->GetType(field.type_url()); + typeinfo_->GetTypeByTypeUrl(field.type_url()); // TODO(xiaofeng): Unify option names. return field.kind() == google::protobuf::Field_Kind_TYPE_MESSAGE && diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index 4a4e6bbf0d..845437f584 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -46,7 +46,6 @@ #include - namespace google { namespace protobuf { class Field; @@ -61,7 +60,10 @@ namespace converter { class TypeInfo; // An ObjectSource that can parse a stream of bytes as a protocol buffer. -// This implementation uses a tech Type for tag lookup. +// Its WriteTo() method can be given an ObjectWriter. +// This implementation uses a google.protobuf.Type for tag and name lookup. +// The field names are converted into lower camel-case when writing to the +// ObjectWriter. // // Sample usage: (suppose input is: string proto) // ArrayInputStream arr_stream(proto.data(), proto.size()); @@ -93,7 +95,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { private: ProtoStreamObjectSource(google::protobuf::io::CodedInputStream* stream, - TypeInfo* typeinfo, + const TypeInfo* typeinfo, const google::protobuf::Type& type); // Function that renders a well known type with a modified behavior. typedef util::Status (*TypeRenderer)(const ProtoStreamObjectSource*, @@ -226,7 +228,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Whether this class owns the typeinfo_ object. If true the typeinfo_ object // should be deleted in the destructor. bool own_typeinfo_; diff --git a/src/google/protobuf/util/internal/protostream_objectsource_test.cc b/src/google/protobuf/util/internal/protostream_objectsource_test.cc index 4cc624104b..f6e5ee7a17 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource_test.cc @@ -117,61 +117,61 @@ class ProtostreamObjectSourceTest void PrepareExpectingObjectWriterForRepeatedPrimitive() { ow_.StartObject("") - ->StartList("rep_fix32") + ->StartList("repFix32") ->RenderUint32("", bit_cast(3201)) ->RenderUint32("", bit_cast(0)) ->RenderUint32("", bit_cast(3202)) ->EndList() - ->StartList("rep_u32") + ->StartList("repU32") ->RenderUint32("", bit_cast(3203)) ->RenderUint32("", bit_cast(0)) ->EndList() - ->StartList("rep_i32") + ->StartList("repI32") ->RenderInt32("", 0) ->RenderInt32("", 3204) ->RenderInt32("", 3205) ->EndList() - ->StartList("rep_sf32") + ->StartList("repSf32") ->RenderInt32("", 3206) ->RenderInt32("", 0) ->EndList() - ->StartList("rep_s32") + ->StartList("repS32") ->RenderInt32("", 0) ->RenderInt32("", 3207) ->RenderInt32("", 3208) ->EndList() - ->StartList("rep_fix64") + ->StartList("repFix64") ->RenderUint64("", bit_cast(6401LL)) ->RenderUint64("", bit_cast(0LL)) ->EndList() - ->StartList("rep_u64") + ->StartList("repU64") ->RenderUint64("", bit_cast(0LL)) ->RenderUint64("", bit_cast(6402LL)) ->RenderUint64("", bit_cast(6403LL)) ->EndList() - ->StartList("rep_i64") + ->StartList("repI64") ->RenderInt64("", 6404L) ->RenderInt64("", 0L) ->EndList() - ->StartList("rep_sf64") + ->StartList("repSf64") ->RenderInt64("", 0L) ->RenderInt64("", 6405L) ->RenderInt64("", 6406L) ->EndList() - ->StartList("rep_s64") + ->StartList("repS64") ->RenderInt64("", 6407L) ->RenderInt64("", 0L) ->EndList() - ->StartList("rep_float") + ->StartList("repFloat") ->RenderFloat("", 0.0f) ->RenderFloat("", 32.1f) ->RenderFloat("", 32.2f) ->EndList() - ->StartList("rep_double") + ->StartList("repDouble") ->RenderDouble("", 64.1L) ->RenderDouble("", 0.0L) ->EndList() - ->StartList("rep_bool") + ->StartList("repBool") ->RenderBool("", true) ->RenderBool("", false) ->EndList() @@ -317,11 +317,11 @@ TEST_P(ProtostreamObjectSourceTest, RepeatingPrimitives) { primitive.add_rep_str("String Two"); primitive.add_rep_bytes("Some Bytes"); - ow_.StartList("rep_str") + ow_.StartList("repStr") ->RenderString("", "String One") ->RenderString("", "String Two") ->EndList() - ->StartList("rep_bytes") + ->StartList("repBytes") ->RenderBytes("", "Some Bytes") ->EndList(); DoTest(primitive, Primitive::descriptor()); @@ -794,16 +794,16 @@ TEST_P(ProtostreamObjectSourceFieldMaskTest, FieldMaskRenderSuccess) { ow_.StartObject("") ->RenderString("id", "1") - ->RenderString("single_mask", "path1,snakeCasePath2") - ->StartList("repeated_mask") + ->RenderString("singleMask", "path1,snakeCasePath2") + ->StartList("repeatedMask") ->RenderString("", "path3") ->RenderString("", "snakeCasePath4,path5") ->EndList() - ->StartList("nested_mask") + ->StartList("nestedMask") ->StartObject("") ->RenderString("data", "data") - ->RenderString("single_mask", "nested.path1,nestedField.snakeCasePath2") - ->StartList("repeated_mask") + ->RenderString("singleMask", "nested.path1,nestedField.snakeCasePath2") + ->StartList("repeatedMask") ->RenderString("", "nestedField.path3,nested.snakeCasePath4") ->RenderString("", "nested.path5") ->RenderString("", diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index f9ddbf326d..87f504e0a4 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -74,7 +74,7 @@ ProtoStreamObjectWriter::ProtoStreamObjectWriter( tracker_(new ObjectLocationTracker()) {} ProtoStreamObjectWriter::ProtoStreamObjectWriter( - TypeInfo* typeinfo, const google::protobuf::Type& type, + const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener) : master_type_(type), typeinfo_(typeinfo), @@ -91,14 +91,19 @@ ProtoStreamObjectWriter::ProtoStreamObjectWriter( tracker_(new ObjectLocationTracker()) {} ProtoStreamObjectWriter::~ProtoStreamObjectWriter() { - // Cleanup explicitly in order to avoid destructor stack overflow when input - // is deeply nested. - while (element_ != NULL) { - element_.reset(element_->pop()); - } if (own_typeinfo_) { delete typeinfo_; } + if (element_ == NULL) return; + // Cleanup explicitly in order to avoid destructor stack overflow when input + // is deeply nested. + // Cast to BaseElement to avoid doing additional checks (like missing fields) + // during pop(). + google::protobuf::scoped_ptr element( + static_cast(element_.get())->pop()); + while (element != NULL) { + element.reset(element->pop()); + } } namespace { @@ -454,7 +459,7 @@ void ProtoStreamObjectWriter::AnyWriter::WriteAny() { } ProtoStreamObjectWriter::ProtoElement::ProtoElement( - TypeInfo* typeinfo, const google::protobuf::Type& type, + const TypeInfo* typeinfo, const google::protobuf::Type& type, ProtoStreamObjectWriter* enclosing) : BaseElement(NULL), ow_(enclosing), @@ -586,6 +591,14 @@ string ProtoStreamObjectWriter::ProtoElement::ToString() const { return loc.empty() ? "." : loc; } +bool ProtoStreamObjectWriter::ProtoElement::OneofIndexTaken(int32 index) { + return ContainsKey(oneof_indices_, index); +} + +void ProtoStreamObjectWriter::ProtoElement::TakeOneofIndex(int32 index) { + InsertIfNotPresent(&oneof_indices_, index); +} + inline void ProtoStreamObjectWriter::InvalidName(StringPiece unknown_name, StringPiece message) { listener_->InvalidName(location(), ToSnakeCase(unknown_name), message); @@ -655,6 +668,13 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartObject( return this; } + // Check to see if this field is a oneof and that no oneof in that group has + // already been set. + if (!ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + if (field->type_url() == GetFullTypeWithUrl(kStructType)) { // Start a struct object. StartStruct(field); @@ -932,6 +952,14 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::StartList(StringPiece name) { // Also we ignore if the field is not found here as it is caught later. field = typeinfo_->FindField(&element_->type(), name); + // Only check for oneof collisions on the first StartList call. We identify + // the first call with !name.empty() check. Subsequent list element calls + // will not have the name filled. + if (!name.empty() && field && !ValidOneof(*field, name)) { + ++invalid_depth_; + return this; + } + // It is an error to try to bind to map, which behind the scenes is a list. if (field && IsMap(*field)) { // Push field to stack for error location tracking & reporting. @@ -1080,9 +1108,9 @@ Status ProtoStreamObjectWriter::RenderFieldMask(ProtoStreamObjectWriter* ow, data.ValueAsStringOrDefault(""))); } - // TODO(tsun): figure out how to do proto descriptor based snake case - // conversions as much as possible. Because ToSnakeCase sometimes returns the - // wrong value. +// TODO(tsun): figure out how to do proto descriptor based snake case +// conversions as much as possible. Because ToSnakeCase sometimes returns the +// wrong value. google::protobuf::scoped_ptr > callback( NewPermanentCallback(&RenderOneFieldPath, ow)); return DecodeCompactFieldMaskPaths(data.str(), callback.get()); @@ -1154,6 +1182,7 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( const google::protobuf::Field* field = NULL; string type_url; bool is_map_entry = false; + // We are at the root when element_ == NULL. if (element_ == NULL) { type_url = GetFullTypeWithUrl(master_type_.name()); } else { @@ -1166,6 +1195,11 @@ ProtoStreamObjectWriter* ProtoStreamObjectWriter::RenderDataPiece( if (field == NULL) { return this; } + + // Check to see if this field is a oneof and that no oneof in that group has + // already been set. + if (!ValidOneof(*field, name)) return this; + type_url = field->type_url(); } @@ -1314,7 +1348,8 @@ void ProtoStreamObjectWriter::RenderSimpleDataPiece( } case google::protobuf::Field_Kind_TYPE_ENUM: { status = WriteEnum(field.number(), data, - typeinfo_->GetEnum(field.type_url()), stream_.get()); + typeinfo_->GetEnumByTypeUrl(field.type_url()), + stream_.get()); break; } default: // TYPE_GROUP or TYPE_MESSAGE @@ -1401,6 +1436,24 @@ ProtoStreamObjectWriter::GetElementType(const google::protobuf::Type& type) { } } +bool ProtoStreamObjectWriter::ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name) { + if (element_ == NULL) return true; + + if (field.oneof_index() > 0) { + if (element_->OneofIndexTaken(field.oneof_index())) { + InvalidValue( + "oneof", + StrCat("oneof field '", + element_->type().oneofs(field.oneof_index() - 1), + "' is already set. Cannot set '", unnormalized_name, "'")); + return false; + } + element_->TakeOneofIndex(field.oneof_index()); + } + return true; +} + const google::protobuf::Field* ProtoStreamObjectWriter::BeginNamed( StringPiece name, bool is_list) { if (invalid_depth_ > 0) { @@ -1450,7 +1503,7 @@ const google::protobuf::Field* ProtoStreamObjectWriter::Lookup( const google::protobuf::Type* ProtoStreamObjectWriter::LookupType( const google::protobuf::Field* field) { return (field->kind() == google::protobuf::Field_Kind_TYPE_MESSAGE - ? typeinfo_->GetType(field->type_url()) + ? typeinfo_->GetTypeByTypeUrl(field->type_url()) : &element_->type()); } @@ -1539,7 +1592,7 @@ bool ProtoStreamObjectWriter::IsMap(const google::protobuf::Field& field) { return false; } const google::protobuf::Type* field_type = - typeinfo_->GetType(field.type_url()); + typeinfo_->GetTypeByTypeUrl(field.type_url()); return GetBoolOptionOrDefault(field_type->options(), "google.protobuf.MessageOptions.map_entry", false); diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.h b/src/google/protobuf/util/internal/protostream_objectwriter.h index eb4a59f917..f11c47c0a5 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.h +++ b/src/google/protobuf/util/internal/protostream_objectwriter.h @@ -71,7 +71,7 @@ class ObjectLocationTracker; // It also supports streaming. class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter { public: - // Constructor. Does not take ownership of any parameter passed in. +// Constructor. Does not take ownership of any parameter passed in. ProtoStreamObjectWriter(TypeResolver* type_resolver, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener); @@ -82,20 +82,17 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter virtual ProtoStreamObjectWriter* EndObject(); virtual ProtoStreamObjectWriter* StartList(StringPiece name); virtual ProtoStreamObjectWriter* EndList(); - virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, - bool value) { + virtual ProtoStreamObjectWriter* RenderBool(StringPiece name, bool value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, - int32 value) { + virtual ProtoStreamObjectWriter* RenderInt32(StringPiece name, int32 value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderUint32(StringPiece name, uint32 value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, - int64 value) { + virtual ProtoStreamObjectWriter* RenderInt64(StringPiece name, int64 value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderUint64(StringPiece name, @@ -106,8 +103,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter double value) { return RenderDataPiece(name, DataPiece(value)); } - virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, - float value) { + virtual ProtoStreamObjectWriter* RenderFloat(StringPiece name, float value) { return RenderDataPiece(name, DataPiece(value)); } virtual ProtoStreamObjectWriter* RenderString(StringPiece name, @@ -217,7 +213,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter }; // Constructor for the root element. No parent nor field. - ProtoElement(TypeInfo* typeinfo, const google::protobuf::Type& type, + ProtoElement(const TypeInfo* typeinfo, const google::protobuf::Type& type, ProtoStreamObjectWriter* enclosing); // Constructor for a field of an element. @@ -256,6 +252,13 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter return static_cast(BaseElement::parent()); } + // Returns true if the index is already taken by a preceeding oneof input. + bool OneofIndexTaken(int32 index); + + // Marks the oneof 'index' as taken. Future inputs to this oneof will + // generate an error. + void TakeOneofIndex(int32 index); + private: // Used for access to variables of the enclosing instance of // ProtoStreamObjectWriter. @@ -269,7 +272,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter const google::protobuf::Field* field_; // TypeInfo to lookup types. - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Additional variables if this element is a message: // (Root element is always a message). @@ -289,6 +292,10 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter // The type of this element, see enum for permissible types. ElementType element_type_; + // Set of oneof indices already seen for the type_. Used to validate + // incoming messages so no more than one oneof is set. + hash_set oneof_indices_; + GOOGLE_DISALLOW_IMPLICIT_CONSTRUCTORS(ProtoElement); }; @@ -298,7 +305,7 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter int size; }; - ProtoStreamObjectWriter(TypeInfo* typeinfo, + ProtoStreamObjectWriter(const TypeInfo* typeinfo, const google::protobuf::Type& type, strings::ByteSink* output, ErrorListener* listener); @@ -407,11 +414,19 @@ class LIBPROTOBUF_EXPORT ProtoStreamObjectWriter : public StructuredObjectWriter static ProtoElement::ElementType GetElementType( const google::protobuf::Type& type); + // Returns true if the field for type_ can be set as a oneof. If field is not + // a oneof type, this function does nothing and returns true. + // If another field for this oneof is already set, this function returns + // false. It also calls the appropriate error callback. + // unnormalized_name is used for error string. + bool ValidOneof(const google::protobuf::Field& field, + StringPiece unnormalized_name); + // Variables for describing the structure of the input tree: // master_type_: descriptor for the whole protobuf message. // typeinfo_ : the TypeInfo object to lookup types. const google::protobuf::Type& master_type_; - TypeInfo* typeinfo_; + const TypeInfo* typeinfo_; // Whether we own the typeinfo_ object. bool own_typeinfo_; diff --git a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc index bd4f29f5ba..96e5ccfbf8 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter_test.cc @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ using ::testing::_; using ::testing::Args; using google::protobuf::testing::anys::AnyM; using google::protobuf::testing::anys::AnyOut; +using google::protobuf::testing::oneofs::OneOfsRequest; using google::protobuf::testing::FieldMaskTest; using google::protobuf::testing::maps::MapIn; using google::protobuf::testing::structs::StructType; @@ -143,7 +145,7 @@ class BaseProtoStreamObjectWriterTest void CheckOutput(const Message& expected) { CheckOutput(expected, -1); } const google::protobuf::Type* GetType(const Descriptor* descriptor) { - return helper_.GetTypeInfo()->GetType(GetTypeUrl(descriptor)); + return helper_.GetTypeInfo()->GetTypeByTypeUrl(GetTypeUrl(descriptor)); } testing::TypeInfoTestHelper helper_; @@ -854,11 +856,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError1) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Timestamp"), - StringPiece( - "Field 'ts', Illegal timestamp format; timestamps " - "must end with 'Z'"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Illegal timestamp format; timestamps " + "must end with 'Z'"))); ow_->StartObject("")->RenderString("ts", "")->EndObject(); CheckOutput(timestamp); @@ -883,11 +884,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidTimestampError3) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Timestamp"), - StringPiece( - "Field 'ts', Invalid time format, failed to parse nano " - "seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Timestamp"), + StringPiece("Field 'ts', Invalid time format, failed to parse nano " + "seconds"))); ow_->StartObject("") ->RenderString("ts", "1970-01-01T00:00:00.ABZ") @@ -919,11 +919,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError1) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Illegal duration format; duration must " - "end with 's'"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Illegal duration format; duration must " + "end with 's'"))); ow_->StartObject("")->RenderString("dur", "")->EndObject(); CheckOutput(duration); @@ -934,11 +933,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError2) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Invalid duration format, failed to parse " - "seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Invalid duration format, failed to parse " + "seconds"))); ow_->StartObject("")->RenderString("dur", "s")->EndObject(); CheckOutput(duration); @@ -949,11 +947,10 @@ TEST_P(ProtoStreamObjectWriterTimestampDurationTest, InvalidDurationError3) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.Duration"), - StringPiece( - "Field 'dur', Invalid duration format, failed to " - "parse nanos seconds"))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.Duration"), + StringPiece("Field 'dur', Invalid duration format, failed to " + "parse nanos seconds"))); ow_->StartObject("")->RenderString("dur", "123.DEFs")->EndObject(); CheckOutput(duration); @@ -1174,10 +1171,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, EmptyAnyFromEmptyObject) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1192,10 +1189,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails1) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1210,10 +1207,10 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails2) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) { AnyOut any; - EXPECT_CALL(listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Missing or invalid @type for any field in " + EXPECT_CALL( + listener_, + InvalidValue(_, StringPiece("Any"), + StringPiece("Missing or invalid @type for any field in " "google.protobuf.testing.anys.AnyOut"))); ow_->StartObject("") @@ -1227,13 +1224,12 @@ TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithoutTypeUrlFails3) { TEST_P(ProtoStreamObjectWriterAnyTest, AnyWithInvalidTypeUrlFails) { AnyOut any; - EXPECT_CALL( - listener_, - InvalidValue(_, StringPiece("Any"), - StringPiece( - "Invalid type URL, type URLs must be of the form " - "'type.googleapis.com/', got: " - "type.other.com/some.Type"))); + EXPECT_CALL(listener_, + InvalidValue( + _, StringPiece("Any"), + StringPiece("Invalid type URL, type URLs must be of the form " + "'type.googleapis.com/', got: " + "type.other.com/some.Type"))); ow_->StartObject("") ->StartObject("any") @@ -1401,11 +1397,10 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MaskUsingApiaryStyleShouldWork) { TEST_P(ProtoStreamObjectWriterFieldMaskTest, MoreCloseThanOpenParentheses) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask 'a(b,c))'. " - "Cannot find matching '(' for all ')'."))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), + StringPiece("Field 'single_mask', Invalid FieldMask 'a(b,c))'. " + "Cannot find matching '(' for all ')'."))); ow_->StartObject(""); ow_->RenderString("id", "1"); @@ -1448,12 +1443,11 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeAtTheEndOfAPathSegment) { EXPECT_CALL( listener_, - InvalidValue(_, - StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. " - "Map keys should be at the end of a path segment."))); + InvalidValue( + _, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"key1\"]a,path.to.map[\"key2\"]'. " + "Map keys should be at the end of a path segment."))); ow_->StartObject(""); ow_->RenderString("single_mask", @@ -1466,10 +1460,9 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustEnd) { listener_, InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"key1\"'. Map keys should be " - "represented as [\"some_key\"]."))); + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"key1\"'. Map keys should be " + "represented as [\"some_key\"]."))); ow_->StartObject(""); ow_->RenderString("single_mask", "path.to.map[\"key1\""); @@ -1481,10 +1474,9 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyMustBeEscapedCorrectly) { listener_, InvalidValue(_, StringPiece("type.googleapis.com/google.protobuf.FieldMask"), - StringPiece( - "Field 'single_mask', Invalid FieldMask " - "'path.to.map[\"ke\"y1\"]'. Map keys should be " - "represented as [\"some_key\"]."))); + StringPiece("Field 'single_mask', Invalid FieldMask " + "'path.to.map[\"ke\"y1\"]'. Map keys should be " + "represented as [\"some_key\"]."))); ow_->StartObject(""); ow_->RenderString("single_mask", "path.to.map[\"ke\"y1\"]"); @@ -1507,6 +1499,192 @@ TEST_P(ProtoStreamObjectWriterFieldMaskTest, MapKeyCanContainAnyChars) { CheckOutput(expected); } +class ProtoStreamObjectWriterOneOfsTest + : public BaseProtoStreamObjectWriterTest { + protected: + ProtoStreamObjectWriterOneOfsTest() { + vector descriptors; + descriptors.push_back(OneOfsRequest::descriptor()); + descriptors.push_back(google::protobuf::Struct::descriptor()); + ResetTypeInfo(descriptors); + } +}; + +INSTANTIATE_TEST_CASE_P(DifferentTypeInfoSourceTest, + ProtoStreamObjectWriterOneOfsTest, + ::testing::Values( + testing::USE_TYPE_RESOLVER)); + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForPrimitiveTypesTest) { + EXPECT_CALL( + listener_, + InvalidValue( + _, StringPiece("oneof"), + StringPiece( + "oneof field 'data' is already set. Cannot set 'intData'"))); + + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->RenderString("intData", "123"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForMessageTypesPrimitiveFirstTest) { + // Test for setting primitive oneof field first and then message field. + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'messageData'"))); + + // JSON: { "strData": "blah", "messageData": { "dataValue": 123 } } + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForMessageTypesMessageFirstTest) { + // Test for setting message oneof field first and then primitive field. + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'strData'"))); + + // JSON: { "messageData": { "dataValue": 123 }, "strData": "blah" } + ow_->StartObject(""); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->RenderString("strData", "blah"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructTypesPrimitiveFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'structData'"))); + + // JSON: { "strData": "blah", "structData": { "a": "b" } } + ow_->StartObject(""); + ow_->RenderString("strData", "blah"); + ow_->StartObject("structData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructTypesStructFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'strData'"))); + + // JSON: { "structData": { "a": "b" }, "strData": "blah" } + ow_->StartObject(""); + ow_->StartObject("structData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->RenderString("strData", "blah"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForStructValueTypesTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'valueData'"))); + + // JSON: { "messageData": { "dataValue": 123 }, "valueData": { "a": "b" } } + ow_->StartObject(""); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->StartObject("valueData"); + ow_->RenderString("a", "b"); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesPrimitiveFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'tsData'"))); + + // JSON: { "intData": 123, "tsData": "1970-01-02T01:00:00.000Z" } + ow_->StartObject(""); + ow_->RenderInt32("intData", 123); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesWktFirstTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'intData'"))); + + // JSON: { "tsData": "1970-01-02T01:00:00.000Z", "intData": 123 } + ow_->StartObject(""); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->RenderInt32("intData", 123); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForWellKnownTypesAndMessageTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'messageData'"))); + + // JSON: { "tsData": "1970-01-02T01:00:00.000Z", + // "messageData": { "dataValue": 123 } } + ow_->StartObject(""); + ow_->RenderString("tsData", "1970-01-02T01:00:00.000Z"); + ow_->StartObject("messageData"); + ow_->RenderInt32("dataValue", 123); + ow_->EndObject(); + ow_->EndObject(); +} + +TEST_P(ProtoStreamObjectWriterOneOfsTest, + MultipleOneofsFailForOneofWithinAnyTest) { + EXPECT_CALL(listener_, + InvalidValue(_, StringPiece("oneof"), + StringPiece("oneof field 'data' is already set. " + "Cannot set 'intData'"))); + + using google::protobuf::testing::oneofs::OneOfsRequest; + // JSON: + // { "anyData": + // { "@type": + // "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest", + // "strData": "blah", + // "intData": 123 + // } + // } + ow_->StartObject(""); + ow_->StartObject("anyData"); + ow_->RenderString( + "@type", + "type.googleapis.com/google.protobuf.testing.oneofs.OneOfsRequest"); + ow_->RenderString("strData", "blah"); + ow_->RenderInt32("intData", 123); + ow_->EndObject(); +} + } // namespace converter } // namespace util } // namespace protobuf diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter.h b/src/google/protobuf/util/internal/snake2camel_objectwriter.h index 1a32bc5624..9b4ab8a385 100644 --- a/src/google/protobuf/util/internal/snake2camel_objectwriter.h +++ b/src/google/protobuf/util/internal/snake2camel_objectwriter.h @@ -58,9 +58,7 @@ class Snake2CamelObjectWriter : public ObjectWriter { // ObjectWriter methods. virtual Snake2CamelObjectWriter* StartObject(StringPiece name) { - ow_->StartObject(ShouldNormalizeCase(name) - ? StringPiece(StringPiece(ToCamelCase(name))) - : name); + ow_->StartObject(name); return this; } @@ -70,8 +68,7 @@ class Snake2CamelObjectWriter : public ObjectWriter { } virtual Snake2CamelObjectWriter* StartList(StringPiece name) { - ow_->StartList(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) - : name); + ow_->StartList(name); return this; } @@ -81,76 +78,57 @@ class Snake2CamelObjectWriter : public ObjectWriter { } virtual Snake2CamelObjectWriter* RenderBool(StringPiece name, bool value) { - ow_->RenderBool( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderBool(name, value); return this; } virtual Snake2CamelObjectWriter* RenderInt32(StringPiece name, int32 value) { - ow_->RenderInt32( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderInt32(name, value); return this; } virtual Snake2CamelObjectWriter* RenderUint32(StringPiece name, uint32 value) { - ow_->RenderUint32( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderUint32(name, value); return this; } virtual Snake2CamelObjectWriter* RenderInt64(StringPiece name, int64 value) { - ow_->RenderInt64( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderInt64(name, value); return this; } virtual Snake2CamelObjectWriter* RenderUint64(StringPiece name, uint64 value) { - ow_->RenderUint64( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderUint64(name, value); return this; } virtual Snake2CamelObjectWriter* RenderDouble(StringPiece name, double value) { - ow_->RenderDouble( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderDouble(name, value); return this; } virtual Snake2CamelObjectWriter* RenderFloat(StringPiece name, float value) { - ow_->RenderFloat( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderFloat(name, value); return this; } virtual Snake2CamelObjectWriter* RenderString(StringPiece name, StringPiece value) { - ow_->RenderString( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderString(name, value); return this; } virtual Snake2CamelObjectWriter* RenderBytes(StringPiece name, StringPiece value) { - ow_->RenderBytes( - ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) : name, - value); + ow_->RenderBytes(name, value); return this; } virtual Snake2CamelObjectWriter* RenderNull(StringPiece name) { - ow_->RenderNull(ShouldNormalizeCase(name) ? StringPiece(ToCamelCase(name)) - : name); + ow_->RenderNull(name); return this; } diff --git a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc index 67388c3b57..e5db844c57 100644 --- a/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc +++ b/src/google/protobuf/util/internal/snake2camel_objectwriter_test.cc @@ -47,263 +47,9 @@ class Snake2CamelObjectWriterTest : public ::testing::Test { Snake2CamelObjectWriter testing_; }; -TEST_F(Snake2CamelObjectWriterTest, Empty) { - // Set expectation - expects_.StartObject("")->EndObject(); - - // Actual testing - testing_.StartObject("")->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, UnderscoresOnly) { - // Set expectation - expects_.StartObject("") - ->RenderInt32("", 1) - ->RenderInt32("", 2) - ->RenderInt32("", 3) - ->RenderInt32("", 4) - ->RenderInt32("", 5) - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderInt32("_", 1) - ->RenderInt32("__", 2) - ->RenderInt32("___", 3) - ->RenderInt32("____", 4) - ->RenderInt32("_____", 5) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LowercaseOnly) { - // Set expectation - expects_.StartObject("") - ->RenderString("key", "value") - ->RenderString("abracadabra", "magic") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("key", "value") - ->RenderString("abracadabra", "magic") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, UppercaseOnly) { - // Set expectation - expects_.StartObject("") - ->RenderString("key", "VALUE") - ->RenderString("abracadabra", "MAGIC") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("KEY", "VALUE") - ->RenderString("ABRACADABRA", "MAGIC") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, CamelCase) { - // Set expectation - expects_.StartObject("") - ->RenderString("camelCase", "camelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "theQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("camelCase", "camelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "theQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, FirstCapCamelCase) { - // Sets expectation - expects_.StartObject("camel") - ->RenderString("camelCase", "CamelCase") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "TheQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); - - // Actual testing - testing_.StartObject("Camel") - ->RenderString("CamelCase", "CamelCase") - ->RenderString("TheQuickBrownFoxJumpsOverTheLazyDog", - "TheQuickBrownFoxJumpsOverTheLazyDog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LastCapCamelCase) { - // Sets expectation - expects_.StartObject("lastCapCamelCasE")->EndObject(); - - // Actual testing - testing_.StartObject("lastCapCamelCasE")->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, MixedCapCamelCase) { - // Sets expectation - expects_.StartObject("googleIsTheBest") - ->RenderFloat("iLoveGOOGLE", 1.61803f) - ->RenderFloat("goGoogleGO", 2.71828f) - ->RenderFloat("gBikeISCool", 3.14159f) - ->EndObject(); - - // Actual testing - testing_.StartObject("GOOGLEIsTheBest") - ->RenderFloat("ILoveGOOGLE", 1.61803f) - ->RenderFloat("GOGoogleGO", 2.71828f) - ->RenderFloat("GBikeISCool", 3.14159f) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, MixedCase) { - // Sets expectation - expects_.StartObject("snakeCaseCamelCase") - ->RenderBool("camelCaseSnakeCase", false) - ->RenderBool("mixedCamelAndUnderScores", false) - ->RenderBool("goGOOGLEGo", true) - ->EndObject(); - - // Actual testing - testing_.StartObject("snake_case_camelCase") - ->RenderBool("camelCase_snake_case", false) - ->RenderBool("MixedCamel_And_UnderScores", false) - ->RenderBool("Go_GOOGLEGo", true) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, SnakeCase) { - // Sets expectation - expects_.StartObject("") - ->RenderString("snakeCase", "snake_case") - ->RenderString("theQuickBrownFoxJumpsOverTheLazyDog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("snake_case", "snake_case") - ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, FirstCapSnakeCase) { - // Sets expectation - expects_.StartObject("firstCapSnakeCase") - ->RenderBool("helloWorld", true) - ->EndObject(); - - // Actual testing - testing_.StartObject("First_Cap_Snake_Case") - ->RenderBool("Hello_World", true) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, AllCapSnakeCase) { - // Sets expectation - expects_.StartObject("allCAPSNAKECASE") - ->RenderDouble("nyseGOOGL", 600.0L) - ->RenderDouble("aBCDE", 1.0L) - ->RenderDouble("klMNOP", 2.0L) - ->RenderDouble("abcIJKPQRXYZ", 3.0L) - ->EndObject(); - - // Actual testing - testing_.StartObject("ALL_CAP_SNAKE_CASE") - ->RenderDouble("NYSE_GOOGL", 600.0L) - ->RenderDouble("A_B_C_D_E", 1.0L) - ->RenderDouble("KL_MN_OP", 2.0L) - ->RenderDouble("ABC_IJK_PQR_XYZ", 3.0L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, RepeatedUnderScoreSnakeCase) { - // Sets expectation - expects_.StartObject("") - ->RenderInt32("doubleUnderscoreSnakeCase", 2) - ->RenderInt32("tripleUnderscoreFirstCap", 3) - ->RenderInt32("quadrupleUNDERSCOREALLCAP", 4) - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderInt32("double__underscore__snake__case", 2) - ->RenderInt32("Triple___Underscore___First___Cap", 3) - ->RenderInt32("QUADRUPLE____UNDERSCORE____ALL____CAP", 4) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, LeadingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("leadingUnderscoreSnakeCase") - ->RenderUint32("leadingDoubleUnderscore", 2) - ->RenderUint32("leadingTripleUnderscoreFirstCap", 3) - ->RenderUint32("leadingQUADRUPLEUNDERSCOREALLCAP", 4) - ->EndObject(); - - // Actual testing - testing_.StartObject("_leading_underscore_snake_case") - ->RenderUint32("__leading_double_underscore", 2) - ->RenderUint32("___Leading_Triple_Underscore_First_Cap", 3) - ->RenderUint32("____LEADING_QUADRUPLE_UNDERSCORE_ALL_CAP", 4) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, TrailingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("trailingUnderscoreSnakeCase") - ->RenderInt64("trailingDoubleUnderscore", 2L) - ->RenderInt64("trailingTripleUnderscoreFirstCap", 3L) - ->RenderInt64("trailingQUADRUPLEUNDERSCOREALLCAP", 4L) - ->EndObject(); - - // Actual testing - testing_.StartObject("trailing_underscore_snake_case") - ->RenderInt64("trailing_double_underscore__", 2L) - ->RenderInt64("Trailing_Triple_Underscore_First_Cap___", 3L) - ->RenderInt64("TRAILING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, EnclosingUnderscoreSnakeCase) { - // Sets expectation - expects_.StartObject("enclosingUnderscoreSnakeCase") - ->RenderUint64("enclosingDoubleUnderscore", 2L) - ->RenderUint64("enclosingTripleUnderscoreFirstCap", 3L) - ->RenderUint64("enclosingQUADRUPLEUNDERSCOREALLCAP", 4L) - ->EndObject(); - - // Actual testing - testing_.StartObject("_enclosing_underscore_snake_case_") - ->RenderUint64("__enclosing_double_underscore__", 2L) - ->RenderUint64("___Enclosing_Triple_Underscore_First_Cap___", 3L) - ->RenderUint64("____ENCLOSING_QUADRUPLE_UNDERSCORE_ALL_CAP____", 4L) - ->EndObject(); -} - -TEST_F(Snake2CamelObjectWriterTest, DisableCaseNormalizationOnlyDisablesFirst) { - // Sets expectation - expects_.StartObject("") - ->RenderString("snakeCase", "snake_case") - ->RenderString( - "the_quick_brown_fox_jumps_over_the_lazy_dog", // case retained - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->RenderBool("theSlowFox", true) // disable case not in effect - ->EndObject(); - - // Actual testing - testing_.StartObject("") - ->RenderString("snake_case", "snake_case") - ->DisableCaseNormalizationForNextKey() - ->RenderString("the_quick_brown_fox_jumps_over_the_lazy_dog", - "the_quick_brown_fox_jumps_over_the_lazy_dog") - ->RenderBool("the_slow_fox", true) - ->EndObject(); -} +// All tests are deleted as they are no longer needed. This file will be removed +// after the component dependecies are cleaned up. +// TODO(skarvaje): Remove this file. } // namespace converter } // namespace util diff --git a/src/google/protobuf/util/internal/testdata/default_value.proto b/src/google/protobuf/util/internal/testdata/default_value.proto index ecfc811908..ebbdf6ab20 100644 --- a/src/google/protobuf/util/internal/testdata/default_value.proto +++ b/src/google/protobuf/util/internal/testdata/default_value.proto @@ -43,6 +43,7 @@ message DefaultValueTestCases { DoubleMessage repeated_double = 4; DoubleMessage nested_message = 5; DoubleMessage repeated_nested_message = 6; + DoubleMessage double_message_with_oneof = 7; StructMessage empty_struct = 201; StructMessage empty_struct2 = 202; StructMessage struct_with_null_value = 203; @@ -75,6 +76,8 @@ message DefaultValueTestCases { MixedMap mixed1 = 404; MixedMap2 mixed2 = 405; MessageMap map_of_objects = 406; + MixedMap mixed_empty = 407; + MessageMap message_map_empty = 408; DoubleValueMessage double_value = 501; DoubleValueMessage double_value_default = 502; } @@ -85,6 +88,10 @@ message DoubleMessage { DoubleMessage nested_message = 3; repeated DoubleMessage repeated_nested_message = 4; google.protobuf.DoubleValue double_wrapper = 100; + oneof value { + string str_value = 112; + int64 num_value = 113; + } } message StructMessage { diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc index 6392e18c98..a45a76e37d 100644 --- a/src/google/protobuf/util/internal/type_info.cc +++ b/src/google/protobuf/util/internal/type_info.cc @@ -35,11 +35,11 @@ #include #include +#include #include #include #include #include -#include namespace google { namespace protobuf { @@ -47,7 +47,6 @@ namespace util { namespace converter { namespace { - // A TypeInfo that looks up information provided by a TypeResolver. class TypeInfoForTypeResolver : public TypeInfo { public: @@ -60,7 +59,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } virtual util::StatusOr ResolveTypeUrl( - StringPiece type_url) { + StringPiece type_url) const { map::iterator it = cached_types_.find(type_url); if (it != cached_types_.end()) { return it->second; @@ -78,12 +77,14 @@ class TypeInfoForTypeResolver : public TypeInfo { return result; } - virtual const google::protobuf::Type* GetType(StringPiece type_url) { + virtual const google::protobuf::Type* GetTypeByTypeUrl( + StringPiece type_url) const { StatusOrType result = ResolveTypeUrl(type_url); return result.ok() ? result.ValueOrDie() : NULL; } - virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) { + virtual const google::protobuf::Enum* GetEnumByTypeUrl( + StringPiece type_url) const { map::iterator it = cached_enums_.find(type_url); if (it != cached_enums_.end()) { return it->second.ok() ? it->second.ValueOrDie() : NULL; @@ -103,7 +104,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } virtual const google::protobuf::Field* FindField( - const google::protobuf::Type* type, StringPiece camel_case_name) { + const google::protobuf::Type* type, StringPiece camel_case_name) const { if (indexed_types_.find(type) == indexed_types_.end()) { PopulateNameLookupTable(type); indexed_types_.insert(type); @@ -131,7 +132,7 @@ class TypeInfoForTypeResolver : public TypeInfo { } } - void PopulateNameLookupTable(const google::protobuf::Type* type) { + void PopulateNameLookupTable(const google::protobuf::Type* type) const { for (int i = 0; i < type->fields_size(); ++i) { const google::protobuf::Field& field = type->fields(i); StringPiece name = field.name(); @@ -151,13 +152,13 @@ class TypeInfoForTypeResolver : public TypeInfo { // Stores string values that will be referenced by StringPieces in // cached_types_, cached_enums_ and camel_case_name_table_. - set string_storage_; + mutable set string_storage_; - map cached_types_; - map cached_enums_; + mutable map cached_types_; + mutable map cached_enums_; - set indexed_types_; - map camel_case_name_table_; + mutable set indexed_types_; + mutable map camel_case_name_table_; }; } // namespace diff --git a/src/google/protobuf/util/internal/type_info.h b/src/google/protobuf/util/internal/type_info.h index 67403fff6d..e394e8cfde 100644 --- a/src/google/protobuf/util/internal/type_info.h +++ b/src/google/protobuf/util/internal/type_info.h @@ -44,7 +44,7 @@ namespace util { namespace converter { // Internal helper class for type resolving. Note that this class is not // thread-safe and should only be accessed in one thread. -class LIBPROTOBUF_EXPORT TypeInfo { +class TypeInfo { public: TypeInfo() {} virtual ~TypeInfo() {} @@ -55,24 +55,29 @@ class LIBPROTOBUF_EXPORT TypeInfo { // // This TypeInfo class retains the ownership of the returned pointer. virtual util::StatusOr ResolveTypeUrl( - StringPiece type_url) = 0; + StringPiece type_url) const = 0; // Resolves a type url into a Type. Like ResolveTypeUrl() but returns // NULL if the type url is invalid or the type cannot be found. // // This TypeInfo class retains the ownership of the returned pointer. - virtual const google::protobuf::Type* GetType(StringPiece type_url) = 0; + virtual const google::protobuf::Type* GetTypeByTypeUrl( + StringPiece type_url) const = 0; // Resolves a type url for an enum. Returns NULL if the type url is // invalid or the type cannot be found. // // This TypeInfo class retains the ownership of the returned pointer. - virtual const google::protobuf::Enum* GetEnum(StringPiece type_url) = 0; + virtual const google::protobuf::Enum* GetEnumByTypeUrl( + StringPiece type_url) const = 0; // Looks up a field in the specified type given a CamelCase name. virtual const google::protobuf::Field* FindField( - const google::protobuf::Type* type, StringPiece camel_case_name) = 0; + const google::protobuf::Type* type, + StringPiece camel_case_name) const = 0; + // Creates a TypeInfo object that looks up type information from a + // TypeResolver. Caller takes ownership of the returned pointer. static TypeInfo* NewTypeInfo(TypeResolver* type_resolver); private: diff --git a/src/google/protobuf/util/internal/type_info_test_helper.cc b/src/google/protobuf/util/internal/type_info_test_helper.cc index 177b96e206..1b9c5154ed 100644 --- a/src/google/protobuf/util/internal/type_info_test_helper.cc +++ b/src/google/protobuf/util/internal/type_info_test_helper.cc @@ -36,6 +36,7 @@ #endif #include +#include #include #include #include @@ -89,7 +90,7 @@ TypeInfo* TypeInfoTestHelper::GetTypeInfo() { return typeinfo_.get(); } ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( io::CodedInputStream* coded_input, const string& type_url) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new ProtoStreamObjectSource(coded_input, type_resolver_.get(), @@ -103,7 +104,7 @@ ProtoStreamObjectSource* TypeInfoTestHelper::NewProtoSource( ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( const string& type_url, strings::ByteSink* output, ErrorListener* listener) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new ProtoStreamObjectWriter(type_resolver_.get(), *type, output, @@ -116,7 +117,7 @@ ProtoStreamObjectWriter* TypeInfoTestHelper::NewProtoWriter( DefaultValueObjectWriter* TypeInfoTestHelper::NewDefaultValueWriter( const string& type_url, ObjectWriter* writer) { - const google::protobuf::Type* type = typeinfo_->GetType(type_url); + const google::protobuf::Type* type = typeinfo_->GetTypeByTypeUrl(type_url); switch (type_) { case USE_TYPE_RESOLVER: { return new DefaultValueObjectWriter(type_resolver_.get(), *type, writer); diff --git a/src/google/protobuf/util/internal/utility.cc b/src/google/protobuf/util/internal/utility.cc index dfc4add292..9d80fa08af 100644 --- a/src/google/protobuf/util/internal/utility.cc +++ b/src/google/protobuf/util/internal/utility.cc @@ -30,7 +30,9 @@ #include +#include #include +#include #include #include #include diff --git a/src/google/protobuf/util/internal/utility.h b/src/google/protobuf/util/internal/utility.h index d0d88c1985..87f7602a9c 100644 --- a/src/google/protobuf/util/internal/utility.h +++ b/src/google/protobuf/util/internal/utility.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -117,23 +118,23 @@ LIBPROTOBUF_EXPORT const string GetFullTypeWithUrl(StringPiece simple_type); // Finds and returns option identified by name and option_name within the // provided map. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::Option* FindOptionOrNull( +const google::protobuf::Option* FindOptionOrNull( const google::protobuf::RepeatedPtrField& options, const string& option_name); // Finds and returns the field identified by field_name in the passed tech Type // object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::Field* FindFieldInTypeOrNull( +const google::protobuf::Field* FindFieldInTypeOrNull( const google::protobuf::Type* type, StringPiece field_name); // Finds and returns the EnumValue identified by enum_name in the passed tech // Enum object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNameOrNull( +const google::protobuf::EnumValue* FindEnumValueByNameOrNull( const google::protobuf::Enum* enum_type, StringPiece enum_name); // Finds and returns the EnumValue identified by value in the passed tech // Enum object. Returns NULL if none found. -LIBPROTOBUF_EXPORT const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( +const google::protobuf::EnumValue* FindEnumValueByNumberOrNull( const google::protobuf::Enum* enum_type, int32 value); // Converts input to camel-case and returns it. @@ -153,7 +154,7 @@ LIBPROTOBUF_EXPORT bool IsWellKnownType(const string& type_name); LIBPROTOBUF_EXPORT bool IsValidBoolString(const string& bool_string); // Returns true if "field" is a protobuf map field based on its type. -bool IsMap(const google::protobuf::Field& field, +LIBPROTOBUF_EXPORT bool IsMap(const google::protobuf::Field& field, const google::protobuf::Type& type); // Infinity/NaN-aware conversion to string. diff --git a/src/google/protobuf/util/json_util.h b/src/google/protobuf/util/json_util.h index 6796ea089f..614564cc1a 100644 --- a/src/google/protobuf/util/json_util.h +++ b/src/google/protobuf/util/json_util.h @@ -44,7 +44,7 @@ class ZeroCopyOutputStream; } // namespace io namespace util { -struct LIBPROTOBUF_EXPORT JsonOptions { +struct JsonOptions { // Whether to add spaces, line breaks and indentation to make the JSON output // easy to read. bool add_whitespace; @@ -65,7 +65,7 @@ struct LIBPROTOBUF_EXPORT JsonOptions { // 2. input is not valid protobuf wire format, or conflicts with the type // information returned by TypeResolver. // Note that unknown fields will be discarded silently. -LIBPROTOBUF_EXPORT util::Status BinaryToJsonStream( +util::Status BinaryToJsonStream( TypeResolver* resolver, const string& type_url, io::ZeroCopyInputStream* binary_input, @@ -80,7 +80,7 @@ inline util::Status BinaryToJsonStream( JsonOptions()); } -LIBPROTOBUF_EXPORT util::Status BinaryToJsonString( +util::Status BinaryToJsonString( TypeResolver* resolver, const string& type_url, const string& binary_input, @@ -101,13 +101,13 @@ inline util::Status BinaryToJsonString(TypeResolver* resolver, // 2. input is not valid JSON format, or conflicts with the type // information returned by TypeResolver. // 3. input has unknown fields. -LIBPROTOBUF_EXPORT util::Status JsonToBinaryStream( +util::Status JsonToBinaryStream( TypeResolver* resolver, const string& type_url, io::ZeroCopyInputStream* json_input, io::ZeroCopyOutputStream* binary_output); -LIBPROTOBUF_EXPORT util::Status JsonToBinaryString( +util::Status JsonToBinaryString( TypeResolver* resolver, const string& type_url, const string& json_input, diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc index 8399b4080b..7f88e6724d 100644 --- a/src/google/protobuf/util/json_util_test.cc +++ b/src/google/protobuf/util/json_util_test.cc @@ -98,27 +98,28 @@ TEST_F(JsonUtilTest, TestWhitespaces) { ToJson(m, options)); } -TEST_F(JsonUtilTest, TestDefaultValues) { - TestMessage m; - JsonOptions options; - EXPECT_EQ("{}", ToJson(m, options)); - options.always_print_primitive_fields = true; - EXPECT_EQ( - "{\"boolValue\":false," - "\"int32Value\":0," - "\"int64Value\":\"0\"," - "\"uint32Value\":0," - "\"uint64Value\":\"0\"," - "\"floatValue\":0," - "\"doubleValue\":0," - "\"stringValue\":\"\"," - "\"bytesValue\":\"\"," - // TODO(xiaofeng): The default enum value should be FOO. I believe - // this is a bug in DefaultValueObjectWriter. - "\"enumValue\":null" - "}", - ToJson(m, options)); -} +// TODO(skarvaje): Uncomment after cl/96232915 is submitted. +// TEST_F(JsonUtilTest, TestDefaultValues) { + // TestMessage m; + // JsonOptions options; + // EXPECT_EQ("{}", ToJson(m, options)); + // options.always_print_primitive_fields = true; + // EXPECT_EQ( + // "{\"boolValue\":false," + // "\"int32Value\":0," + // "\"int64Value\":\"0\"," + // "\"uint32Value\":0," + // "\"uint64Value\":\"0\"," + // "\"floatValue\":0," + // "\"doubleValue\":0," + // "\"stringValue\":\"\"," + // "\"bytesValue\":\"\"," + // // TODO(xiaofeng): The default enum value should be FOO. I believe + // // this is a bug in DefaultValueObjectWriter. + // "\"enumValue\":null" + // "}", + // ToJson(m, options)); +// } TEST_F(JsonUtilTest, ParseMessage) { // Some random message but good enough to verify that the parsing warpper diff --git a/src/google/protobuf/util/message_differencer.cc b/src/google/protobuf/util/message_differencer.cc index 057b414aa6..d709da57d8 100644 --- a/src/google/protobuf/util/message_differencer.cc +++ b/src/google/protobuf/util/message_differencer.cc @@ -45,6 +45,7 @@ #endif #include +#include #include #include #include diff --git a/src/google/protobuf/util/message_differencer_unittest.cc b/src/google/protobuf/util/message_differencer_unittest.cc index bd19f6958e..701b94ae10 100755 --- a/src/google/protobuf/util/message_differencer_unittest.cc +++ b/src/google/protobuf/util/message_differencer_unittest.cc @@ -52,6 +52,7 @@ #include #include +#include #include #include #include @@ -131,7 +132,7 @@ TEST(MessageDifferencerTest, MapFieldEqualityTest) { unittest::TestMap msg1; unittest::TestMap msg2; - MapTestUtil::MapReflectionTester tester(unittest::TestMap::descriptor()); + MapReflectionTester tester(unittest::TestMap::descriptor()); tester.SetMapFieldsViaReflection(&msg1); tester.SetMapFieldsViaReflection(&msg2); tester.SwapMapsViaReflection(&msg1); diff --git a/src/google/protobuf/util/type_resolver_util.cc b/src/google/protobuf/util/type_resolver_util.cc index 053a4ed77b..908634ebbd 100644 --- a/src/google/protobuf/util/type_resolver_util.cc +++ b/src/google/protobuf/util/type_resolver_util.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include namespace google { @@ -64,6 +65,47 @@ bool SplitTypeUrl(const string& type_url, return true; } +// This code is originally defined in +// //google/protobuf/util/converter/utility.h. Copied here due to component +// dependency. +// TODO(xiaofeng): Remove this when converter code is in components. +string ToCamelCase(const StringPiece input) { + bool capitalize_next = false; + bool was_cap = true; + bool is_cap = false; + bool first_word = true; + string result; + result.reserve(input.size()); + + for (size_t i = 0; i < input.size(); ++i, was_cap = is_cap) { + is_cap = ascii_isupper(input[i]); + if (input[i] == '_') { + capitalize_next = true; + if (!result.empty()) first_word = false; + continue; + } else if (first_word) { + // Consider when the current character B is capitalized, + // first word ends when: + // 1) following a lowercase: "...aB..." + // 2) followed by a lowercase: "...ABc..." + if (!result.empty() && is_cap && + (!was_cap || (i + 1 < input.size() && ascii_islower(input[i + 1])))) { + first_word = false; + } else { + result.push_back(ascii_tolower(input[i])); + continue; + } + } else if (capitalize_next) { + capitalize_next = false; + if (ascii_islower(input[i])) { + result.push_back(ascii_toupper(input[i])); + continue; + } + } + result.push_back(input[i]); + } + return result; +} class DescriptorPoolTypeResolver : public TypeResolver { public: @@ -155,6 +197,7 @@ class DescriptorPoolTypeResolver : public TypeResolver { } field->set_number(descriptor->number()); field->set_name(descriptor->name()); + field->set_json_name(ToCamelCase(descriptor->name())); if (descriptor->type() == FieldDescriptor::TYPE_MESSAGE) { field->set_type_url(GetTypeUrl(descriptor->message_type())); } else if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { diff --git a/src/google/protobuf/util/type_resolver_util.h b/src/google/protobuf/util/type_resolver_util.h index c0ef3c1af9..00cf9c1348 100644 --- a/src/google/protobuf/util/type_resolver_util.h +++ b/src/google/protobuf/util/type_resolver_util.h @@ -42,7 +42,7 @@ class TypeResolver; // Creates a TypeResolver that serves type information in the given descriptor // pool. Caller takes ownership of the returned TypeResolver. -LIBPROTOBUF_EXPORT TypeResolver* NewTypeResolverForDescriptorPool( +TypeResolver* NewTypeResolverForDescriptorPool( const string& url_prefix, const DescriptorPool* pool); } // namespace util diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc index c5db963b91..5ee4e25d49 100644 --- a/src/google/protobuf/wire_format.cc +++ b/src/google/protobuf/wire_format.cc @@ -38,6 +38,7 @@ #include +#include #include #include #include @@ -460,6 +461,10 @@ bool WireFormat::ParseAndMergeMessageSetField(uint32 field_number, } } +static bool StrictUtf8Check(const FieldDescriptor* field) { + return field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3; +} + bool WireFormat::ParseAndMergeField( uint32 tag, const FieldDescriptor* field, // May be NULL for unknown @@ -632,10 +637,19 @@ bool WireFormat::ParseAndMergeField( // Handle strings separately so that we can optimize the ctype=CORD case. case FieldDescriptor::TYPE_STRING: { + bool strict_utf8_check = StrictUtf8Check(field); string value; if (!WireFormatLite::ReadString(input, &value)) return false; - VerifyUTF8StringNamedField(value.data(), value.length(), PARSE, - field->name().c_str()); + if (strict_utf8_check) { + if (!WireFormatLite::VerifyUtf8String( + value.data(), value.length(), WireFormatLite::PARSE, + field->full_name().c_str())) { + return false; + } + } else { + VerifyUTF8StringNamedField(value.data(), value.length(), PARSE, + field->full_name().c_str()); + } if (field->is_repeated()) { message_reflection->AddString(message, field, value); } else { @@ -893,13 +907,20 @@ void WireFormat::SerializeFieldWithCachedSizes( // Handle strings separately so that we can get string references // instead of copying. case FieldDescriptor::TYPE_STRING: { + bool strict_utf8_check = StrictUtf8Check(field); string scratch; const string& value = field->is_repeated() ? message_reflection->GetRepeatedStringReference( message, field, j, &scratch) : message_reflection->GetStringReference(message, field, &scratch); - VerifyUTF8StringNamedField(value.data(), value.length(), SERIALIZE, - field->name().c_str()); + if (strict_utf8_check) { + WireFormatLite::VerifyUtf8String(value.data(), value.length(), + WireFormatLite::SERIALIZE, + field->full_name().c_str()); + } else { + VerifyUTF8StringNamedField(value.data(), value.length(), SERIALIZE, + field->full_name().c_str()); + } WireFormatLite::WriteString(field->number(), value, output); break; } @@ -1107,34 +1128,6 @@ int WireFormat::MessageSetItemByteSize( return our_size; } -void WireFormat::VerifyUTF8StringFallback(const char* data, - int size, - Operation op, - const char* field_name) { - if (!IsStructurallyValidUTF8(data, size)) { - const char* operation_str = NULL; - switch (op) { - case PARSE: - operation_str = "parsing"; - break; - case SERIALIZE: - operation_str = "serializing"; - break; - // no default case: have the compiler warn if a case is not covered. - } - string quoted_field_name = ""; - if (field_name != NULL) { - quoted_field_name = StringPrintf(" '%s'", field_name); - } - // no space below to avoid double space when the field name is missing. - GOOGLE_LOG(ERROR) << "String field" << quoted_field_name << " contains invalid " - << "UTF-8 data when " << operation_str << " a protocol " - << "buffer. Use the 'bytes' type if you intend to send raw " - << "bytes. "; - } -} - - } // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/wire_format.h b/src/google/protobuf/wire_format.h index 84270fee7e..941be75ba1 100644 --- a/src/google/protobuf/wire_format.h +++ b/src/google/protobuf/wire_format.h @@ -231,8 +231,8 @@ class LIBPROTOBUF_EXPORT WireFormat { const Message& message); enum Operation { - PARSE, - SERIALIZE, + PARSE = 0, + SERIALIZE = 1, }; // Verifies that a string field is valid UTF8, logging an error if not. @@ -247,13 +247,6 @@ class LIBPROTOBUF_EXPORT WireFormat { const char* field_name); private: - // Verifies that a string field is valid UTF8, logging an error if not. - static void VerifyUTF8StringFallback( - const char* data, - int size, - Operation op, - const char* field_name); - // Skip a MessageSet field. static bool SkipMessageSetField(io::CodedInputStream* input, uint32 field_number, @@ -265,8 +258,6 @@ class LIBPROTOBUF_EXPORT WireFormat { Message* message, io::CodedInputStream* input); - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormat); }; @@ -321,7 +312,8 @@ inline int WireFormat::TagSize(int field_number, FieldDescriptor::Type type) { inline void WireFormat::VerifyUTF8String(const char* data, int size, WireFormat::Operation op) { #ifdef GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED - WireFormat::VerifyUTF8StringFallback(data, size, op, NULL); + WireFormatLite::VerifyUtf8String( + data, size, static_cast(op), NULL); #else // Avoid the compiler warning about unsued variables. (void)data; (void)size; (void)op; @@ -332,7 +324,8 @@ inline void WireFormat::VerifyUTF8StringNamedField( const char* data, int size, WireFormat::Operation op, const char* field_name) { #ifdef GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED - WireFormat::VerifyUTF8StringFallback(data, size, op, field_name); + WireFormatLite::VerifyUtf8String( + data, size, static_cast(op), field_name); #endif } diff --git a/src/google/protobuf/wire_format_lite.cc b/src/google/protobuf/wire_format_lite.cc index 2ce4920c7e..847e35007a 100644 --- a/src/google/protobuf/wire_format_lite.cc +++ b/src/google/protobuf/wire_format_lite.cc @@ -37,11 +37,14 @@ #include #include #include +#include #include +#include #include #include #include + namespace google { namespace protobuf { namespace internal { @@ -484,9 +487,9 @@ void WireFormatLite::WriteMessageMaybeToArray(int field_number, } } -static inline bool ReadBytesToString(io::CodedInputStream* input, - string* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; -static inline bool ReadBytesToString(io::CodedInputStream* input, +GOOGLE_ATTRIBUTE_ALWAYS_INLINE static bool ReadBytesToString( + io::CodedInputStream* input, string* value); +inline static bool ReadBytesToString(io::CodedInputStream* input, string* value) { uint32 length; return input->ReadVarint32(&length) && @@ -504,6 +507,35 @@ bool WireFormatLite::ReadBytes(io::CodedInputStream* input, string** p) { return ReadBytesToString(input, *p); } +bool WireFormatLite::VerifyUtf8String(const char* data, + int size, + Operation op, + const char* field_name) { + if (!IsStructurallyValidUTF8(data, size)) { + const char* operation_str = NULL; + switch (op) { + case PARSE: + operation_str = "parsing"; + break; + case SERIALIZE: + operation_str = "serializing"; + break; + // no default case: have the compiler warn if a case is not covered. + } + string quoted_field_name = ""; + if (field_name != NULL) { + quoted_field_name = StringPrintf(" '%s'", field_name); + } + // no space below to avoid double space when the field name is missing. + GOOGLE_LOG(ERROR) << "String field" << quoted_field_name << " contains invalid " + << "UTF-8 data when " << operation_str << " a protocol " + << "buffer. Use the 'bytes' type if you intend to send raw " + << "bytes. "; + return false; + } + return true; +} + } // namespace internal } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h index ac83abdc0d..55fc7ecdfc 100644 --- a/src/google/protobuf/wire_format_lite.h +++ b/src/google/protobuf/wire_format_lite.h @@ -250,17 +250,17 @@ class LIBPROTOBUF_EXPORT WireFormatLite { // For primitive fields, we just use a templatized routine parameterized by // the represented type and the FieldType. These are specialized with the // appropriate definition for each declared type. - template - static inline bool ReadPrimitive(input, CType* value) INL; + template INL + static bool ReadPrimitive(input, CType* value); // Reads repeated primitive values, with optimizations for repeats. // tag_size and tag should both be compile-time constants provided by the // protocol compiler. - template - static inline bool ReadRepeatedPrimitive(int tag_size, - uint32 tag, - input, - RepeatedField* value) INL; + template INL + static bool ReadRepeatedPrimitive(int tag_size, + uint32 tag, + input, + RepeatedField* value); // Identical to ReadRepeatedPrimitive, except will not inline the // implementation. @@ -275,16 +275,14 @@ class LIBPROTOBUF_EXPORT WireFormatLite { // // This is only implemented for the types with fixed wire size, e.g. // float, double, and the (s)fixed* types. - template - static inline const uint8* ReadPrimitiveFromArray(const uint8* buffer, - CType* value) INL; + template INL + static const uint8* ReadPrimitiveFromArray(const uint8* buffer, CType* value); // Reads a primitive packed field. // // This is only implemented for packable types. - template - static inline bool ReadPackedPrimitive(input, - RepeatedField* value) INL; + template INL + static bool ReadPackedPrimitive(input, RepeatedField* value); // Identical to ReadPackedPrimitive, except will not inline the // implementation. @@ -318,6 +316,16 @@ class LIBPROTOBUF_EXPORT WireFormatLite { static bool ReadBytes(input, string** p); + enum Operation { + PARSE = 0, + SERIALIZE = 1, + }; + + // Returns true if the data is valid UTF-8. + static bool VerifyUtf8String(const char* data, int size, + Operation op, + const char* field_name); + static inline bool ReadGroup (field_number, input, MessageLite* value); static inline bool ReadMessage(input, MessageLite* value); @@ -344,23 +352,23 @@ class LIBPROTOBUF_EXPORT WireFormatLite { // Write a tag. The Write*() functions typically include the tag, so // normally there's no need to call this unless using the Write*NoTag() // variants. - static inline void WriteTag(field_number, WireType type, output) INL; + INL static void WriteTag(field_number, WireType type, output); // Write fields, without tags. - static inline void WriteInt32NoTag (int32 value, output) INL; - static inline void WriteInt64NoTag (int64 value, output) INL; - static inline void WriteUInt32NoTag (uint32 value, output) INL; - static inline void WriteUInt64NoTag (uint64 value, output) INL; - static inline void WriteSInt32NoTag (int32 value, output) INL; - static inline void WriteSInt64NoTag (int64 value, output) INL; - static inline void WriteFixed32NoTag (uint32 value, output) INL; - static inline void WriteFixed64NoTag (uint64 value, output) INL; - static inline void WriteSFixed32NoTag(int32 value, output) INL; - static inline void WriteSFixed64NoTag(int64 value, output) INL; - static inline void WriteFloatNoTag (float value, output) INL; - static inline void WriteDoubleNoTag (double value, output) INL; - static inline void WriteBoolNoTag (bool value, output) INL; - static inline void WriteEnumNoTag (int value, output) INL; + INL static void WriteInt32NoTag (int32 value, output); + INL static void WriteInt64NoTag (int64 value, output); + INL static void WriteUInt32NoTag (uint32 value, output); + INL static void WriteUInt64NoTag (uint64 value, output); + INL static void WriteSInt32NoTag (int32 value, output); + INL static void WriteSInt64NoTag (int64 value, output); + INL static void WriteFixed32NoTag (uint32 value, output); + INL static void WriteFixed64NoTag (uint64 value, output); + INL static void WriteSFixed32NoTag(int32 value, output); + INL static void WriteSFixed64NoTag(int64 value, output); + INL static void WriteFloatNoTag (float value, output); + INL static void WriteDoubleNoTag (double value, output); + INL static void WriteBoolNoTag (bool value, output); + INL static void WriteEnumNoTag (int value, output); // Write fields, including tags. static void WriteInt32 (field_number, int32 value, output); @@ -410,73 +418,59 @@ class LIBPROTOBUF_EXPORT WireFormatLite { #define output uint8* target // Like above, but use only *ToArray methods of CodedOutputStream. - static inline uint8* WriteTagToArray(field_number, WireType type, output) INL; + INL static uint8* WriteTagToArray(field_number, WireType type, output); // Write fields, without tags. - static inline uint8* WriteInt32NoTagToArray (int32 value, output) INL; - static inline uint8* WriteInt64NoTagToArray (int64 value, output) INL; - static inline uint8* WriteUInt32NoTagToArray (uint32 value, output) INL; - static inline uint8* WriteUInt64NoTagToArray (uint64 value, output) INL; - static inline uint8* WriteSInt32NoTagToArray (int32 value, output) INL; - static inline uint8* WriteSInt64NoTagToArray (int64 value, output) INL; - static inline uint8* WriteFixed32NoTagToArray (uint32 value, output) INL; - static inline uint8* WriteFixed64NoTagToArray (uint64 value, output) INL; - static inline uint8* WriteSFixed32NoTagToArray(int32 value, output) INL; - static inline uint8* WriteSFixed64NoTagToArray(int64 value, output) INL; - static inline uint8* WriteFloatNoTagToArray (float value, output) INL; - static inline uint8* WriteDoubleNoTagToArray (double value, output) INL; - static inline uint8* WriteBoolNoTagToArray (bool value, output) INL; - static inline uint8* WriteEnumNoTagToArray (int value, output) INL; + INL static uint8* WriteInt32NoTagToArray (int32 value, output); + INL static uint8* WriteInt64NoTagToArray (int64 value, output); + INL static uint8* WriteUInt32NoTagToArray (uint32 value, output); + INL static uint8* WriteUInt64NoTagToArray (uint64 value, output); + INL static uint8* WriteSInt32NoTagToArray (int32 value, output); + INL static uint8* WriteSInt64NoTagToArray (int64 value, output); + INL static uint8* WriteFixed32NoTagToArray (uint32 value, output); + INL static uint8* WriteFixed64NoTagToArray (uint64 value, output); + INL static uint8* WriteSFixed32NoTagToArray(int32 value, output); + INL static uint8* WriteSFixed64NoTagToArray(int64 value, output); + INL static uint8* WriteFloatNoTagToArray (float value, output); + INL static uint8* WriteDoubleNoTagToArray (double value, output); + INL static uint8* WriteBoolNoTagToArray (bool value, output); + INL static uint8* WriteEnumNoTagToArray (int value, output); // Write fields, including tags. - static inline uint8* WriteInt32ToArray( - field_number, int32 value, output) INL; - static inline uint8* WriteInt64ToArray( - field_number, int64 value, output) INL; - static inline uint8* WriteUInt32ToArray( - field_number, uint32 value, output) INL; - static inline uint8* WriteUInt64ToArray( - field_number, uint64 value, output) INL; - static inline uint8* WriteSInt32ToArray( - field_number, int32 value, output) INL; - static inline uint8* WriteSInt64ToArray( - field_number, int64 value, output) INL; - static inline uint8* WriteFixed32ToArray( - field_number, uint32 value, output) INL; - static inline uint8* WriteFixed64ToArray( - field_number, uint64 value, output) INL; - static inline uint8* WriteSFixed32ToArray( - field_number, int32 value, output) INL; - static inline uint8* WriteSFixed64ToArray( - field_number, int64 value, output) INL; - static inline uint8* WriteFloatToArray( - field_number, float value, output) INL; - static inline uint8* WriteDoubleToArray( - field_number, double value, output) INL; - static inline uint8* WriteBoolToArray( - field_number, bool value, output) INL; - static inline uint8* WriteEnumToArray( - field_number, int value, output) INL; - - static inline uint8* WriteStringToArray( - field_number, const string& value, output) INL; - static inline uint8* WriteBytesToArray( - field_number, const string& value, output) INL; - - static inline uint8* WriteGroupToArray( - field_number, const MessageLite& value, output) INL; - static inline uint8* WriteMessageToArray( - field_number, const MessageLite& value, output) INL; + INL static uint8* WriteInt32ToArray(field_number, int32 value, output); + INL static uint8* WriteInt64ToArray(field_number, int64 value, output); + INL static uint8* WriteUInt32ToArray(field_number, uint32 value, output); + INL static uint8* WriteUInt64ToArray(field_number, uint64 value, output); + INL static uint8* WriteSInt32ToArray(field_number, int32 value, output); + INL static uint8* WriteSInt64ToArray(field_number, int64 value, output); + INL static uint8* WriteFixed32ToArray(field_number, uint32 value, output); + INL static uint8* WriteFixed64ToArray(field_number, uint64 value, output); + INL static uint8* WriteSFixed32ToArray(field_number, int32 value, output); + INL static uint8* WriteSFixed64ToArray(field_number, int64 value, output); + INL static uint8* WriteFloatToArray(field_number, float value, output); + INL static uint8* WriteDoubleToArray(field_number, double value, output); + INL static uint8* WriteBoolToArray(field_number, bool value, output); + INL static uint8* WriteEnumToArray(field_number, int value, output); + + INL static uint8* WriteStringToArray( + field_number, const string& value, output); + INL static uint8* WriteBytesToArray( + field_number, const string& value, output); + + INL static uint8* WriteGroupToArray( + field_number, const MessageLite& value, output); + INL static uint8* WriteMessageToArray( + field_number, const MessageLite& value, output); // Like above, but de-virtualize the call to SerializeWithCachedSizes(). The // pointer must point at an instance of MessageType, *not* a subclass (or // the subclass must not override SerializeWithCachedSizes()). template - static inline uint8* WriteGroupNoVirtualToArray( - field_number, const MessageType& value, output) INL; + INL static uint8* WriteGroupNoVirtualToArray( + field_number, const MessageType& value, output); template - static inline uint8* WriteMessageNoVirtualToArray( - field_number, const MessageType& value, output) INL; + INL static uint8* WriteMessageNoVirtualToArray( + field_number, const MessageType& value, output); #undef output #undef input @@ -527,18 +521,17 @@ class LIBPROTOBUF_EXPORT WireFormatLite { // A helper method for the repeated primitive reader. This method has // optimizations for primitive types that have fixed size on the wire, and // can be read using potentially faster paths. - template - static inline bool ReadRepeatedFixedSizePrimitive( + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static bool ReadRepeatedFixedSizePrimitive( int tag_size, uint32 tag, google::protobuf::io::CodedInputStream* input, - RepeatedField* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + RepeatedField* value); // Like ReadRepeatedFixedSizePrimitive but for packed primitive fields. - template - static inline bool ReadPackedFixedSizePrimitive( - google::protobuf::io::CodedInputStream* input, - RepeatedField* value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + template GOOGLE_ATTRIBUTE_ALWAYS_INLINE + static bool ReadPackedFixedSizePrimitive(google::protobuf::io::CodedInputStream* input, + RepeatedField* value); static const CppType kFieldTypeToCppTypeMap[]; static const WireFormatLite::WireType kWireTypeForFieldType[]; diff --git a/src/google/protobuf/wire_format_lite_inl.h b/src/google/protobuf/wire_format_lite_inl.h index d073ff9232..991c3d041b 100644 --- a/src/google/protobuf/wire_format_lite_inl.h +++ b/src/google/protobuf/wire_format_lite_inl.h @@ -43,6 +43,7 @@ #include #include +#include #include #include #include diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc index aef22b2936..15c37556eb 100644 --- a/src/google/protobuf/wire_format_unittest.cc +++ b/src/google/protobuf/wire_format_unittest.cc @@ -40,8 +40,10 @@ #include #include #include +#include #include +#include #include #include #include @@ -419,7 +421,7 @@ const int kUnknownTypeId = 1550055; TEST(WireFormatTest, SerializeMessageSet) { // Set up a TestMessageSet with two known messages and an unknown one. - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; message_set.MutableExtension( unittest::TestMessageSetExtension1::message_set_extension)->set_i(123); message_set.MutableExtension( @@ -462,7 +464,7 @@ TEST(WireFormatTest, SerializeMessageSetVariousWaysAreEqual) { // Set up a TestMessageSet with two known messages and an unknown one, as // above. - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; message_set.MutableExtension( unittest::TestMessageSetExtension1::message_set_extension)->set_i(123); message_set.MutableExtension( @@ -539,7 +541,7 @@ TEST(WireFormatTest, ParseMessageSet) { ASSERT_TRUE(raw.SerializeToString(&data)); // Parse as a TestMessageSet and check the contents. - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; ASSERT_TRUE(message_set.ParseFromString(data)); EXPECT_EQ(123, message_set.GetExtension( @@ -553,7 +555,7 @@ TEST(WireFormatTest, ParseMessageSet) { EXPECT_EQ("bar", message_set.unknown_fields().field(0).length_delimited()); // Also parse using WireFormat. - unittest::TestMessageSet dynamic_message_set; + proto2_wireformat_unittest::TestMessageSet dynamic_message_set; io::CodedInputStream input(reinterpret_cast(data.data()), data.size()); ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &dynamic_message_set)); @@ -583,7 +585,7 @@ TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { coded_output.WriteTag(WireFormatLite::kMessageSetItemEndTag); } { - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; ASSERT_TRUE(message_set.ParseFromString(data)); EXPECT_EQ(123, message_set.GetExtension( @@ -591,7 +593,7 @@ TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { } { // Test parse the message via Reflection. - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; io::CodedInputStream input( reinterpret_cast(data.data()), data.size()); EXPECT_TRUE(WireFormat::ParseAndMergePartial(&input, &message_set)); @@ -603,7 +605,7 @@ TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { } TEST(WireFormatTest, ParseBrokenMessageSet) { - unittest::TestMessageSet message_set; + proto2_wireformat_unittest::TestMessageSet message_set; string input("goodbye"); // Invalid wire format data. EXPECT_FALSE(message_set.ParseFromString(input)); } diff --git a/update_file_lists.sh b/update_file_lists.sh index 206be4399c..bc2911d2ae 100755 --- a/update_file_lists.sh +++ b/update_file_lists.sh @@ -55,8 +55,10 @@ LITE_PROTOS=$(get_proto_files $MAKEFILE protoc_lite_outputs) PROTOS=$(get_proto_files $MAKEFILE protoc_outputs) WKT_PROTOS=$(get_variable_value $MAKEFILE nobase_dist_proto_DATA) COMMON_TEST_SOURCES=$(get_source_files $MAKEFILE COMMON_TEST_SOURCES) +COMMON_LITE_TEST_SOURCES=$(get_source_files $MAKEFILE COMMON_LITE_TEST_SOURCES) TEST_SOURCES=$(get_source_files $MAKEFILE protobuf_test_SOURCES) LITE_TEST_SOURCES=$(get_source_files $MAKEFILE protobuf_lite_test_SOURCES) +LITE_ARENA_TEST_SOURCES=$(get_source_files $MAKEFILE protobuf_lite_arena_test_SOURCES) TEST_PLUGIN_SOURCES=$(get_source_files $MAKEFILE test_plugin_SOURCES) ################################################################################ @@ -112,8 +114,10 @@ set_cmake_value $CMAKE_DIR/libprotoc.cmake libprotoc_files $CMAKE_PREFIX $LIBPRO set_cmake_value $CMAKE_DIR/tests.cmake lite_test_protos "" $LITE_PROTOS set_cmake_value $CMAKE_DIR/tests.cmake tests_protos "" $PROTOS set_cmake_value $CMAKE_DIR/tests.cmake common_test_files $CMAKE_PREFIX $COMMON_TEST_SOURCES +set_cmake_value $CMAKE_DIR/tests.cmake common_lite_test_files $CMAKE_PREFIX $COMMON_LITE_TEST_SOURCES set_cmake_value $CMAKE_DIR/tests.cmake tests_files $CMAKE_PREFIX $TEST_SOURCES set_cmake_value $CMAKE_DIR/tests.cmake lite_test_files $CMAKE_PREFIX $LITE_TEST_SOURCES +set_cmake_value $CMAKE_DIR/tests.cmake lite_arena_test_files $CMAKE_PREFIX $LITE_ARENA_TEST_SOURCES # Generate extract_includes.bat echo "mkdir include" > $EXTRACT_INCLUDES_BAT