diff options
author | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-19 17:14:29 -0800 |
---|---|---|
committer | Feng Xiao <xfxyjwf@gmail.com> | 2014-11-19 17:14:29 -0800 |
commit | 95c25918aa1550fcba50e1f76b9db6ebded25a80 (patch) | |
tree | 7a15bcee55b4dc9b85e84ec907ce8269b0ed1c02 | |
parent | 6a51460b985bc715b32d43b8e8f284e36b051f36 (diff) | |
parent | cd980d1c13c736b0f9fc453843f696a93c2c2a71 (diff) |
Merge nano proto into protobuf repository.
57 files changed, 15021 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk new file mode 100644 index 00000000..18bdd091 --- /dev/null +++ b/Android.mk @@ -0,0 +1,486 @@ +# Copyright (C) 2009 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# + +LOCAL_PATH := $(call my-dir) + +IGNORED_WARNINGS := -Wno-sign-compare -Wno-unused-parameter -Wno-sign-promo + +CC_LITE_SRC_FILES := \ + src/google/protobuf/stubs/common.cc \ + src/google/protobuf/stubs/once.cc \ + src/google/protobuf/stubs/hash.cc \ + src/google/protobuf/stubs/hash.h \ + src/google/protobuf/stubs/map-util.h \ + src/google/protobuf/stubs/stl_util-inl.h \ + src/google/protobuf/extension_set.cc \ + src/google/protobuf/generated_message_util.cc \ + src/google/protobuf/message_lite.cc \ + src/google/protobuf/repeated_field.cc \ + src/google/protobuf/wire_format_lite.cc \ + src/google/protobuf/io/coded_stream.cc \ + src/google/protobuf/io/coded_stream_inl.h \ + src/google/protobuf/io/zero_copy_stream.cc \ + src/google/protobuf/io/zero_copy_stream_impl_lite.cc + +JAVA_LITE_SRC_FILES := \ + java/src/main/java/com/google/protobuf/UninitializedMessageException.java \ + java/src/main/java/com/google/protobuf/MessageLite.java \ + java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ + java/src/main/java/com/google/protobuf/CodedOutputStream.java \ + java/src/main/java/com/google/protobuf/ByteString.java \ + java/src/main/java/com/google/protobuf/CodedInputStream.java \ + java/src/main/java/com/google/protobuf/ExtensionRegistryLite.java \ + java/src/main/java/com/google/protobuf/AbstractMessageLite.java \ + java/src/main/java/com/google/protobuf/FieldSet.java \ + java/src/main/java/com/google/protobuf/Internal.java \ + java/src/main/java/com/google/protobuf/WireFormat.java \ + java/src/main/java/com/google/protobuf/GeneratedMessageLite.java + +COMPILER_SRC_FILES := \ + src/google/protobuf/descriptor.cc \ + src/google/protobuf/descriptor.pb.cc \ + src/google/protobuf/descriptor_database.cc \ + src/google/protobuf/dynamic_message.cc \ + src/google/protobuf/extension_set.cc \ + src/google/protobuf/extension_set_heavy.cc \ + src/google/protobuf/generated_message_reflection.cc \ + src/google/protobuf/generated_message_util.cc \ + src/google/protobuf/message.cc \ + src/google/protobuf/message_lite.cc \ + src/google/protobuf/reflection_ops.cc \ + src/google/protobuf/repeated_field.cc \ + src/google/protobuf/service.cc \ + src/google/protobuf/text_format.cc \ + src/google/protobuf/unknown_field_set.cc \ + src/google/protobuf/wire_format.cc \ + src/google/protobuf/wire_format_lite.cc \ + src/google/protobuf/compiler/code_generator.cc \ + src/google/protobuf/compiler/command_line_interface.cc \ + src/google/protobuf/compiler/importer.cc \ + src/google/protobuf/compiler/main.cc \ + src/google/protobuf/compiler/parser.cc \ + src/google/protobuf/compiler/plugin.cc \ + src/google/protobuf/compiler/plugin.pb.cc \ + src/google/protobuf/compiler/subprocess.cc \ + src/google/protobuf/compiler/zip_writer.cc \ + src/google/protobuf/compiler/cpp/cpp_enum.cc \ + src/google/protobuf/compiler/cpp/cpp_enum_field.cc \ + src/google/protobuf/compiler/cpp/cpp_extension.cc \ + src/google/protobuf/compiler/cpp/cpp_field.cc \ + src/google/protobuf/compiler/cpp/cpp_file.cc \ + src/google/protobuf/compiler/cpp/cpp_generator.cc \ + src/google/protobuf/compiler/cpp/cpp_helpers.cc \ + src/google/protobuf/compiler/cpp/cpp_message.cc \ + src/google/protobuf/compiler/cpp/cpp_message_field.cc \ + src/google/protobuf/compiler/cpp/cpp_primitive_field.cc \ + src/google/protobuf/compiler/cpp/cpp_service.cc \ + src/google/protobuf/compiler/cpp/cpp_string_field.cc \ + src/google/protobuf/compiler/java/java_enum.cc \ + src/google/protobuf/compiler/java/java_enum_field.cc \ + src/google/protobuf/compiler/java/java_extension.cc \ + src/google/protobuf/compiler/java/java_field.cc \ + src/google/protobuf/compiler/java/java_file.cc \ + src/google/protobuf/compiler/java/java_generator.cc \ + src/google/protobuf/compiler/java/java_helpers.cc \ + src/google/protobuf/compiler/java/java_message.cc \ + src/google/protobuf/compiler/java/java_message_field.cc \ + src/google/protobuf/compiler/java/java_primitive_field.cc \ + src/google/protobuf/compiler/java/java_service.cc \ + src/google/protobuf/compiler/javamicro/javamicro_enum.cc \ + src/google/protobuf/compiler/javamicro/javamicro_enum_field.cc \ + src/google/protobuf/compiler/javamicro/javamicro_field.cc \ + src/google/protobuf/compiler/javamicro/javamicro_file.cc \ + src/google/protobuf/compiler/javamicro/javamicro_generator.cc \ + src/google/protobuf/compiler/javamicro/javamicro_helpers.cc \ + src/google/protobuf/compiler/javamicro/javamicro_message.cc \ + src/google/protobuf/compiler/javamicro/javamicro_message_field.cc \ + src/google/protobuf/compiler/javamicro/javamicro_primitive_field.cc \ + src/google/protobuf/compiler/javanano/javanano_enum.cc \ + src/google/protobuf/compiler/javanano/javanano_enum_field.cc \ + src/google/protobuf/compiler/javanano/javanano_extension.cc \ + src/google/protobuf/compiler/javanano/javanano_field.cc \ + src/google/protobuf/compiler/javanano/javanano_file.cc \ + src/google/protobuf/compiler/javanano/javanano_generator.cc \ + src/google/protobuf/compiler/javanano/javanano_helpers.cc \ + src/google/protobuf/compiler/javanano/javanano_message.cc \ + src/google/protobuf/compiler/javanano/javanano_message_field.cc \ + src/google/protobuf/compiler/javanano/javanano_primitive_field.cc \ + src/google/protobuf/compiler/python/python_generator.cc \ + src/google/protobuf/io/coded_stream.cc \ + src/google/protobuf/io/gzip_stream.cc \ + src/google/protobuf/io/printer.cc \ + src/google/protobuf/io/tokenizer.cc \ + src/google/protobuf/io/zero_copy_stream.cc \ + src/google/protobuf/io/zero_copy_stream_impl.cc \ + src/google/protobuf/io/zero_copy_stream_impl_lite.cc \ + src/google/protobuf/stubs/common.cc \ + src/google/protobuf/stubs/hash.cc \ + src/google/protobuf/stubs/once.cc \ + src/google/protobuf/stubs/structurally_valid.cc \ + src/google/protobuf/stubs/strutil.cc \ + src/google/protobuf/stubs/substitute.cc + +# Java nano library (for device-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-java-nano +LOCAL_MODULE_TAGS := optional +LOCAL_SDK_VERSION := 8 + +LOCAL_SRC_FILES := $(call all-java-files-under, java/src/main/java/com/google/protobuf/nano) +LOCAL_SRC_FILES += $(call all-java-files-under, java/src/device/main/java/com/google/protobuf/nano) + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Java nano library (for host-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := host-libprotobuf-java-nano +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, java/src/main/java/com/google/protobuf/nano) + +include $(BUILD_HOST_JAVA_LIBRARY) + +# Java micro library (for device-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-java-micro +LOCAL_MODULE_TAGS := optional +LOCAL_SDK_VERSION := 8 + +LOCAL_SRC_FILES := $(call all-java-files-under, java/src/main/java/com/google/protobuf/micro) + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Java micro library (for host-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := host-libprotobuf-java-micro +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(call all-java-files-under, java/src/main/java/com/google/protobuf/micro) + +include $(BUILD_HOST_JAVA_LIBRARY) + +# Java lite library (for device-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-java-lite +LOCAL_MODULE_TAGS := optional +LOCAL_SDK_VERSION := 8 + +LOCAL_SRC_FILES := $(JAVA_LITE_SRC_FILES) + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# Java lite library (for host-side users) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := host-libprotobuf-java-lite +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := $(JAVA_LITE_SRC_FILES) + +include $(BUILD_HOST_JAVA_LIBRARY) + +# C++ lite library +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-cpp-lite +LOCAL_MODULE_TAGS := optional + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := $(CC_LITE_SRC_FILES) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + $(LOCAL_PATH)/src + +# Define the header files to be copied +#LOCAL_COPY_HEADERS := \ +# src/google/protobuf/stubs/once.h \ +# src/google/protobuf/stubs/common.h \ +# src/google/protobuf/io/coded_stream.h \ +# src/google/protobuf/generated_message_util.h \ +# src/google/protobuf/repeated_field.h \ +# src/google/protobuf/extension_set.h \ +# src/google/protobuf/wire_format_lite_inl.h +# +#LOCAL_COPY_HEADERS_TO := $(LOCAL_MODULE) + +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) + +# These are the minimum versions and don't need to be update. +ifeq ($(TARGET_ARCH),arm) +LOCAL_SDK_VERSION := 8 +else +# x86/mips support only available from API 9. +LOCAL_SDK_VERSION := 9 +endif +LOCAL_NDK_STL_VARIANT := stlport_static + +include $(BUILD_STATIC_LIBRARY) + +# C++ lite library (libc++ flavored for the platform) +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-cpp-lite +LOCAL_MODULE_TAGS := optional + +LOCAL_CPP_EXTENSION := .cc + +LOCAL_SRC_FILES := $(CC_LITE_SRC_FILES) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + $(LOCAL_PATH)/src + +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) + +include $(BUILD_SHARED_LIBRARY) + +# C++ full library +# ======================================================= +protobuf_cc_full_src_files := \ + $(CC_LITE_SRC_FILES) \ + src/google/protobuf/stubs/strutil.cc \ + src/google/protobuf/stubs/strutil.h \ + src/google/protobuf/stubs/substitute.cc \ + src/google/protobuf/stubs/substitute.h \ + src/google/protobuf/stubs/structurally_valid.cc \ + src/google/protobuf/descriptor.cc \ + src/google/protobuf/descriptor.pb.cc \ + src/google/protobuf/descriptor_database.cc \ + src/google/protobuf/dynamic_message.cc \ + src/google/protobuf/extension_set_heavy.cc \ + src/google/protobuf/generated_message_reflection.cc \ + src/google/protobuf/message.cc \ + src/google/protobuf/reflection_ops.cc \ + src/google/protobuf/service.cc \ + src/google/protobuf/text_format.cc \ + src/google/protobuf/unknown_field_set.cc \ + src/google/protobuf/wire_format.cc \ + src/google/protobuf/io/gzip_stream.cc \ + src/google/protobuf/io/printer.cc \ + src/google/protobuf/io/tokenizer.cc \ + src/google/protobuf/io/zero_copy_stream_impl.cc \ + src/google/protobuf/compiler/importer.cc \ + src/google/protobuf/compiler/parser.cc + +# C++ full library - stlport version +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-cpp-full +LOCAL_MODULE_TAGS := optional +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := $(protobuf_cc_full_src_files) +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + external/zlib \ + $(LOCAL_PATH)/src + +# Define the header files to be copied +#LOCAL_COPY_HEADERS := \ +# src/google/protobuf/stubs/once.h \ +# src/google/protobuf/stubs/common.h \ +# src/google/protobuf/io/coded_stream.h \ +# src/google/protobuf/generated_message_util.h \ +# src/google/protobuf/repeated_field.h \ +# src/google/protobuf/extension_set.h \ +# src/google/protobuf/wire_format_lite_inl.h +# +#LOCAL_COPY_HEADERS_TO := $(LOCAL_MODULE) + +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) + +# These are the minimum versions and don't need to be update. +ifeq ($(TARGET_ARCH),arm) +LOCAL_SDK_VERSION := 8 +else +# x86/mips support only available from API 9. +LOCAL_SDK_VERSION := 9 +endif +LOCAL_NDK_STL_VARIANT := stlport_static + +include $(BUILD_STATIC_LIBRARY) + +# C++ full library - Gnustl+rtti version +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-cpp-full-gnustl-rtti +LOCAL_MODULE_TAGS := optional +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := $(protobuf_cc_full_src_files) +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + external/zlib \ + $(LOCAL_PATH)/src + +LOCAL_CFLAGS := -frtti $(IGNORED_WARNINGS) +LOCAL_SDK_VERSION := 14 +LOCAL_NDK_STL_VARIANT := gnustl_static + +include $(BUILD_STATIC_LIBRARY) + +# C++ full library - libc++ version for the platform +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := libprotobuf-cpp-full +LOCAL_MODULE_TAGS := optional +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := $(protobuf_cc_full_src_files) +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + external/zlib \ + $(LOCAL_PATH)/src + +LOCAL_CFLAGS := -DGOOGLE_PROTOBUF_NO_RTTI $(IGNORED_WARNINGS) +LOCAL_SHARED_LIBRARIES := libz + +include $(BUILD_SHARED_LIBRARY) + +# Clean temp vars +protobuf_cc_full_src_files := + + +# Android Protocol buffer compiler, aprotoc (host executable) +# used by the build systems as $(PROTOC) defined in +# build/core/config.mk +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := aprotoc +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_TAGS := optional + +LOCAL_CPP_EXTENSION := .cc +LOCAL_SRC_FILES := $(COMPILER_SRC_FILES) + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/android \ + external/zlib \ + $(LOCAL_PATH)/src + +LOCAL_STATIC_LIBRARIES += libz + +ifneq ($(HOST_OS),windows) +LOCAL_LDLIBS := -lpthread +endif + +LOCAL_CFLAGS := $(IGNORED_WARNINGS) + +include $(BUILD_HOST_EXECUTABLE) + +# To test java proto params build rules. +# ======================================================= +include $(CLEAR_VARS) + +LOCAL_MODULE := aprotoc-test-nano-params +LOCAL_MODULE_TAGS := tests +LOCAL_SDK_VERSION := current + +LOCAL_PROTOC_OPTIMIZE_TYPE := nano + +LOCAL_SRC_FILES := \ + src/google/protobuf/unittest_import_nano.proto \ + src/google/protobuf/unittest_simple_nano.proto \ + src/google/protobuf/unittest_stringutf8_nano.proto \ + src/google/protobuf/unittest_recursive_nano.proto + + +LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src + +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ + java_package = $(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|com.google.protobuf.nano, \ + java_outer_classname = $(LOCAL_PATH)/src/google/protobuf/unittest_import_nano.proto|UnittestImportNano + +include $(BUILD_STATIC_JAVA_LIBRARY) + +# To test Android-specific nanoproto features. +# ======================================================= +include $(CLEAR_VARS) + +# Parcelable messages +LOCAL_MODULE := android-nano-test-parcelable +LOCAL_MODULE_TAGS := tests +LOCAL_SDK_VERSION := current + +LOCAL_PROTOC_OPTIMIZE_TYPE := nano + +LOCAL_SRC_FILES := src/google/protobuf/unittest_simple_nano.proto + +LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src + +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ + parcelable_messages = true + +include $(BUILD_STATIC_JAVA_LIBRARY) + +include $(CLEAR_VARS) + +# Parcelable and extendable messages +LOCAL_MODULE := android-nano-test-parcelable-extendable +LOCAL_MODULE_TAGS := tests +LOCAL_SDK_VERSION := current + +LOCAL_PROTOC_OPTIMIZE_TYPE := nano + +LOCAL_SRC_FILES := src/google/protobuf/unittest_extension_nano.proto + +LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/src + +LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \ + parcelable_messages = true, \ + store_unknown_fields = true + +include $(BUILD_STATIC_JAVA_LIBRARY) + +include $(CLEAR_VARS) + +# Test APK +LOCAL_PACKAGE_NAME := NanoAndroidTest + +LOCAL_SDK_VERSION := 8 + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-java-files-under, java/src/device/test/java/com/google/protobuf/nano) + +LOCAL_MANIFEST_FILE := java/src/device/test/AndroidManifest.xml + +LOCAL_STATIC_JAVA_LIBRARIES := libprotobuf-java-nano \ + android-nano-test-parcelable \ + android-nano-test-parcelable-extendable + +LOCAL_DEX_PREOPT := false + +include $(BUILD_PACKAGE) + +# 2.3.0 prebuilts for backwards compatibility. +include $(LOCAL_PATH)/prebuilts/Android.mk diff --git a/javanano/README.txt b/javanano/README.txt new file mode 100644 index 00000000..fae32927 --- /dev/null +++ b/javanano/README.txt @@ -0,0 +1,354 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. + +This directory contains the Java Protocol Buffers Nano runtime library. + +Installation - With Maven +========================= + +The Protocol Buffers build is managed using Maven. If you would +rather build without Maven, see below. + +1) Install Apache Maven if you don't have it: + + http://maven.apache.org/ + +2) Build the C++ code, or obtain a binary distribution of protoc. If + you install a binary distribution, make sure that it is the same + version as this package. If in doubt, run: + + $ protoc --version + + You will need to place the protoc executable in ../src. (If you + built it yourself, it should already be there.) + +3) Run the tests: + + $ mvn test + + If some tests fail, this library may not work correctly on your + system. Continue at your own risk. + +4) Install the library into your Maven repository: + + $ mvn install + +5) If you do not use Maven to manage your own build, you can build a + .jar file to use: + + $ mvn package + + The .jar will be placed in the "target" directory. + +Installation - Without Maven +============================ + +If you would rather not install Maven to build the library, you may +follow these instructions instead. Note that these instructions skip +running unit tests. + +1) Build the C++ code, or obtain a binary distribution of protoc. If + you install a binary distribution, make sure that it is the same + version as this package. If in doubt, run: + + $ protoc --version + + If you built the C++ code without installing, the compiler binary + should be located in ../src. + +2) Invoke protoc to build DescriptorProtos.java: + + $ protoc --java_out=src/main/java -I../src \ + ../src/google/protobuf/descriptor.proto + +3) Compile the code in src/main/java using whatever means you prefer. + +4) Install the classes wherever you prefer. + +Nano version +============================ + +Nano is a special code generator and runtime library designed specially +for Android, and is very resource-friendly in both the amount of code +and the runtime overhead. An overview of Nano features: + +- No descriptors or message builders. +- All messages are mutable; fields are public Java fields. +- For optional fields only, encapsulation behind setter/getter/hazzer/ + clearer functions is opt-in, which provide proper 'has' state support. +- If not opted in, has state is not available. Serialization outputs + all fields not equal to their defaults (see important implications + below). +- Required fields are always serialized. +- Enum constants are integers; protection against invalid values only + when parsing from the wire. +- Enum constants can be generated into container interfaces bearing + the enum's name (so the referencing code is in Java style). +- CodedInputByteBufferNano can only take byte[] (not InputStream). +- Similarly CodedOutputByteBufferNano can only write to byte[]. +- Repeated fields are in arrays, not ArrayList or Vector. Null array + elements are allowed and silently ignored. +- Full support of serializing/deserializing repeated packed fields. +- Support of extensions. +- Unset messages/groups are null, not an immutable empty default + instance. +- toByteArray(...) and mergeFrom(...) are now static functions of + MessageNano. +- The 'bytes' type translates to the Java type byte[]. + +The generated messages are not thread-safe for writes, but may be +used simultaneously from multiple threads in a read-only manner. +In other words, an appropriate synchronization mechanism (such as +a ReadWriteLock) must be used to ensure that a message, its +ancestors, and descendants are not accessed by any other threads +while the message is being modified. Field reads, getter methods +(but not getExtension(...)), toByteArray(...), writeTo(...), +getCachedSize(), and getSerializedSize() are all considered read-only +operations. + +IMPORTANT: If you have fields with defaults and opt out of accessors + +How fields with defaults are serialized has changed. Because we don't +keep "has" state, any field equal to its default is assumed to be not +set and therefore is not serialized. Consider the situation where we +change the default value of a field. Senders compiled against an older +version of the proto continue to match against the old default, and +don't send values to the receiver even though the receiver assumes the +new default value. Therefore, think carefully about the implications +of changing the default value. Alternatively, turn on accessors and +enjoy the benefit of the explicit has() checks. + +IMPORTANT: If you have "bytes" fields with non-empty defaults + +Because the byte buffer is now of mutable type byte[], the default +static final cannot be exposed through a public field. Each time a +message's constructor or clear() function is called, the default value +(kept in a private byte[]) is cloned. This causes a small memory +penalty. This is not a problem if the field has no default or is an +empty default. + +Nano Generator options + +java_package -> <file-name>|<package-name> +java_outer_classname -> <file-name>|<package-name> +java_multiple_files -> true or false +java_nano_generate_has -> true or false [DEPRECATED] +optional_field_style -> default or accessors +enum_style -> c or java +ignore_services -> true or false +parcelable_messages -> true or false + +java_package=<file-name>|<package-name> (no default) + This allows overriding the 'java_package' option value + for the given file from the command line. Use multiple + java_package options to override the option for multiple + files. The final Java package for each file is the value + of this command line option if present, or the value of + the same option defined in the file if present, or the + proto package if present, or the default Java package. + +java_outer_classname=<file-name>|<outer-classname> (no default) + This allows overriding the 'java_outer_classname' option + for the given file from the command line. Use multiple + java_outer_classname options to override the option for + multiple files. The final Java outer class name for each + file is the value of this command line option if present, + or the value of the same option defined in the file if + present, or the file name converted to CamelCase. This + outer class will nest all classes and integer constants + generated from file-scope messages and enums. + +java_multiple_files={true,false} (no default) + This allows overriding the 'java_multiple_files' option + in all source files and their imported files from the + command line. The final value of this option for each + file is the value defined in this command line option, or + the value of the same option defined in the file if + present, or false. This specifies whether to generate + package-level classes for the file-scope messages in the + same Java package as the outer class (instead of nested + classes in the outer class). File-scope enum constants + are still generated as integer constants in the outer + class. This affects the fully qualified references in the + Java code. NOTE: because the command line option + overrides the value for all files and their imported + files, using this option inconsistently may result in + incorrect references to the imported messages and enum + constants. + +java_nano_generate_has={true,false} (default: false) + DEPRECATED. Use optional_field_style=accessors. + + If true, generates a public boolean variable has<fieldname> + accompanying each optional or required field (not present for + repeated fields, groups or messages). It is set to false initially + and upon clear(). If parseFrom(...) reads the field from the wire, + it is set to true. This is a way for clients to inspect the "has" + value upon parse. If it is set to true, writeTo(...) will ALWAYS + output that field (even if field value is equal to its + default). + + IMPORTANT: This option costs an extra 4 bytes per primitive field in + the message. Think carefully about whether you really need this. In + many cases reading the default works and determining whether the + field was received over the wire is irrelevant. + +optional_field_style={default,accessors,reftypes} (default: default) + Defines the style of the generated code for fields. + + * default * + + In the default style, optional fields translate into public mutable + Java fields, and the serialization process is as discussed in the + "IMPORTANT" section above. + + * accessors * + + When set to 'accessors', each optional field is encapsulated behind + 4 accessors, namely get<fieldname>(), set<fieldname>(), has<fieldname>() + and clear<fieldname>() methods, with the standard semantics. The hazzer's + return value determines whether a field is serialized, so this style is + useful when you need to serialize a field with the default value, or check + if a field has been explicitly set to its default value from the wire. + + In the 'accessors' style, required and nested message fields are still + translated to one public mutable Java field each, repeated fields are still + translated to arrays. No accessors are generated for them. + + IMPORTANT: When using the 'accessors' style, ProGuard should always + be enabled with optimization (don't use -dontoptimize) and allowing + access modification (use -allowaccessmodification). This removes the + unused accessors and maybe inline the rest at the call sites, + reducing the final code size. + TODO(maxtroy): find ProGuard config that would work the best. + + * reftypes * + + When set to 'reftypes', each proto field is generated as a public Java + field. For primitive types, these fields use the Java reference types + such as java.lang.Integer instead of primitive types such as int. + + In the 'reftypes' style, fields are initialized to null (or empty + arrays for repeated fields), and their default values are not available. + They are serialized over the wire based on equality to null. + + The 'reftypes' mode has some additional cost due to autoboxing and usage + of reference types. In practice, many boxed types are cached, and so don't + result in object creation. However, references do take slightly more memory + than primitives. + + The 'reftypes' mode is useful when you want to be able to serialize fields + with default values, or check if a field has been explicitly set to the + default over the wire without paying the extra method cost of the + 'accessors' mode. + + Note that if you attempt to write null to a required field in the reftypes + mode, serialization of the proto will cause a NullPointerException. This is + an intentional indicator that you must set required fields. + + NOTE + optional_field_style=accessors or reftypes cannot be used together with + java_nano_generate_has=true. If you need the 'has' flag for any + required field (you have no reason to), you can only use + java_nano_generate_has=true. + +enum_style={c,java} (default: c) + Defines where to put the int constants generated from enum members. + + * c * + + Use C-style, so the enum constants are available at the scope where + the enum is defined. A file-scope enum's members are referenced like + 'FileOuterClass.ENUM_VALUE'; a message-scope enum's members are + referenced as 'Message.ENUM_VALUE'. The enum name is unavailable. + This complies with the Micro code generator's behavior. + + * java * + + Use Java-style, so the enum constants are available under the enum + name and referenced like 'EnumName.ENUM_VALUE' (they are still int + constants). The enum name becomes the name of a public interface, at + the scope where the enum is defined. If the enum is file-scope and + the java_multiple_files option is on, the interface will be defined + in its own file. To reduce code size, this interface should not be + implemented and ProGuard shrinking should be used, so after the Java + compiler inlines all referenced enum constants into the call sites, + the interface remains unused and can be removed by ProGuard. + +ignore_services={true,false} (default: false) + Skips services definitions. + + Nano doesn't support services. By default, if a service is defined + it will generate a compilation error. If this flag is set to true, + services will be silently ignored, instead. + +parcelable_messages={true,false} (default: false) + Android-specific option to generate Parcelable messages. + + +To use nano protobufs within the Android repo: + +- Set 'LOCAL_PROTOC_OPTIMIZE_TYPE := nano' in your local .mk file. + When building a Java library or an app (package) target, the build + system will add the Java nano runtime library to the + LOCAL_STATIC_JAVA_LIBRARIES variable, so you don't need to. +- Set 'LOCAL_PROTO_JAVA_OUTPUT_PARAMS := ...' in your local .mk file + for any command-line options you need. Use commas to join multiple + options. In the nano flavor only, whitespace surrounding the option + names and values are ignored, so you can use backslash-newline or + '+=' to structure your make files nicely. +- The options will be applied to *all* proto files in LOCAL_SRC_FILES + when you build a Java library or package. In case different options + are needed for different proto files, build separate Java libraries + and reference them in your main target. Note: you should make sure + that, for each separate target, all proto files imported from any + proto file in LOCAL_SRC_FILES are included in LOCAL_SRC_FILES. This + is because the generator has to assume that the imported files are + built using the same options, and will generate code that reference + the fields and enums from the imported files using the same code + style. +- Hint: 'include $(CLEAR_VARS)' resets all LOCAL_ variables, including + the two above. + +To use nano protobufs outside of Android repo: + +- Link with the generated jar file + <protobuf-root>java/target/protobuf-java-2.3.0-nano.jar. +- Invoke with --javanano_out, e.g.: + +./protoc '--javanano_out=\ + java_package=src/proto/simple-data.proto|my_package,\ + java_outer_classname=src/proto/simple-data.proto|OuterName\ + :.' src/proto/simple-data.proto + +Contributing to nano: + +Please add/edit tests in NanoTest.java. + +Please run the following steps to test: + +- cd external/protobuf +- ./configure +- Run "make -j12 check" and verify all tests pass. +- cd java +- Run "mvn test" and verify all tests pass. +- cd ../../.. +- . build/envsetup.sh +- lunch 1 +- "make -j12 aprotoc libprotobuf-java-2.3.0-nano aprotoc-test-nano-params NanoAndroidTest" and + check for build errors. +- Plug in an Android device or start an emulator. +- adb install -r out/target/product/generic/data/app/NanoAndroidTest.apk +- Run: + "adb shell am instrument -w com.google.protobuf.nano.test/android.test.InstrumentationTestRunner" + and verify all tests pass. +- repo sync -c -j256 +- "make -j12" and check for build errors + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers://developers.google.com/protocol-buffers/ diff --git a/javanano/pom.xml b/javanano/pom.xml new file mode 100644 index 00000000..3d98a5e0 --- /dev/null +++ b/javanano/pom.xml @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>com.google</groupId> + <artifactId>google</artifactId> + <version>1</version> + </parent> + <groupId>com.google.protobuf.nano</groupId> + <artifactId>protobuf-javanano</artifactId> + <version>2.6.2-pre</version> + <packaging>bundle</packaging> + <name>Protocol Buffer JavaNano API</name> + <description> + Protocol Buffers are a way of encoding structured data in an efficient yet + extensible format. + </description> + <inceptionYear>2008</inceptionYear> + <url>https://developers.google.com/protocol-buffers/</url> + <licenses> + <license> + <name>New BSD license</name> + <url>http://www.opensource.org/licenses/bsd-license.php</url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> + <url>https://github.com/google/protobuf</url> + <connection> + scm:git:https://github.com/google/protobuf.git + </connection> + </scm> + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.4</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymock</artifactId> + <version>2.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.easymock</groupId> + <artifactId>easymockclassextension</artifactId> + <version>2.2.1</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + </configuration> + </plugin> + <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <includes> + <include>**/*Test.java</include> + </includes> + </configuration> + </plugin> + <plugin> + <artifactId>maven-antrun-plugin</artifactId> + <executions> + <execution> + <id>generate-test-sources</id> + <phase>generate-test-sources</phase> + <configuration> + <tasks> + <mkdir dir="target/generated-test-sources" /> + <exec executable="../src/protoc"> + <arg value="--javanano_out=generate_equals=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_simple_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_stringutf8_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_recursive_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_import_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_single_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_multiple_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=store_unknown_fields=true,generate_equals=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=store_unknown_fields=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=java_nano_generate_has=true,generate_equals=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_has_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=optional_field_style=accessors,generate_equals=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_accessors_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=enum_style=java:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_enum_class_multiple_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_repeated_packables_nano.proto" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_enum_validity_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out= + optional_field_style=accessors, + java_outer_classname=google/protobuf/nano/unittest_enum_validity_nano.proto|EnumValidityAccessors + :target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_enum_validity_nano.proto" /> + </exec> + <exec executable="../src/protoc"> + <arg value="--javanano_out=optional_field_style=reftypes,generate_equals=true:target/generated-test-sources" /> + <arg value="--proto_path=src/test/java/com" /> + <arg value="src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto" /> + </exec> + </tasks> + <testSourceRoot>target/generated-test-sources</testSourceRoot> + </configuration> + <goals> + <goal>run</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-DocURL>https://developers.google.com/protocol-buffers/</Bundle-DocURL> + <Bundle-SymbolicName>com.google.protobuf</Bundle-SymbolicName> + <Export-Package>com.google.protobuf;version=2.6.2-pre</Export-Package> + </instructions> + </configuration> + </plugin> + </plugins> + </build> +</project> diff --git a/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java b/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java new file mode 100644 index 00000000..c5fea5ae --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/CodedInputByteBufferNano.java @@ -0,0 +1,641 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; + +/** + * Reads and decodes protocol message fields. + * + * This class contains two kinds of methods: methods that read specific + * protocol message constructs and field types (e.g. {@link #readTag()} and + * {@link #readInt32()}) and methods that read low-level values (e.g. + * {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading + * encoded protocol messages, you should use the former methods, but if you are + * reading some other format of your own design, use the latter. + * + * @author kenton@google.com Kenton Varda + */ +public final class CodedInputByteBufferNano { + /** + * Create a new CodedInputStream wrapping the given byte array. + */ + public static CodedInputByteBufferNano newInstance(final byte[] buf) { + return newInstance(buf, 0, buf.length); + } + + /** + * Create a new CodedInputStream wrapping the given byte array slice. + */ + public static CodedInputByteBufferNano newInstance(final byte[] buf, final int off, + final int len) { + return new CodedInputByteBufferNano(buf, off, len); + } + + // ----------------------------------------------------------------- + + /** + * Attempt to read a field tag, returning zero if we have reached EOF. + * Protocol message parsers use this to read tags, since a protocol message + * may legally end wherever a tag occurs, and zero is not a valid tag number. + */ + public int readTag() throws IOException { + if (isAtEnd()) { + lastTag = 0; + return 0; + } + + lastTag = readRawVarint32(); + if (lastTag == 0) { + // If we actually read zero, that's not a valid tag. + throw InvalidProtocolBufferNanoException.invalidTag(); + } + return lastTag; + } + + /** + * Verifies that the last call to readTag() returned the given tag value. + * This is used to verify that a nested group ended with the correct + * end tag. + * + * @throws InvalidProtocolBufferNanoException {@code value} does not match the + * last tag. + */ + public void checkLastTagWas(final int value) + throws InvalidProtocolBufferNanoException { + if (lastTag != value) { + throw InvalidProtocolBufferNanoException.invalidEndTag(); + } + } + + /** + * Reads and discards a single field, given its tag value. + * + * @return {@code false} if the tag is an endgroup tag, in which case + * nothing is skipped. Otherwise, returns {@code true}. + */ + public boolean skipField(final int tag) throws IOException { + switch (WireFormatNano.getTagWireType(tag)) { + case WireFormatNano.WIRETYPE_VARINT: + readInt32(); + return true; + case WireFormatNano.WIRETYPE_FIXED64: + readRawLittleEndian64(); + return true; + case WireFormatNano.WIRETYPE_LENGTH_DELIMITED: + skipRawBytes(readRawVarint32()); + return true; + case WireFormatNano.WIRETYPE_START_GROUP: + skipMessage(); + checkLastTagWas( + WireFormatNano.makeTag(WireFormatNano.getTagFieldNumber(tag), + WireFormatNano.WIRETYPE_END_GROUP)); + return true; + case WireFormatNano.WIRETYPE_END_GROUP: + return false; + case WireFormatNano.WIRETYPE_FIXED32: + readRawLittleEndian32(); + return true; + default: + throw InvalidProtocolBufferNanoException.invalidWireType(); + } + } + + /** + * Reads and discards an entire message. This will read either until EOF + * or until an endgroup tag, whichever comes first. + */ + public void skipMessage() throws IOException { + while (true) { + final int tag = readTag(); + if (tag == 0 || !skipField(tag)) { + return; + } + } + } + + // ----------------------------------------------------------------- + + /** Read a {@code double} field value from the stream. */ + public double readDouble() throws IOException { + return Double.longBitsToDouble(readRawLittleEndian64()); + } + + /** Read a {@code float} field value from the stream. */ + public float readFloat() throws IOException { + return Float.intBitsToFloat(readRawLittleEndian32()); + } + + /** Read a {@code uint64} field value from the stream. */ + public long readUInt64() throws IOException { + return readRawVarint64(); + } + + /** Read an {@code int64} field value from the stream. */ + public long readInt64() throws IOException { + return readRawVarint64(); + } + + /** Read an {@code int32} field value from the stream. */ + public int readInt32() throws IOException { + return readRawVarint32(); + } + + /** Read a {@code fixed64} field value from the stream. */ + public long readFixed64() throws IOException { + return readRawLittleEndian64(); + } + + /** Read a {@code fixed32} field value from the stream. */ + public int readFixed32() throws IOException { + return readRawLittleEndian32(); + } + + /** Read a {@code bool} field value from the stream. */ + public boolean readBool() throws IOException { + return readRawVarint32() != 0; + } + + /** Read a {@code string} field value from the stream. */ + public String readString() throws IOException { + final int size = readRawVarint32(); + if (size <= (bufferSize - bufferPos) && size > 0) { + // Fast path: We already have the bytes in a contiguous buffer, so + // just copy directly from it. + final String result = new String(buffer, bufferPos, size, "UTF-8"); + bufferPos += size; + return result; + } else { + // Slow path: Build a byte array first then copy it. + return new String(readRawBytes(size), "UTF-8"); + } + } + + /** Read a {@code group} field value from the stream. */ + public void readGroup(final MessageNano msg, final int fieldNumber) + throws IOException { + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferNanoException.recursionLimitExceeded(); + } + ++recursionDepth; + msg.mergeFrom(this); + checkLastTagWas( + WireFormatNano.makeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP)); + --recursionDepth; + } + + public void readMessage(final MessageNano msg) + throws IOException { + final int length = readRawVarint32(); + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferNanoException.recursionLimitExceeded(); + } + final int oldLimit = pushLimit(length); + ++recursionDepth; + msg.mergeFrom(this); + checkLastTagWas(0); + --recursionDepth; + popLimit(oldLimit); + } + + /** Read a {@code bytes} field value from the stream. */ + public byte[] readBytes() throws IOException { + final int size = readRawVarint32(); + if (size <= (bufferSize - bufferPos) && size > 0) { + // Fast path: We already have the bytes in a contiguous buffer, so + // just copy directly from it. + final byte[] result = new byte[size]; + System.arraycopy(buffer, bufferPos, result, 0, size); + bufferPos += size; + return result; + } else { + // Slow path: Build a byte array first then copy it. + return readRawBytes(size); + } + } + + /** Read a {@code uint32} field value from the stream. */ + public int readUInt32() throws IOException { + return readRawVarint32(); + } + + /** + * Read an enum field value from the stream. Caller is responsible + * for converting the numeric value to an actual enum. + */ + public int readEnum() throws IOException { + return readRawVarint32(); + } + + /** Read an {@code sfixed32} field value from the stream. */ + public int readSFixed32() throws IOException { + return readRawLittleEndian32(); + } + + /** Read an {@code sfixed64} field value from the stream. */ + public long readSFixed64() throws IOException { + return readRawLittleEndian64(); + } + + /** Read an {@code sint32} field value from the stream. */ + public int readSInt32() throws IOException { + return decodeZigZag32(readRawVarint32()); + } + + /** Read an {@code sint64} field value from the stream. */ + public long readSInt64() throws IOException { + return decodeZigZag64(readRawVarint64()); + } + + // ================================================================= + + /** + * Read a raw Varint from the stream. If larger than 32 bits, discard the + * upper bits. + */ + public int readRawVarint32() throws IOException { + byte tmp = readRawByte(); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = readRawByte()) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = readRawByte()) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (readRawByte() >= 0) { + return result; + } + } + throw InvalidProtocolBufferNanoException.malformedVarint(); + } + } + } + } + return result; + } + + /** Read a raw Varint from the stream. */ + public long readRawVarint64() throws IOException { + int shift = 0; + long result = 0; + while (shift < 64) { + final byte b = readRawByte(); + result |= (long)(b & 0x7F) << shift; + if ((b & 0x80) == 0) { + return result; + } + shift += 7; + } + throw InvalidProtocolBufferNanoException.malformedVarint(); + } + + /** Read a 32-bit little-endian integer from the stream. */ + public int readRawLittleEndian32() throws IOException { + final byte b1 = readRawByte(); + final byte b2 = readRawByte(); + final byte b3 = readRawByte(); + final byte b4 = readRawByte(); + return ((b1 & 0xff) ) | + ((b2 & 0xff) << 8) | + ((b3 & 0xff) << 16) | + ((b4 & 0xff) << 24); + } + + /** Read a 64-bit little-endian integer from the stream. */ + public long readRawLittleEndian64() throws IOException { + final byte b1 = readRawByte(); + final byte b2 = readRawByte(); + final byte b3 = readRawByte(); + final byte b4 = readRawByte(); + final byte b5 = readRawByte(); + final byte b6 = readRawByte(); + final byte b7 = readRawByte(); + final byte b8 = readRawByte(); + return (((long)b1 & 0xff) ) | + (((long)b2 & 0xff) << 8) | + (((long)b3 & 0xff) << 16) | + (((long)b4 & 0xff) << 24) | + (((long)b5 & 0xff) << 32) | + (((long)b6 & 0xff) << 40) | + (((long)b7 & 0xff) << 48) | + (((long)b8 & 0xff) << 56); + } + + /** + * Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n An unsigned 32-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + * @return A signed 32-bit integer. + */ + public static int decodeZigZag32(final int n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n An unsigned 64-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + * @return A signed 64-bit integer. + */ + public static long decodeZigZag64(final long n) { + return (n >>> 1) ^ -(n & 1); + } + + // ----------------------------------------------------------------- + + private final byte[] buffer; + private int bufferStart; + private int bufferSize; + private int bufferSizeAfterLimit; + private int bufferPos; + private int lastTag; + + /** The absolute position of the end of the current message. */ + private int currentLimit = Integer.MAX_VALUE; + + /** See setRecursionLimit() */ + private int recursionDepth; + private int recursionLimit = DEFAULT_RECURSION_LIMIT; + + /** See setSizeLimit() */ + private int sizeLimit = DEFAULT_SIZE_LIMIT; + + private static final int DEFAULT_RECURSION_LIMIT = 64; + private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB + + private CodedInputByteBufferNano(final byte[] buffer, final int off, final int len) { + this.buffer = buffer; + bufferStart = off; + bufferSize = off + len; + bufferPos = off; + } + + /** + * Set the maximum message recursion depth. In order to prevent malicious + * messages from causing stack overflows, {@code CodedInputStream} limits + * how deeply messages may be nested. The default limit is 64. + * + * @return the old limit. + */ + public int setRecursionLimit(final int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Recursion limit cannot be negative: " + limit); + } + final int oldLimit = recursionLimit; + recursionLimit = limit; + return oldLimit; + } + + /** + * Set the maximum message size. In order to prevent malicious + * messages from exhausting memory or causing integer overflows, + * {@code CodedInputStream} limits how large a message may be. + * The default limit is 64MB. You should set this limit as small + * as you can without harming your app's functionality. Note that + * size limits only apply when reading from an {@code InputStream}, not + * when constructed around a raw byte array. + * <p> + * If you want to read several messages from a single CodedInputStream, you + * could call {@link #resetSizeCounter()} after each one to avoid hitting the + * size limit. + * + * @return the old limit. + */ + public int setSizeLimit(final int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Size limit cannot be negative: " + limit); + } + final int oldLimit = sizeLimit; + sizeLimit = limit; + return oldLimit; + } + + /** + * Resets the current size counter to zero (see {@link #setSizeLimit(int)}). + */ + public void resetSizeCounter() { + } + + /** + * Sets {@code currentLimit} to (current position) + {@code byteLimit}. This + * is called when descending into a length-delimited embedded message. + * + * @return the old limit. + */ + public int pushLimit(int byteLimit) throws InvalidProtocolBufferNanoException { + if (byteLimit < 0) { + throw InvalidProtocolBufferNanoException.negativeSize(); + } + byteLimit += bufferPos; + final int oldLimit = currentLimit; + if (byteLimit > oldLimit) { + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + currentLimit = byteLimit; + + recomputeBufferSizeAfterLimit(); + + return oldLimit; + } + + private void recomputeBufferSizeAfterLimit() { + bufferSize += bufferSizeAfterLimit; + final int bufferEnd = bufferSize; + if (bufferEnd > currentLimit) { + // Limit is in current buffer. + bufferSizeAfterLimit = bufferEnd - currentLimit; + bufferSize -= bufferSizeAfterLimit; + } else { + bufferSizeAfterLimit = 0; + } + } + + /** + * Discards the current limit, returning to the previous limit. + * + * @param oldLimit The old limit, as returned by {@code pushLimit}. + */ + public void popLimit(final int oldLimit) { + currentLimit = oldLimit; + recomputeBufferSizeAfterLimit(); + } + + /** + * Returns the number of bytes to be read before the current limit. + * If no limit is set, returns -1. + */ + public int getBytesUntilLimit() { + if (currentLimit == Integer.MAX_VALUE) { + return -1; + } + + final int currentAbsolutePosition = bufferPos; + return currentLimit - currentAbsolutePosition; + } + + /** + * Returns true if the stream has reached the end of the input. This is the + * case if either the end of the underlying input source has been reached or + * if the stream has reached a limit created using {@link #pushLimit(int)}. + */ + public boolean isAtEnd() { + return bufferPos == bufferSize; + } + + /** + * Get current position in buffer relative to beginning offset. + */ + public int getPosition() { + return bufferPos - bufferStart; + } + + /** + * Retrieves a subset of data in the buffer. The returned array is not backed by the original + * buffer array. + * + * @param offset the position (relative to the buffer start position) to start at. + * @param length the number of bytes to retrieve. + */ + public byte[] getData(int offset, int length) { + if (length == 0) { + return WireFormatNano.EMPTY_BYTES; + } + byte[] copy = new byte[length]; + int start = bufferStart + offset; + System.arraycopy(buffer, start, copy, 0, length); + return copy; + } + + /** + * Rewind to previous position. Cannot go forward. + */ + public void rewindToPosition(int position) { + if (position > bufferPos - bufferStart) { + throw new IllegalArgumentException( + "Position " + position + " is beyond current " + (bufferPos - bufferStart)); + } + if (position < 0) { + throw new IllegalArgumentException("Bad position " + position); + } + bufferPos = bufferStart + position; + } + + /** + * Read one byte from the input. + * + * @throws InvalidProtocolBufferNanoException The end of the stream or the current + * limit was reached. + */ + public byte readRawByte() throws IOException { + if (bufferPos == bufferSize) { + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + return buffer[bufferPos++]; + } + + /** + * Read a fixed size of bytes from the input. + * + * @throws InvalidProtocolBufferNanoException The end of the stream or the current + * limit was reached. + */ + public byte[] readRawBytes(final int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferNanoException.negativeSize(); + } + + if (bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - bufferPos); + // Then fail. + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + final byte[] bytes = new byte[size]; + System.arraycopy(buffer, bufferPos, bytes, 0, size); + bufferPos += size; + return bytes; + } else { + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + } + + /** + * Reads and discards {@code size} bytes. + * + * @throws InvalidProtocolBufferNanoException The end of the stream or the current + * limit was reached. + */ + public void skipRawBytes(final int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferNanoException.negativeSize(); + } + + if (bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - bufferPos); + // Then fail. + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + bufferPos += size; + } else { + throw InvalidProtocolBufferNanoException.truncatedMessage(); + } + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java new file mode 100644 index 00000000..88df38d7 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/CodedOutputByteBufferNano.java @@ -0,0 +1,879 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; + +/** + * Encodes and writes protocol message fields. + * + * <p>This class contains two kinds of methods: methods that write specific + * protocol message constructs and field types (e.g. {@link #writeTag} and + * {@link #writeInt32}) and methods that write low-level values (e.g. + * {@link #writeRawVarint32} and {@link #writeRawBytes}). If you are + * writing encoded protocol messages, you should use the former methods, but if + * you are writing some other format of your own design, use the latter. + * + * <p>This class is totally unsynchronized. + * + * @author kneton@google.com Kenton Varda + */ +public final class CodedOutputByteBufferNano { + private final byte[] buffer; + private final int limit; + private int position; + + private CodedOutputByteBufferNano(final byte[] buffer, final int offset, + final int length) { + this.buffer = buffer; + position = offset; + limit = offset + length; + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array. If more bytes are written than fit in the array, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. + */ + public static CodedOutputByteBufferNano newInstance(final byte[] flatArray) { + return newInstance(flatArray, 0, flatArray.length); + } + + /** + * Create a new {@code CodedOutputStream} that writes directly to the given + * byte array slice. If more bytes are written than fit in the slice, + * {@link OutOfSpaceException} will be thrown. Writing directly to a flat + * array is faster than writing to an {@code OutputStream}. + */ + public static CodedOutputByteBufferNano newInstance(final byte[] flatArray, + final int offset, + final int length) { + return new CodedOutputByteBufferNano(flatArray, offset, length); + } + + // ----------------------------------------------------------------- + + /** Write a {@code double} field, including tag, to the stream. */ + public void writeDouble(final int fieldNumber, final double value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED64); + writeDoubleNoTag(value); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public void writeFloat(final int fieldNumber, final float value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED32); + writeFloatNoTag(value); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + public void writeUInt64(final int fieldNumber, final long value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeUInt64NoTag(value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public void writeInt64(final int fieldNumber, final long value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeInt64NoTag(value); + } + + /** Write an {@code int32} field, including tag, to the stream. */ + public void writeInt32(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeInt32NoTag(value); + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + public void writeFixed64(final int fieldNumber, final long value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED64); + writeFixed64NoTag(value); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + public void writeFixed32(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED32); + writeFixed32NoTag(value); + } + + /** Write a {@code bool} field, including tag, to the stream. */ + public void writeBool(final int fieldNumber, final boolean value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeBoolNoTag(value); + } + + /** Write a {@code string} field, including tag, to the stream. */ + public void writeString(final int fieldNumber, final String value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_LENGTH_DELIMITED); + writeStringNoTag(value); + } + + /** Write a {@code group} field, including tag, to the stream. */ + public void writeGroup(final int fieldNumber, final MessageNano value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_START_GROUP); + writeGroupNoTag(value); + writeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP); + } + + /** Write an embedded message field, including tag, to the stream. */ + public void writeMessage(final int fieldNumber, final MessageNano value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_LENGTH_DELIMITED); + writeMessageNoTag(value); + } + + /** Write a {@code bytes} field, including tag, to the stream. */ + public void writeBytes(final int fieldNumber, final byte[] value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_LENGTH_DELIMITED); + writeBytesNoTag(value); + } + + /** Write a {@code uint32} field, including tag, to the stream. */ + public void writeUInt32(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeUInt32NoTag(value); + } + + /** + * Write an enum field, including tag, to the stream. Caller is responsible + * for converting the enum value to its numeric value. + */ + public void writeEnum(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeEnumNoTag(value); + } + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public void writeSFixed32(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED32); + writeSFixed32NoTag(value); + } + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public void writeSFixed64(final int fieldNumber, final long value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_FIXED64); + writeSFixed64NoTag(value); + } + + /** Write an {@code sint32} field, including tag, to the stream. */ + public void writeSInt32(final int fieldNumber, final int value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeSInt32NoTag(value); + } + + /** Write an {@code sint64} field, including tag, to the stream. */ + public void writeSInt64(final int fieldNumber, final long value) + throws IOException { + writeTag(fieldNumber, WireFormatNano.WIRETYPE_VARINT); + writeSInt64NoTag(value); + } + + /** + * Write a MessageSet extension field to the stream. For historical reasons, + * the wire format differs from normal fields. + */ +// public void writeMessageSetExtension(final int fieldNumber, +// final MessageMicro value) +// throws IOException { +// writeTag(WireFormatMicro.MESSAGE_SET_ITEM, WireFormatMicro.WIRETYPE_START_GROUP); +// writeUInt32(WireFormatMicro.MESSAGE_SET_TYPE_ID, fieldNumber); +// writeMessage(WireFormatMicro.MESSAGE_SET_MESSAGE, value); +// writeTag(WireFormatMicro.MESSAGE_SET_ITEM, WireFormatMicro.WIRETYPE_END_GROUP); +// } + + /** + * Write an unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ +// public void writeRawMessageSetExtension(final int fieldNumber, +// final ByteStringMicro value) +// throws IOException { +// writeTag(WireFormatMicro.MESSAGE_SET_ITEM, WireFormatMicro.WIRETYPE_START_GROUP); +// writeUInt32(WireFormatMicro.MESSAGE_SET_TYPE_ID, fieldNumber); +// writeBytes(WireFormatMicro.MESSAGE_SET_MESSAGE, value); +// writeTag(WireFormatMicro.MESSAGE_SET_ITEM, WireFormatMicro.WIRETYPE_END_GROUP); +// } + + // ----------------------------------------------------------------- + + /** Write a {@code double} field to the stream. */ + public void writeDoubleNoTag(final double value) throws IOException { + writeRawLittleEndian64(Double.doubleToLongBits(value)); + } + + /** Write a {@code float} field to the stream. */ + public void writeFloatNoTag(final float value) throws IOException { + writeRawLittleEndian32(Float.floatToIntBits(value)); + } + + /** Write a {@code uint64} field to the stream. */ + public void writeUInt64NoTag(final long value) throws IOException { + writeRawVarint64(value); + } + + /** Write an {@code int64} field to the stream. */ + public void writeInt64NoTag(final long value) throws IOException { + writeRawVarint64(value); + } + + /** Write an {@code int32} field to the stream. */ + public void writeInt32NoTag(final int value) throws IOException { + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } + } + + /** Write a {@code fixed64} field to the stream. */ + public void writeFixed64NoTag(final long value) throws IOException { + writeRawLittleEndian64(value); + } + + /** Write a {@code fixed32} field to the stream. */ + public void writeFixed32NoTag(final int value) throws IOException { + writeRawLittleEndian32(value); + } + + /** Write a {@code bool} field to the stream. */ + public void writeBoolNoTag(final boolean value) throws IOException { + writeRawByte(value ? 1 : 0); + } + + /** Write a {@code string} field to the stream. */ + public void writeStringNoTag(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. + final byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code group} field to the stream. */ + public void writeGroupNoTag(final MessageNano value) throws IOException { + value.writeTo(this); + } + + /** Write an embedded message field to the stream. */ + public void writeMessageNoTag(final MessageNano value) throws IOException { + writeRawVarint32(value.getCachedSize()); + value.writeTo(this); + } + + /** Write a {@code bytes} field to the stream. */ + public void writeBytesNoTag(final byte[] value) throws IOException { + writeRawVarint32(value.length); + writeRawBytes(value); + } + + /** Write a {@code uint32} field to the stream. */ + public void writeUInt32NoTag(final int value) throws IOException { + writeRawVarint32(value); + } + + /** + * Write an enum field to the stream. Caller is responsible + * for converting the enum value to its numeric value. + */ + public void writeEnumNoTag(final int value) throws IOException { + writeRawVarint32(value); + } + + /** Write an {@code sfixed32} field to the stream. */ + public void writeSFixed32NoTag(final int value) throws IOException { + writeRawLittleEndian32(value); + } + + /** Write an {@code sfixed64} field to the stream. */ + public void writeSFixed64NoTag(final long value) throws IOException { + writeRawLittleEndian64(value); + } + + /** Write an {@code sint32} field to the stream. */ + public void writeSInt32NoTag(final int value) throws IOException { + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write an {@code sint64} field to the stream. */ + public void writeSInt64NoTag(final long value) throws IOException { + writeRawVarint64(encodeZigZag64(value)); + } + + // ================================================================= + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSize(final int fieldNumber, + final double value) { + return computeTagSize(fieldNumber) + computeDoubleSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSize(final int fieldNumber, final float value) { + return computeTagSize(fieldNumber) + computeFloatSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeUInt64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeInt64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field, including tag. + */ + public static int computeFixed64Size(final int fieldNumber, + final long value) { + return computeTagSize(fieldNumber) + computeFixed64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field, including tag. + */ + public static int computeFixed32Size(final int fieldNumber, + final int value) { + return computeTagSize(fieldNumber) + computeFixed32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field, including tag. + */ + public static int computeBoolSize(final int fieldNumber, + final boolean value) { + return computeTagSize(fieldNumber) + computeBoolSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field, including tag. + */ + public static int computeStringSize(final int fieldNumber, + final String value) { + return computeTagSize(fieldNumber) + computeStringSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field, including tag. + */ + public static int computeGroupSize(final int fieldNumber, + final MessageNano value) { + return computeTagSize(fieldNumber) * 2 + computeGroupSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. + */ + public static int computeMessageSize(final int fieldNumber, + final MessageNano value) { + return computeTagSize(fieldNumber) + computeMessageSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeBytesSize(final int fieldNumber, + final byte[] value) { + return computeTagSize(fieldNumber) + computeBytesSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field, including tag. + */ + public static int computeUInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeUInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * enum field, including tag. Caller is responsible for converting the + * enum value to its numeric value. + */ + public static int computeEnumSize(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeEnumSizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field, including tag. + */ + public static int computeSFixed32Size(final int fieldNumber, + final int value) { + return computeTagSize(fieldNumber) + computeSFixed32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. + */ + public static int computeSFixed64Size(final int fieldNumber, + final long value) { + return computeTagSize(fieldNumber) + computeSFixed64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field, including tag. + */ + public static int computeSInt32Size(final int fieldNumber, final int value) { + return computeTagSize(fieldNumber) + computeSInt32SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field, including tag. + */ + public static int computeSInt64Size(final int fieldNumber, final long value) { + return computeTagSize(fieldNumber) + computeSInt64SizeNoTag(value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * MessageSet extension to the stream. For historical reasons, + * the wire format differs from normal fields. + */ +// public static int computeMessageSetExtensionSize( +// final int fieldNumber, final MessageMicro value) { +// return computeTagSize(WireFormatMicro.MESSAGE_SET_ITEM) * 2 + +// computeUInt32Size(WireFormatMicro.MESSAGE_SET_TYPE_ID, fieldNumber) + +// computeMessageSize(WireFormatMicro.MESSAGE_SET_MESSAGE, value); +// } + + /** + * Compute the number of bytes that would be needed to encode an + * unparsed MessageSet extension field to the stream. For + * historical reasons, the wire format differs from normal fields. + */ +// public static int computeRawMessageSetExtensionSize( +// final int fieldNumber, final ByteStringMicro value) { +// return computeTagSize(WireFormatMicro.MESSAGE_SET_ITEM) * 2 + +// computeUInt32Size(WireFormatMicro.MESSAGE_SET_TYPE_ID, fieldNumber) + +// computeBytesSize(WireFormatMicro.MESSAGE_SET_MESSAGE, value); +// } + + // ----------------------------------------------------------------- + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSizeNoTag(final double value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSizeNoTag(final float value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64SizeNoTag(final long value) { + return computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64SizeNoTag(final long value) { + return computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32SizeNoTag(final int value) { + if (value >= 0) { + return computeRawVarint32Size(value); + } else { + // Must sign-extend. + return 10; + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field. + */ + public static int computeFixed64SizeNoTag(final long value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field. + */ + public static int computeFixed32SizeNoTag(final int value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field. + */ + public static int computeBoolSizeNoTag(final boolean value) { + return 1; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field. + */ + public static int computeStringSizeNoTag(final String value) { + try { + final byte[] bytes = value.getBytes("UTF-8"); + return computeRawVarint32Size(bytes.length) + + bytes.length; + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported."); + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field. + */ + public static int computeGroupSizeNoTag(final MessageNano value) { + return value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode an embedded + * message field. + */ + public static int computeMessageSizeNoTag(final MessageNano value) { + final int size = value.getSerializedSize(); + return computeRawVarint32Size(size) + size; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field. + */ + public static int computeBytesSizeNoTag(final byte[] value) { + return computeRawVarint32Size(value.length) + value.length; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field. + */ + public static int computeUInt32SizeNoTag(final int value) { + return computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an enum field. + * Caller is responsible for converting the enum value to its numeric value. + */ + public static int computeEnumSizeNoTag(final int value) { + return computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field. + */ + public static int computeSFixed32SizeNoTag(final int value) { + return LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field. + */ + public static int computeSFixed64SizeNoTag(final long value) { + return LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field. + */ + public static int computeSInt32SizeNoTag(final int value) { + return computeRawVarint32Size(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field. + */ + public static int computeSInt64SizeNoTag(final long value) { + return computeRawVarint64Size(encodeZigZag64(value)); + } + + // ================================================================= + + /** + * If writing to a flat array, return the space left in the array. + * Otherwise, throws {@code UnsupportedOperationException}. + */ + public int spaceLeft() { + return limit - position; + } + + /** + * Verifies that {@link #spaceLeft()} returns zero. It's common to create + * a byte array that is exactly big enough to hold a message, then write to + * it with a {@code CodedOutputStream}. Calling {@code checkNoSpaceLeft()} + * after writing verifies that the message was actually as big as expected, + * which can help catch bugs. + */ + public void checkNoSpaceLeft() { + if (spaceLeft() != 0) { + throw new IllegalStateException( + "Did not write as much data as expected."); + } + } + + /** + * If you create a CodedOutputStream around a simple flat array, you must + * not attempt to write more bytes than the array has space. Otherwise, + * this exception will be thrown. + */ + public static class OutOfSpaceException extends IOException { + private static final long serialVersionUID = -6947486886997889499L; + + OutOfSpaceException(int position, int limit) { + super("CodedOutputStream was writing to a flat byte array and ran " + + "out of space (pos " + position + " limit " + limit + ")."); + } + } + + /** Write a single byte. */ + public void writeRawByte(final byte value) throws IOException { + if (position == limit) { + // We're writing to a single buffer. + throw new OutOfSpaceException(position, limit); + } + + buffer[position++] = value; + } + + /** Write a single byte, represented by an integer value. */ + public void writeRawByte(final int value) throws IOException { + writeRawByte((byte) value); + } + + /** Write an array of bytes. */ + public void writeRawBytes(final byte[] value) throws IOException { + writeRawBytes(value, 0, value.length); + } + + /** Write part of an array of bytes. */ + public void writeRawBytes(final byte[] value, int offset, int length) + throws IOException { + if (limit - position >= length) { + // We have room in the current buffer. + System.arraycopy(value, offset, buffer, position, length); + position += length; + } else { + // We're writing to a single buffer. + throw new OutOfSpaceException(position, limit); + } + } + + /** Encode and write a tag. */ + public void writeTag(final int fieldNumber, final int wireType) + throws IOException { + writeRawVarint32(WireFormatNano.makeTag(fieldNumber, wireType)); + } + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(final int fieldNumber) { + return computeRawVarint32Size(WireFormatNano.makeTag(fieldNumber, 0)); + } + + /** + * Encode and write a varint. {@code value} is treated as + * unsigned, so it won't be sign-extended if negative. + */ + public void writeRawVarint32(int value) throws IOException { + while (true) { + if ((value & ~0x7F) == 0) { + writeRawByte(value); + return; + } else { + writeRawByte((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + */ + public static int computeRawVarint32Size(final int value) { + if ((value & (0xffffffff << 7)) == 0) return 1; + if ((value & (0xffffffff << 14)) == 0) return 2; + if ((value & (0xffffffff << 21)) == 0) return 3; + if ((value & (0xffffffff << 28)) == 0) return 4; + return 5; + } + + /** Encode and write a varint. */ + public void writeRawVarint64(long value) throws IOException { + while (true) { + if ((value & ~0x7FL) == 0) { + writeRawByte((int)value); + return; + } else { + writeRawByte(((int)value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** Compute the number of bytes that would be needed to encode a varint. */ + public static int computeRawVarint64Size(final long value) { + if ((value & (0xffffffffffffffffL << 7)) == 0) return 1; + if ((value & (0xffffffffffffffffL << 14)) == 0) return 2; + if ((value & (0xffffffffffffffffL << 21)) == 0) return 3; + if ((value & (0xffffffffffffffffL << 28)) == 0) return 4; + if ((value & (0xffffffffffffffffL << 35)) == 0) return 5; + if ((value & (0xffffffffffffffffL << 42)) == 0) return 6; + if ((value & (0xffffffffffffffffL << 49)) == 0) return 7; + if ((value & (0xffffffffffffffffL << 56)) == 0) return 8; + if ((value & (0xffffffffffffffffL << 63)) == 0) return 9; + return 10; + } + + /** Write a little-endian 32-bit integer. */ + public void writeRawLittleEndian32(final int value) throws IOException { + writeRawByte((value ) & 0xFF); + writeRawByte((value >> 8) & 0xFF); + writeRawByte((value >> 16) & 0xFF); + writeRawByte((value >> 24) & 0xFF); + } + + public static final int LITTLE_ENDIAN_32_SIZE = 4; + + /** Write a little-endian 64-bit integer. */ + public void writeRawLittleEndian64(final long value) throws IOException { + writeRawByte((int)(value ) & 0xFF); + writeRawByte((int)(value >> 8) & 0xFF); + writeRawByte((int)(value >> 16) & 0xFF); + writeRawByte((int)(value >> 24) & 0xFF); + writeRawByte((int)(value >> 32) & 0xFF); + writeRawByte((int)(value >> 40) & 0xFF); + writeRawByte((int)(value >> 48) & 0xFF); + writeRawByte((int)(value >> 56) & 0xFF); + } + + public static final int LITTLE_ENDIAN_64_SIZE = 8; + + /** + * Encode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 32-bit integer. + * @return An unsigned 32-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + */ + public static int encodeZigZag32(final int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); + } + + /** + * Encode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers + * into values that can be efficiently encoded with varint. (Otherwise, + * negative values must be sign-extended to 64 bits to be varint encoded, + * thus always taking 10 bytes on the wire.) + * + * @param n A signed 64-bit integer. + * @return An unsigned 64-bit integer, stored in a signed int because + * Java has no explicit unsigned support. + */ + public static long encodeZigZag64(final long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java b/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java new file mode 100644 index 00000000..46cd86f3 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/ExtendableMessageNano.java @@ -0,0 +1,187 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; + +/** + * Base class of those Protocol Buffer messages that need to store unknown fields, + * such as extensions. + */ +public abstract class ExtendableMessageNano<M extends ExtendableMessageNano<M>> + extends MessageNano { + /** + * A container for fields unknown to the message, including extensions. Extension fields can + * can be accessed through the {@link #getExtension} and {@link #setExtension} methods. + */ + protected FieldArray unknownFieldData; + + @Override + protected int computeSerializedSize() { + int size = 0; + if (unknownFieldData != null) { + for (int i = 0; i < unknownFieldData.size(); i++) { + FieldData field = unknownFieldData.dataAt(i); + size += field.computeSerializedSize(); + } + } + return size; + } + + @Override + public void writeTo(CodedOutputByteBufferNano output) throws IOException { + if (unknownFieldData == null) { + return; + } + for (int i = 0; i < unknownFieldData.size(); i++) { + FieldData field = unknownFieldData.dataAt(i); + field.writeTo(output); + } + } + + /** + * Checks if there is a value stored for the specified extension in this + * message. + */ + public final boolean hasExtension(Extension<M, ?> extension) { + if (unknownFieldData == null) { + return false; + } + FieldData field = unknownFieldData.get(WireFormatNano.getTagFieldNumber(extension.tag)); + return field != null; + } + + /** + * Gets the value stored in the specified extension of this message. + */ + public final <T> T getExtension(Extension<M, T> extension) { + if (unknownFieldData == null) { + return null; + } + FieldData field = unknownFieldData.get(WireFormatNano.getTagFieldNumber(extension.tag)); + return field == null ? null : field.getValue(extension); + } + + /** + * Sets the value of the specified extension of this message. + */ + public final <T> M setExtension(Extension<M, T> extension, T value) { + int fieldNumber = WireFormatNano.getTagFieldNumber(extension.tag); + if (value == null) { + if (unknownFieldData != null) { + unknownFieldData.remove(fieldNumber); + if (unknownFieldData.isEmpty()) { + unknownFieldData = null; + } + } + } else { + FieldData field = null; + if (unknownFieldData == null) { + unknownFieldData = new FieldArray(); + } else { + field = unknownFieldData.get(fieldNumber); + } + if (field == null) { + unknownFieldData.put(fieldNumber, new FieldData(extension, value)); + } else { + field.setValue(extension, value); + } + } + + @SuppressWarnings("unchecked") // Generated code should guarantee type safety + M typedThis = (M) this; + return typedThis; + } + + /** + * Stores the binary data of an unknown field. + * + * <p>Generated messages will call this for unknown fields if the store_unknown_fields + * option is on. + * + * <p>Note that the tag might be a end-group tag (rather than the start of an unknown field) in + * which case we do not want to add an unknown field entry. + * + * @param input the input buffer. + * @param tag the tag of the field. + + * @return {@literal true} unless the tag is an end-group tag. + */ + protected final boolean storeUnknownField(CodedInputByteBufferNano input, int tag) + throws IOException { + int startPos = input.getPosition(); + if (!input.skipField(tag)) { + return false; // This wasn't an unknown field, it's an end-group tag. + } + int fieldNumber = WireFormatNano.getTagFieldNumber(tag); + int endPos = input.getPosition(); + byte[] bytes = input.getData(startPos, endPos - startPos); + UnknownFieldData unknownField = new UnknownFieldData(tag, bytes); + + FieldData field = null; + if (unknownFieldData == null) { + unknownFieldData = new FieldArray(); + } else { + field = unknownFieldData.get(fieldNumber); + } + if (field == null) { + field = new FieldData(); + unknownFieldData.put(fieldNumber, field); + } + field.addUnknownField(unknownField); + return true; + } + + /** + * Returns whether the stored unknown field data in this message is equivalent to that in the + * other message. + * + * @param other the other message. + * @return whether the two sets of unknown field data are equal. + */ + protected final boolean unknownFieldDataEquals(M other) { + if (unknownFieldData == null || unknownFieldData.isEmpty()) { + return other.unknownFieldData == null || other.unknownFieldData.isEmpty(); + } else { + return unknownFieldData.equals(other.unknownFieldData); + } + } + + /** + * Computes the hashcode representing the unknown field data stored in this message. + * + * @return the hashcode for the unknown field data. + */ + protected final int unknownFieldDataHashCode() { + return (unknownFieldData == null || unknownFieldData.isEmpty() + ? 0 : unknownFieldData.hashCode()); + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/Extension.java b/javanano/src/main/java/com/google/protobuf/nano/Extension.java new file mode 100644 index 00000000..a851daf8 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/Extension.java @@ -0,0 +1,722 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +/** + * Represents an extension. + * + * @author bduff@google.com (Brian Duff) + * @author maxtroy@google.com (Max Cai) + * @param <M> the type of the extendable message this extension is for. + * @param <T> the Java type of the extension; see {@link #clazz}. + */ +public class Extension<M extends ExtendableMessageNano<M>, T> { + + /* + * Because we typically only define message-typed extensions, the Extension class hierarchy is + * designed as follows, to allow a big amount of code in this file to be removed by ProGuard: + * + * Extension // ready to use for message/group typed extensions + * Δ + * | + * PrimitiveExtension // for primitive/enum typed extensions + */ + + public static final int TYPE_DOUBLE = 1; + public static final int TYPE_FLOAT = 2; + public static final int TYPE_INT64 = 3; + public static final int TYPE_UINT64 = 4; + public static final int TYPE_INT32 = 5; + public static final int TYPE_FIXED64 = 6; + public static final int TYPE_FIXED32 = 7; + public static final int TYPE_BOOL = 8; + public static final int TYPE_STRING = 9; + public static final int TYPE_GROUP = 10; + public static final int TYPE_MESSAGE = 11; + public static final int TYPE_BYTES = 12; + public static final int TYPE_UINT32 = 13; + public static final int TYPE_ENUM = 14; + public static final int TYPE_SFIXED32 = 15; + public static final int TYPE_SFIXED64 = 16; + public static final int TYPE_SINT32 = 17; + public static final int TYPE_SINT64 = 18; + + /** + * Creates an {@code Extension} of the given message type and tag number. + * Should be used by the generated code only. + * + * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} + */ + public static <M extends ExtendableMessageNano<M>, T extends MessageNano> + Extension<M, T> createMessageTyped(int type, Class<T> clazz, int tag) { + return new Extension<M, T>(type, clazz, tag, false); + } + + /** + * Creates a repeated {@code Extension} of the given message type and tag number. + * Should be used by the generated code only. + * + * @param type {@link #TYPE_MESSAGE} or {@link #TYPE_GROUP} + */ + public static <M extends ExtendableMessageNano<M>, T extends MessageNano> + Extension<M, T[]> createRepeatedMessageTyped(int type, Class<T[]> clazz, int tag) { + return new Extension<M, T[]>(type, clazz, tag, true); + } + + /** + * Creates an {@code Extension} of the given primitive type and tag number. + * Should be used by the generated code only. + * + * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP} + * @param clazz the boxed Java type of this extension + */ + public static <M extends ExtendableMessageNano<M>, T> + Extension<M, T> createPrimitiveTyped(int type, Class<T> clazz, int tag) { + return new PrimitiveExtension<M, T>(type, clazz, tag, false, 0, 0); + } + + /** + * Creates a repeated {@code Extension} of the given primitive type and tag number. + * Should be used by the generated code only. + * + * @param type one of {@code TYPE_*}, except {@link #TYPE_MESSAGE} and {@link #TYPE_GROUP} + * @param clazz the Java array type of this extension, with an unboxed component type + */ + public static <M extends ExtendableMessageNano<M>, T> + Extension<M, T> createRepeatedPrimitiveTyped( + int type, Class<T> clazz, int tag, int nonPackedTag, int packedTag) { + return new PrimitiveExtension<M, T>(type, clazz, tag, true, nonPackedTag, packedTag); + } + + /** + * Protocol Buffer type of this extension; one of the {@code TYPE_} constants. + */ + protected final int type; + + /** + * Java type of this extension. For a singular extension, this is the boxed Java type for the + * Protocol Buffer {@link #type}; for a repeated extension, this is an array type whose + * component type is the unboxed Java type for {@link #type}. For example, for a singular + * {@code int32}/{@link #TYPE_INT32} extension, this equals {@code Integer.class}; for a + * repeated {@code int32} extension, this equals {@code int[].class}. + */ + protected final Class<T> clazz; + + /** + * Tag number of this extension. + */ + public final int tag; + + /** + * Whether this extension is repeated. + */ + protected final boolean repeated; + + private Extension(int type, Class<T> clazz, int tag, boolean repeated) { + this.type = type; + this.clazz = clazz; + this.tag = tag; + this.repeated = repeated; + } + + /** + * Returns the value of this extension stored in the given list of unknown fields, or + * {@code null} if no unknown fields matches this extension. + * + * @param unknownFields a list of {@link UnknownFieldData}. All of the elements must have a tag + * that matches this Extension's tag. + * + */ + final T getValueFrom(List<UnknownFieldData> unknownFields) { + if (unknownFields == null) { + return null; + } + return repeated ? getRepeatedValueFrom(unknownFields) : getSingularValueFrom(unknownFields); + } + + private T getRepeatedValueFrom(List<UnknownFieldData> unknownFields) { + // For repeated extensions, read all matching unknown fields in their original order. + List<Object> resultList = new ArrayList<Object>(); + for (int i = 0; i < unknownFields.size(); i++) { + UnknownFieldData data = unknownFields.get(i); + if (data.bytes.length != 0) { + readDataInto(data, resultList); + } + } + + int resultSize = resultList.size(); + if (resultSize == 0) { + return null; + } else { + T result = clazz.cast(Array.newInstance(clazz.getComponentType(), resultSize)); + for (int i = 0; i < resultSize; i++) { + Array.set(result, i, resultList.get(i)); + } + return result; + } + } + + private T getSingularValueFrom(List<UnknownFieldData> unknownFields) { + // For singular extensions, get the last piece of data stored under this extension. + if (unknownFields.isEmpty()) { + return null; + } + UnknownFieldData lastData = unknownFields.get(unknownFields.size() - 1); + return clazz.cast(readData(CodedInputByteBufferNano.newInstance(lastData.bytes))); + } + + protected Object readData(CodedInputByteBufferNano input) { + // This implementation is for message/group extensions. + Class<?> messageType = repeated ? clazz.getComponentType() : clazz; + try { + switch (type) { + case TYPE_GROUP: + MessageNano group = (MessageNano) messageType.newInstance(); + input.readGroup(group, WireFormatNano.getTagFieldNumber(tag)); + return group; + case TYPE_MESSAGE: + MessageNano message = (MessageNano) messageType.newInstance(); + input.readMessage(message); + return message; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (InstantiationException e) { + throw new IllegalArgumentException( + "Error creating instance of class " + messageType, e); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException( + "Error creating instance of class " + messageType, e); + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + } + + protected void readDataInto(UnknownFieldData data, List<Object> resultList) { + // This implementation is for message/group extensions. + resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes))); + } + + void writeTo(Object value, CodedOutputByteBufferNano output) throws IOException { + if (repeated) { + writeRepeatedData(value, output); + } else { + writeSingularData(value, output); + } + } + + protected void writeSingularData(Object value, CodedOutputByteBufferNano out) { + // This implementation is for message/group extensions. + try { + out.writeRawVarint32(tag); + switch (type) { + case TYPE_GROUP: + MessageNano groupValue = (MessageNano) value; + int fieldNumber = WireFormatNano.getTagFieldNumber(tag); + out.writeGroupNoTag(groupValue); + // The endgroup tag must be included in the data payload. + out.writeTag(fieldNumber, WireFormatNano.WIRETYPE_END_GROUP); + break; + case TYPE_MESSAGE: + MessageNano messageValue = (MessageNano) value; + out.writeMessageNoTag(messageValue); + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + // Should not happen + throw new IllegalStateException(e); + } + } + + protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) { + // This implementation is for non-packed extensions. + int arrayLength = Array.getLength(array); + for (int i = 0; i < arrayLength; i++) { + Object element = Array.get(array, i); + if (element != null) { + writeSingularData(element, output); + } + } + } + + int computeSerializedSize(Object value) { + if (repeated) { + return computeRepeatedSerializedSize(value); + } else { + return computeSingularSerializedSize(value); + } + } + + protected int computeRepeatedSerializedSize(Object array) { + // This implementation is for non-packed extensions. + int size = 0; + int arrayLength = Array.getLength(array); + for (int i = 0; i < arrayLength; i++) { + Object element = Array.get(array, i); + if (element != null) { + size += computeSingularSerializedSize(Array.get(array, i)); + } + } + return size; + } + + protected int computeSingularSerializedSize(Object value) { + // This implementation is for message/group extensions. + int fieldNumber = WireFormatNano.getTagFieldNumber(tag); + switch (type) { + case TYPE_GROUP: + MessageNano groupValue = (MessageNano) value; + return CodedOutputByteBufferNano.computeGroupSize(fieldNumber, groupValue); + case TYPE_MESSAGE: + MessageNano messageValue = (MessageNano) value; + return CodedOutputByteBufferNano.computeMessageSize(fieldNumber, messageValue); + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } + + /** + * Represents an extension of a primitive (including enum) type. If there is no primitive + * extensions, this subclass will be removable by ProGuard. + */ + private static class PrimitiveExtension<M extends ExtendableMessageNano<M>, T> + extends Extension<M, T> { + + /** + * Tag of a piece of non-packed data from the wire compatible with this extension. + */ + private final int nonPackedTag; + + /** + * Tag of a piece of packed data from the wire compatible with this extension. + * 0 if the type of this extension is not packable. + */ + private final int packedTag; + + public PrimitiveExtension(int type, Class<T> clazz, int tag, boolean repeated, + int nonPackedTag, int packedTag) { + super(type, clazz, tag, repeated); + this.nonPackedTag = nonPackedTag; + this.packedTag = packedTag; + } + + @Override + protected Object readData(CodedInputByteBufferNano input) { + try { + switch (type) { + case TYPE_DOUBLE: + return input.readDouble(); + case TYPE_FLOAT: + return input.readFloat(); + case TYPE_INT64: + return input.readInt64(); + case TYPE_UINT64: + return input.readUInt64(); + case TYPE_INT32: + return input.readInt32(); + case TYPE_FIXED64: + return input.readFixed64(); + case TYPE_FIXED32: + return input.readFixed32(); + case TYPE_BOOL: + return input.readBool(); + case TYPE_STRING: + return input.readString(); + case TYPE_BYTES: + return input.readBytes(); + case TYPE_UINT32: + return input.readUInt32(); + case TYPE_ENUM: + return input.readEnum(); + case TYPE_SFIXED32: + return input.readSFixed32(); + case TYPE_SFIXED64: + return input.readSFixed64(); + case TYPE_SINT32: + return input.readSInt32(); + case TYPE_SINT64: + return input.readSInt64(); + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + } + + @Override + protected void readDataInto(UnknownFieldData data, List<Object> resultList) { + // This implementation is for primitive typed extensions, + // which can read both packed and non-packed data. + if (data.tag == nonPackedTag) { + resultList.add(readData(CodedInputByteBufferNano.newInstance(data.bytes))); + } else { + CodedInputByteBufferNano buffer = + CodedInputByteBufferNano.newInstance(data.bytes); + try { + buffer.pushLimit(buffer.readRawVarint32()); // length limit + } catch (IOException e) { + throw new IllegalArgumentException("Error reading extension field", e); + } + while (!buffer.isAtEnd()) { + resultList.add(readData(buffer)); + } + } + } + + @Override + protected final void writeSingularData(Object value, CodedOutputByteBufferNano output) { + try { + output.writeRawVarint32(tag); + switch (type) { + case TYPE_DOUBLE: + Double doubleValue = (Double) value; + output.writeDoubleNoTag(doubleValue); + break; + case TYPE_FLOAT: + Float floatValue = (Float) value; + output.writeFloatNoTag(floatValue); + break; + case TYPE_INT64: + Long int64Value = (Long) value; + output.writeInt64NoTag(int64Value); + break; + case TYPE_UINT64: + Long uint64Value = (Long) value; + output.writeUInt64NoTag(uint64Value); + break; + case TYPE_INT32: + Integer int32Value = (Integer) value; + output.writeInt32NoTag(int32Value); + break; + case TYPE_FIXED64: + Long fixed64Value = (Long) value; + output.writeFixed64NoTag(fixed64Value); + break; + case TYPE_FIXED32: + Integer fixed32Value = (Integer) value; + output.writeFixed32NoTag(fixed32Value); + break; + case TYPE_BOOL: + Boolean boolValue = (Boolean) value; + output.writeBoolNoTag(boolValue); + break; + case TYPE_STRING: + String stringValue = (String) value; + output.writeStringNoTag(stringValue); + break; + case TYPE_BYTES: + byte[] bytesValue = (byte[]) value; + output.writeBytesNoTag(bytesValue); + break; + case TYPE_UINT32: + Integer uint32Value = (Integer) value; + output.writeUInt32NoTag(uint32Value); + break; + case TYPE_ENUM: + Integer enumValue = (Integer) value; + output.writeEnumNoTag(enumValue); + break; + case TYPE_SFIXED32: + Integer sfixed32Value = (Integer) value; + output.writeSFixed32NoTag(sfixed32Value); + break; + case TYPE_SFIXED64: + Long sfixed64Value = (Long) value; + output.writeSFixed64NoTag(sfixed64Value); + break; + case TYPE_SINT32: + Integer sint32Value = (Integer) value; + output.writeSInt32NoTag(sint32Value); + break; + case TYPE_SINT64: + Long sint64Value = (Long) value; + output.writeSInt64NoTag(sint64Value); + break; + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } catch (IOException e) { + // Should not happen + throw new IllegalStateException(e); + } + } + + @Override + protected void writeRepeatedData(Object array, CodedOutputByteBufferNano output) { + if (tag == nonPackedTag) { + // Use base implementation for non-packed data + super.writeRepeatedData(array, output); + } else if (tag == packedTag) { + // Packed. Note that the array element type is guaranteed to be primitive, so there + // won't be any null elements, so no null check in this block. + int arrayLength = Array.getLength(array); + int dataSize = computePackedDataSize(array); + + try { + output.writeRawVarint32(tag); + output.writeRawVarint32(dataSize); + switch (type) { + case TYPE_BOOL: + for (int i = 0; i < arrayLength; i++) { + output.writeBoolNoTag(Array.getBoolean(array, i)); + } + break; + case TYPE_FIXED32: + for (int i = 0; i < arrayLength; i++) { + output.writeFixed32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_SFIXED32: + for (int i = 0; i < arrayLength; i++) { + output.writeSFixed32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_FLOAT: + for (int i = 0; i < arrayLength; i++) { + output.writeFloatNoTag(Array.getFloat(array, i)); + } + break; + case TYPE_FIXED64: + for (int i = 0; i < arrayLength; i++) { + output.writeFixed64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_SFIXED64: + for (int i = 0; i < arrayLength; i++) { + output.writeSFixed64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_DOUBLE: + for (int i = 0; i < arrayLength; i++) { + output.writeDoubleNoTag(Array.getDouble(array, i)); + } + break; + case TYPE_INT32: + for (int i = 0; i < arrayLength; i++) { + output.writeInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_SINT32: + for (int i = 0; i < arrayLength; i++) { + output.writeSInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_UINT32: + for (int i = 0; i < arrayLength; i++) { + output.writeUInt32NoTag(Array.getInt(array, i)); + } + break; + case TYPE_INT64: + for (int i = 0; i < arrayLength; i++) { + output.writeInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_SINT64: + for (int i = 0; i < arrayLength; i++) { + output.writeSInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_UINT64: + for (int i = 0; i < arrayLength; i++) { + output.writeUInt64NoTag(Array.getLong(array, i)); + } + break; + case TYPE_ENUM: + for (int i = 0; i < arrayLength; i++) { + output.writeEnumNoTag(Array.getInt(array, i)); + } + break; + default: + throw new IllegalArgumentException("Unpackable type " + type); + } + } catch (IOException e) { + // Should not happen. + throw new IllegalStateException(e); + } + } else { + throw new IllegalArgumentException("Unexpected repeated extension tag " + tag + + ", unequal to both non-packed variant " + nonPackedTag + + " and packed variant " + packedTag); + } + } + + private int computePackedDataSize(Object array) { + int dataSize = 0; + int arrayLength = Array.getLength(array); + switch (type) { + case TYPE_BOOL: + // Bools are stored as int32 but just as 0 or 1, so 1 byte each. + dataSize = arrayLength; + break; + case TYPE_FIXED32: + case TYPE_SFIXED32: + case TYPE_FLOAT: + dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_32_SIZE; + break; + case TYPE_FIXED64: + case TYPE_SFIXED64: + case TYPE_DOUBLE: + dataSize = arrayLength * CodedOutputByteBufferNano.LITTLE_ENDIAN_64_SIZE; + break; + case TYPE_INT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_SINT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeSInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_UINT32: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeUInt32SizeNoTag( + Array.getInt(array, i)); + } + break; + case TYPE_INT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_SINT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeSInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_UINT64: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeUInt64SizeNoTag( + Array.getLong(array, i)); + } + break; + case TYPE_ENUM: + for (int i = 0; i < arrayLength; i++) { + dataSize += CodedOutputByteBufferNano.computeEnumSizeNoTag( + Array.getInt(array, i)); + } + break; + default: + throw new IllegalArgumentException("Unexpected non-packable type " + type); + } + return dataSize; + } + + @Override + protected int computeRepeatedSerializedSize(Object array) { + if (tag == nonPackedTag) { + // Use base implementation for non-packed data + return super.computeRepeatedSerializedSize(array); + } else if (tag == packedTag) { + // Packed. + int dataSize = computePackedDataSize(array); + int payloadSize = + dataSize + CodedOutputByteBufferNano.computeRawVarint32Size(dataSize); + return payloadSize + CodedOutputByteBufferNano.computeRawVarint32Size(tag); + } else { + throw new IllegalArgumentException("Unexpected repeated extension tag " + tag + + ", unequal to both non-packed variant " + nonPackedTag + + " and packed variant " + packedTag); + } + } + + @Override + protected final int computeSingularSerializedSize(Object value) { + int fieldNumber = WireFormatNano.getTagFieldNumber(tag); + switch (type) { + case TYPE_DOUBLE: + Double doubleValue = (Double) value; + return CodedOutputByteBufferNano.computeDoubleSize(fieldNumber, doubleValue); + case TYPE_FLOAT: + Float floatValue = (Float) value; + return CodedOutputByteBufferNano.computeFloatSize(fieldNumber, floatValue); + case TYPE_INT64: + Long int64Value = (Long) value; + return CodedOutputByteBufferNano.computeInt64Size(fieldNumber, int64Value); + case TYPE_UINT64: + Long uint64Value = (Long) value; + return CodedOutputByteBufferNano.computeUInt64Size(fieldNumber, uint64Value); + case TYPE_INT32: + Integer int32Value = (Integer) value; + return CodedOutputByteBufferNano.computeInt32Size(fieldNumber, int32Value); + case TYPE_FIXED64: + Long fixed64Value = (Long) value; + return CodedOutputByteBufferNano.computeFixed64Size(fieldNumber, fixed64Value); + case TYPE_FIXED32: + Integer fixed32Value = (Integer) value; + return CodedOutputByteBufferNano.computeFixed32Size(fieldNumber, fixed32Value); + case TYPE_BOOL: + Boolean boolValue = (Boolean) value; + return CodedOutputByteBufferNano.computeBoolSize(fieldNumber, boolValue); + case TYPE_STRING: + String stringValue = (String) value; + return CodedOutputByteBufferNano.computeStringSize(fieldNumber, stringValue); + case TYPE_BYTES: + byte[] bytesValue = (byte[]) value; + return CodedOutputByteBufferNano.computeBytesSize(fieldNumber, bytesValue); + case TYPE_UINT32: + Integer uint32Value = (Integer) value; + return CodedOutputByteBufferNano.computeUInt32Size(fieldNumber, uint32Value); + case TYPE_ENUM: + Integer enumValue = (Integer) value; + return CodedOutputByteBufferNano.computeEnumSize(fieldNumber, enumValue); + case TYPE_SFIXED32: + Integer sfixed32Value = (Integer) value; + return CodedOutputByteBufferNano.computeSFixed32Size(fieldNumber, + sfixed32Value); + case TYPE_SFIXED64: + Long sfixed64Value = (Long) value; + return CodedOutputByteBufferNano.computeSFixed64Size(fieldNumber, + sfixed64Value); + case TYPE_SINT32: + Integer sint32Value = (Integer) value; + return CodedOutputByteBufferNano.computeSInt32Size(fieldNumber, sint32Value); + case TYPE_SINT64: + Long sint64Value = (Long) value; + return CodedOutputByteBufferNano.computeSInt64Size(fieldNumber, sint64Value); + default: + throw new IllegalArgumentException("Unknown type " + type); + } + } + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java new file mode 100644 index 00000000..ab923a4d --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/FieldArray.java @@ -0,0 +1,273 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + + +/** + * A custom version of {@link android.util.SparseArray} with the minimal API + * for storing {@link FieldData} objects. + * + * Based on {@link android.support.v4.util.SpareArrayCompat}. + */ +class FieldArray { + private static final FieldData DELETED = new FieldData(); + private boolean mGarbage = false; + + private int[] mFieldNumbers; + private FieldData[] mData; + private int mSize; + + /** + * Creates a new FieldArray containing no fields. + */ + public FieldArray() { + this(10); + } + + /** + * Creates a new FieldArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public FieldArray(int initialCapacity) { + initialCapacity = idealIntArraySize(initialCapacity); + mFieldNumbers = new int[initialCapacity]; + mData = new FieldData[initialCapacity]; + mSize = 0; + } + + /** + * Gets the FieldData mapped from the specified fieldNumber, or <code>null</code> + * if no such mapping has been made. + */ + public FieldData get(int fieldNumber) { + int i = binarySearch(fieldNumber); + + if (i < 0 || mData[i] == DELETED) { + return null; + } else { + return mData[i]; + } + } + + /** + * Removes the data from the specified fieldNumber, if there was any. + */ + public void remove(int fieldNumber) { + int i = binarySearch(fieldNumber); + + if (i >= 0 && mData[i] != DELETED) { + mData[i] = DELETED; + mGarbage = true; + } + } + + private void gc() { + int n = mSize; + int o = 0; + int[] keys = mFieldNumbers; + FieldData[] values = mData; + + for (int i = 0; i < n; i++) { + FieldData val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + values[i] = null; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + } + + /** + * Adds a mapping from the specified fieldNumber to the specified data, + * replacing the previous mapping if there was one. + */ + public void put(int fieldNumber, FieldData data) { + int i = binarySearch(fieldNumber); + + if (i >= 0) { + mData[i] = data; + } else { + i = ~i; + + if (i < mSize && mData[i] == DELETED) { + mFieldNumbers[i] = fieldNumber; + mData[i] = data; + return; + } + + if (mGarbage && mSize >= mFieldNumbers.length) { + gc(); + + // Search again because indices may have changed. + i = ~ binarySearch(fieldNumber); + } + + if (mSize >= mFieldNumbers.length) { + int n = idealIntArraySize(mSize + 1); + + int[] nkeys = new int[n]; + FieldData[] nvalues = new FieldData[n]; + + System.arraycopy(mFieldNumbers, 0, nkeys, 0, mFieldNumbers.length); + System.arraycopy(mData, 0, nvalues, 0, mData.length); + + mFieldNumbers = nkeys; + mData = nvalues; + } + + if (mSize - i != 0) { + System.arraycopy(mFieldNumbers, i, mFieldNumbers, i + 1, mSize - i); + System.arraycopy(mData, i, mData, i + 1, mSize - i); + } + + mFieldNumbers[i] = fieldNumber; + mData[i] = data; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this FieldArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + public boolean isEmpty() { + return size() == 0; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * FieldArray stores. + */ + public FieldData dataAt(int index) { + if (mGarbage) { + gc(); + } + + return mData[index]; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof FieldArray)) { + return false; + } + + FieldArray other = (FieldArray) o; + if (size() != other.size()) { // size() will call gc() if necessary. + return false; + } + return arrayEquals(mFieldNumbers, other.mFieldNumbers, mSize) && + arrayEquals(mData, other.mData, mSize); + } + + @Override + public int hashCode() { + if (mGarbage) { + gc(); + } + int result = 17; + for (int i = 0; i < mSize; i++) { + result = 31 * result + mFieldNumbers[i]; + result = 31 * result + mData[i].hashCode(); + } + return result; + } + + private int idealIntArraySize(int need) { + return idealByteArraySize(need * 4) / 4; + } + + private int idealByteArraySize(int need) { + for (int i = 4; i < 32; i++) + if (need <= (1 << i) - 12) + return (1 << i) - 12; + + return need; + } + + private int binarySearch(int value) { + int lo = 0; + int hi = mSize - 1; + + while (lo <= hi) { + int mid = (lo + hi) >>> 1; + int midVal = mFieldNumbers[mid]; + + if (midVal < value) { + lo = mid + 1; + } else if (midVal > value) { + hi = mid - 1; + } else { + return mid; // value found + } + } + return ~lo; // value not present + } + + private boolean arrayEquals(int[] a, int[] b, int size) { + for (int i = 0; i < size; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + private boolean arrayEquals(FieldData[] a, FieldData[] b, int size) { + for (int i = 0; i < size; i++) { + if (!a[i].equals(b[i])) { + return false; + } + } + return true; + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/FieldData.java b/javanano/src/main/java/com/google/protobuf/nano/FieldData.java new file mode 100644 index 00000000..e5b69aad --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/FieldData.java @@ -0,0 +1,190 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2014 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Stores unknown fields. These might be extensions or fields that the generated API doesn't + * know about yet. + */ +class FieldData { + private Extension<?, ?> cachedExtension; + private Object value; + /** The serialised values for this object. Will be cleared if getValue is called */ + private List<UnknownFieldData> unknownFieldData; + + <T> FieldData(Extension<?, T> extension, T newValue) { + cachedExtension = extension; + value = newValue; + } + + FieldData() { + unknownFieldData = new ArrayList<UnknownFieldData>(); + } + + void addUnknownField(UnknownFieldData unknownField) { + unknownFieldData.add(unknownField); + } + + UnknownFieldData getUnknownField(int index) { + if (unknownFieldData == null) { + return null; + } + if (index < unknownFieldData.size()) { + return unknownFieldData.get(index); + } + return null; + } + + int getUnknownFieldSize() { + if (unknownFieldData == null) { + return 0; + } + return unknownFieldData.size(); + } + + <T> T getValue(Extension<?, T> extension) { + if (value != null){ + if (cachedExtension != extension) { // Extension objects are singletons. + throw new IllegalStateException( + "Tried to getExtension with a differernt Extension."); + } + } else { + cachedExtension = extension; + value = extension.getValueFrom(unknownFieldData); + unknownFieldData = null; + } + return (T) value; + } + + <T> void setValue(Extension<?, T> extension, T newValue) { + cachedExtension = extension; + value = newValue; + unknownFieldData = null; + } + + int computeSerializedSize() { + int size = 0; + if (value != null) { + size = cachedExtension.computeSerializedSize(value); + } else { + for (UnknownFieldData unknownField : unknownFieldData) { + size += unknownField.computeSerializedSize(); + } + } + return size; + } + + void writeTo(CodedOutputByteBufferNano output) throws IOException { + if (value != null) { + cachedExtension.writeTo(value, output); + } else { + for (UnknownFieldData unknownField : unknownFieldData) { + unknownField.writeTo(output); + } + } + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof FieldData)) { + return false; + } + + FieldData other = (FieldData) o; + if (value != null && other.value != null) { + // If both objects have deserialized values, compare those. + // Since unknown fields are only compared if messages have generated equals methods + // we know this will be a meaningful comparison (not identity) for all values. + if (cachedExtension != other.cachedExtension) { // Extension objects are singletons. + return false; + } + if (!cachedExtension.clazz.isArray()) { + // Can't test (!cachedExtension.repeated) due to 'bytes' -> 'byte[]' + return value.equals(other.value); + } + if (value instanceof byte[]) { + return Arrays.equals((byte[]) value, (byte[]) other.value); + } else if (value instanceof int[]) { + return Arrays.equals((int[]) value, (int[]) other.value); + } else if (value instanceof long[]) { + return Arrays.equals((long[]) value, (long[]) other.value); + } else if (value instanceof float[]) { + return Arrays.equals((float[]) value, (float[]) other.value); + } else if (value instanceof double[]) { + return Arrays.equals((double[]) value, (double[]) other.value); + } else if (value instanceof boolean[]) { + return Arrays.equals((boolean[]) value, (boolean[]) other.value); + } else { + return Arrays.deepEquals((Object[]) value, (Object[]) other.value); + } + } + if (unknownFieldData != null && other.unknownFieldData != null) { + // If both objects have byte arrays compare those directly. + return unknownFieldData.equals(other.unknownFieldData); + } + try { + // As a last resort, serialize and compare the resulting byte arrays. + return Arrays.equals(toByteArray(), other.toByteArray()); + } catch (IOException e) { + // Should not happen. + throw new IllegalStateException(e); + } + } + + @Override + public int hashCode() { + int result = 17; + try { + // The only way to generate a consistent hash is to use the serialized form. + result = 31 * result + Arrays.hashCode(toByteArray()); + } catch (IOException e) { + // Should not happen. + throw new IllegalStateException(e); + } + return result; + } + + private byte[] toByteArray() throws IOException { + byte[] result = new byte[computeSerializedSize()]; + CodedOutputByteBufferNano output = CodedOutputByteBufferNano.newInstance(result); + writeTo(output); + return result; + } + +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java new file mode 100644 index 00000000..90ca11d5 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/InternalNano.java @@ -0,0 +1,333 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.UnsupportedEncodingException; +import java.util.Arrays; + +/** + * The classes contained within are used internally by the Protocol Buffer + * library and generated message implementations. They are public only because + * those generated messages do not reside in the {@code protobuf} package. + * Others should not use this class directly. + * + * @author kenton@google.com (Kenton Varda) + */ +public final class InternalNano { + + private InternalNano() {} + + /** + * An object to provide synchronization when lazily initializing static fields + * of {@link MessageNano} subclasses. + * <p> + * To enable earlier versions of ProGuard to inline short methods from a + * generated MessageNano subclass to the call sites, that class must not have + * a class initializer, which will be created if there is any static variable + * initializers. To lazily initialize the static variables in a thread-safe + * manner, the initialization code will synchronize on this object. + */ + public static final Object LAZY_INIT_LOCK = new Object(); + + /** + * Helper called by generated code to construct default values for string + * fields. + * <p> + * The protocol compiler does not actually contain a UTF-8 decoder -- it + * just pushes UTF-8-encoded text around without touching it. The one place + * where this presents a problem is when generating Java string literals. + * Unicode characters in the string literal would normally need to be encoded + * using a Unicode escape sequence, which would require decoding them. + * To get around this, protoc instead embeds the UTF-8 bytes into the + * generated code and leaves it to the runtime library to decode them. + * <p> + * It gets worse, though. If protoc just generated a byte array, like: + * new byte[] {0x12, 0x34, 0x56, 0x78} + * Java actually generates *code* which allocates an array and then fills + * in each value. This is much less efficient than just embedding the bytes + * directly into the bytecode. To get around this, we need another + * work-around. String literals are embedded directly, so protoc actually + * generates a string literal corresponding to the bytes. The easiest way + * to do this is to use the ISO-8859-1 character set, which corresponds to + * the first 256 characters of the Unicode range. Protoc can then use + * good old CEscape to generate the string. + * <p> + * So we have a string literal which represents a set of bytes which + * represents another string. This function -- stringDefaultValue -- + * converts from the generated string to the string we actually want. The + * generated code calls this automatically. + */ + public static String stringDefaultValue(String bytes) { + try { + return new String(bytes.getBytes("ISO-8859-1"), "UTF-8"); + } catch (UnsupportedEncodingException e) { + // This should never happen since all JVMs are required to implement + // both of the above character sets. + throw new IllegalStateException( + "Java VM does not support a standard character set.", e); + } + } + + /** + * Helper called by generated code to construct default values for bytes + * fields. + * <p> + * This is a lot like {@link #stringDefaultValue}, but for bytes fields. + * In this case we only need the second of the two hacks -- allowing us to + * embed raw bytes as a string literal with ISO-8859-1 encoding. + */ + public static byte[] bytesDefaultValue(String bytes) { + try { + return bytes.getBytes("ISO-8859-1"); + } catch (UnsupportedEncodingException e) { + // This should never happen since all JVMs are required to implement + // ISO-8859-1. + throw new IllegalStateException( + "Java VM does not support a standard character set.", e); + } + } + + /** + * Helper function to convert a string into UTF-8 while turning the + * UnsupportedEncodingException to a RuntimeException. + */ + public static byte[] copyFromUtf8(final String text) { + try { + return text.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported?"); + } + } + + /** + * Checks repeated int field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(int[] field1, int[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated long field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(long[] field1, long[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated float field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(float[] field1, float[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated double field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(double[] field1, double[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated boolean field equality; null-value and 0-length fields are + * considered equal. + */ + public static boolean equals(boolean[] field1, boolean[] field2) { + if (field1 == null || field1.length == 0) { + return field2 == null || field2.length == 0; + } else { + return Arrays.equals(field1, field2); + } + } + + /** + * Checks repeated bytes field equality. Only non-null elements are tested. + * Returns true if the two fields have the same sequence of non-null + * elements. Null-value fields and fields of any length with only null + * elements are considered equal. + */ + public static boolean equals(byte[][] field1, byte[][] field2) { + int index1 = 0; + int length1 = field1 == null ? 0 : field1.length; + int index2 = 0; + int length2 = field2 == null ? 0 : field2.length; + while (true) { + while (index1 < length1 && field1[index1] == null) { + index1++; + } + while (index2 < length2 && field2[index2] == null) { + index2++; + } + boolean atEndOf1 = index1 >= length1; + boolean atEndOf2 = index2 >= length2; + if (atEndOf1 && atEndOf2) { + // no more non-null elements to test in both arrays + return true; + } else if (atEndOf1 != atEndOf2) { + // one of the arrays have extra non-null elements + return false; + } else if (!Arrays.equals(field1[index1], field2[index2])) { + // element mismatch + return false; + } + index1++; + index2++; + } + } + + /** + * Checks repeated string/message field equality. Only non-null elements are + * tested. Returns true if the two fields have the same sequence of non-null + * elements. Null-value fields and fields of any length with only null + * elements are considered equal. + */ + public static boolean equals(Object[] field1, Object[] field2) { + int index1 = 0; + int length1 = field1 == null ? 0 : field1.length; + int index2 = 0; + int length2 = field2 == null ? 0 : field2.length; + while (true) { + while (index1 < length1 && field1[index1] == null) { + index1++; + } + while (index2 < length2 && field2[index2] == null) { + index2++; + } + boolean atEndOf1 = index1 >= length1; + boolean atEndOf2 = index2 >= length2; + if (atEndOf1 && atEndOf2) { + // no more non-null elements to test in both arrays + return true; + } else if (atEndOf1 != atEndOf2) { + // one of the arrays have extra non-null elements + return false; + } else if (!field1[index1].equals(field2[index2])) { + // element mismatch + return false; + } + index1++; + index2++; + } + } + + /** + * Computes the hash code of a repeated int field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(int[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated long field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(long[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated float field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(float[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated double field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(double[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated boolean field. Null-value and 0-length + * fields have the same hash code. + */ + public static int hashCode(boolean[] field) { + return field == null || field.length == 0 ? 0 : Arrays.hashCode(field); + } + + /** + * Computes the hash code of a repeated bytes field. Only the sequence of all + * non-null elements are used in the computation. Null-value fields and fields + * of any length with only null elements have the same hash code. + */ + public static int hashCode(byte[][] field) { + int result = 0; + for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) { + byte[] element = field[i]; + if (element != null) { + result = 31 * result + Arrays.hashCode(element); + } + } + return result; + } + + /** + * Computes the hash code of a repeated string/message field. Only the + * sequence of all non-null elements are used in the computation. Null-value + * fields and fields of any length with only null elements have the same hash + * code. + */ + public static int hashCode(Object[] field) { + int result = 0; + for (int i = 0, size = field == null ? 0 : field.length; i < size; i++) { + Object element = field[i]; + if (element != null) { + result = 31 * result + element.hashCode(); + } + } + return result; + } + +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/InvalidProtocolBufferNanoException.java b/javanano/src/main/java/com/google/protobuf/nano/InvalidProtocolBufferNanoException.java new file mode 100644 index 00000000..ff0af9df --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/InvalidProtocolBufferNanoException.java @@ -0,0 +1,93 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; + +/** + * Thrown when a protocol message being parsed is invalid in some way, + * e.g. it contains a malformed varint or a negative byte length. + * + * @author kenton@google.com Kenton Varda + */ +public class InvalidProtocolBufferNanoException extends IOException { + private static final long serialVersionUID = -1616151763072450476L; + + public InvalidProtocolBufferNanoException(final String description) { + super(description); + } + + static InvalidProtocolBufferNanoException truncatedMessage() { + return new InvalidProtocolBufferNanoException( + "While parsing a protocol message, the input ended unexpectedly " + + "in the middle of a field. This could mean either than the " + + "input has been truncated or that an embedded message " + + "misreported its own length."); + } + + static InvalidProtocolBufferNanoException negativeSize() { + return new InvalidProtocolBufferNanoException( + "CodedInputStream encountered an embedded string or message " + + "which claimed to have negative size."); + } + + static InvalidProtocolBufferNanoException malformedVarint() { + return new InvalidProtocolBufferNanoException( + "CodedInputStream encountered a malformed varint."); + } + + static InvalidProtocolBufferNanoException invalidTag() { + return new InvalidProtocolBufferNanoException( + "Protocol message contained an invalid tag (zero)."); + } + + static InvalidProtocolBufferNanoException invalidEndTag() { + return new InvalidProtocolBufferNanoException( + "Protocol message end-group tag did not match expected tag."); + } + + static InvalidProtocolBufferNanoException invalidWireType() { + return new InvalidProtocolBufferNanoException( + "Protocol message tag had invalid wire type."); + } + + static InvalidProtocolBufferNanoException recursionLimitExceeded() { + return new InvalidProtocolBufferNanoException( + "Protocol message had too many levels of nesting. May be malicious. " + + "Use CodedInputStream.setRecursionLimit() to increase the depth limit."); + } + + static InvalidProtocolBufferNanoException sizeLimitExceeded() { + return new InvalidProtocolBufferNanoException( + "Protocol message was too large. May be malicious. " + + "Use CodedInputStream.setSizeLimit() to increase the size limit."); + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java b/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java new file mode 100644 index 00000000..164f317f --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/MessageNano.java @@ -0,0 +1,190 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; +import java.util.Arrays; + +/** + * Abstract interface implemented by Protocol Message objects. + * + * @author wink@google.com Wink Saville + */ +public abstract class MessageNano { + protected volatile int cachedSize = -1; + + /** + * Get the number of bytes required to encode this message. + * Returns the cached size or calls getSerializedSize which + * sets the cached size. This is used internally when serializing + * so the size is only computed once. If a member is modified + * then this could be stale call getSerializedSize if in doubt. + */ + public int getCachedSize() { + if (cachedSize < 0) { + // getSerializedSize sets cachedSize + getSerializedSize(); + } + return cachedSize; + } + + /** + * Computes the number of bytes required to encode this message. + * The size is cached and the cached result can be retrieved + * using getCachedSize(). + */ + public int getSerializedSize() { + int size = computeSerializedSize(); + cachedSize = size; + return size; + } + + /** + * Computes the number of bytes required to encode this message. This does not update the + * cached size. + */ + protected int computeSerializedSize() { + // This is overridden if the generated message has serialized fields. + return 0; + } + + /** + * Serializes the message and writes it to {@code output}. + * + * @param output the output to receive the serialized form. + * @throws IOException if an error occurred writing to {@code output}. + */ + public void writeTo(CodedOutputByteBufferNano output) throws IOException { + // Does nothing by default. Overridden by subclasses which have data to write. + } + + /** + * Parse {@code input} as a message of this type and merge it with the + * message being built. + */ + public abstract MessageNano mergeFrom(CodedInputByteBufferNano input) throws IOException; + + /** + * Serialize to a byte array. + * @return byte array with the serialized data. + */ + public static final byte[] toByteArray(MessageNano msg) { + final byte[] result = new byte[msg.getSerializedSize()]; + toByteArray(msg, result, 0, result.length); + return result; + } + + /** + * Serialize to a byte array starting at offset through length. The + * method getSerializedSize must have been called prior to calling + * this method so the proper length is know. If an attempt to + * write more than length bytes OutOfSpaceException will be thrown + * and if length bytes are not written then IllegalStateException + * is thrown. + */ + public static final void toByteArray(MessageNano msg, byte[] data, int offset, int length) { + try { + final CodedOutputByteBufferNano output = + CodedOutputByteBufferNano.newInstance(data, offset, length); + msg.writeTo(output); + output.checkNoSpaceLeft(); + } catch (IOException e) { + throw new RuntimeException("Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. + */ + public static final <T extends MessageNano> T mergeFrom(T msg, final byte[] data) + throws InvalidProtocolBufferNanoException { + return mergeFrom(msg, data, 0, data.length); + } + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. + */ + public static final <T extends MessageNano> T mergeFrom(T msg, final byte[] data, + final int off, final int len) throws InvalidProtocolBufferNanoException { + try { + final CodedInputByteBufferNano input = + CodedInputByteBufferNano.newInstance(data, off, len); + msg.mergeFrom(input); + input.checkLastTagWas(0); + return msg; + } catch (InvalidProtocolBufferNanoException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException("Reading from a byte array threw an IOException (should " + + "never happen)."); + } + } + + /** + * Compares two {@code MessageNano}s and returns true if the message's are the same class and + * have serialized form equality (i.e. all of the field values are the same). + */ + public static final boolean messageNanoEquals(MessageNano a, MessageNano b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a.getClass() != b.getClass()) { + return false; + } + final int serializedSize = a.getSerializedSize(); + if (b.getSerializedSize() != serializedSize) { + return false; + } + final byte[] aByteArray = new byte[serializedSize]; + final byte[] bByteArray = new byte[serializedSize]; + toByteArray(a, aByteArray, 0, serializedSize); + toByteArray(b, bByteArray, 0, serializedSize); + return Arrays.equals(aByteArray, bByteArray); + } + + /** + * Returns a string that is (mostly) compatible with ProtoBuffer's TextFormat. Note that groups + * (which are deprecated) are not serialized with the correct field name. + * + * <p>This is implemented using reflection, so it is not especially fast nor is it guaranteed + * to find all fields if you have method removal turned on for proguard. + */ + @Override + public String toString() { + return MessageNanoPrinter.print(this); + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java b/javanano/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java new file mode 100644 index 00000000..4cca3d5e --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/MessageNanoPrinter.java @@ -0,0 +1,257 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Static helper methods for printing nano protos. + * + * @author flynn@google.com Andrew Flynn + */ +public final class MessageNanoPrinter { + // Do not allow instantiation + private MessageNanoPrinter() {} + + private static final String INDENT = " "; + private static final int MAX_STRING_LEN = 200; + + /** + * Returns an text representation of a MessageNano suitable for debugging. The returned string + * is mostly compatible with Protocol Buffer's TextFormat (as provided by non-nano protocol + * buffers) -- groups (which are deprecated) are output with an underscore name (e.g. foo_bar + * instead of FooBar) and will thus not parse. + * + * <p>Employs Java reflection on the given object and recursively prints primitive fields, + * groups, and messages.</p> + */ + public static <T extends MessageNano> String print(T message) { + if (message == null) { + return ""; + } + + StringBuffer buf = new StringBuffer(); + try { + print(null, message, new StringBuffer(), buf); + } catch (IllegalAccessException e) { + return "Error printing proto: " + e.getMessage(); + } catch (InvocationTargetException e) { + return "Error printing proto: " + e.getMessage(); + } + return buf.toString(); + } + + /** + * Function that will print the given message/field into the StringBuffer. + * Meant to be called recursively. + * + * @param identifier the identifier to use, or {@code null} if this is the root message to + * print. + * @param object the value to print. May in fact be a primitive value or byte array and not a + * message. + * @param indentBuf the indentation each line should begin with. + * @param buf the output buffer. + */ + private static void print(String identifier, Object object, + StringBuffer indentBuf, StringBuffer buf) throws IllegalAccessException, + InvocationTargetException { + if (object == null) { + // This can happen if... + // - we're about to print a message, String, or byte[], but it not present; + // - we're about to print a primitive, but "reftype" optional style is enabled, and + // the field is unset. + // In both cases the appropriate behavior is to output nothing. + } else if (object instanceof MessageNano) { // Nano proto message + int origIndentBufLength = indentBuf.length(); + if (identifier != null) { + buf.append(indentBuf).append(deCamelCaseify(identifier)).append(" <\n"); + indentBuf.append(INDENT); + } + Class<?> clazz = object.getClass(); + + // Proto fields follow one of two formats: + // + // 1) Public, non-static variables that do not begin or end with '_' + // Find and print these using declared public fields + for (Field field : clazz.getFields()) { + int modifiers = field.getModifiers(); + String fieldName = field.getName(); + + if ((modifiers & Modifier.PUBLIC) == Modifier.PUBLIC + && (modifiers & Modifier.STATIC) != Modifier.STATIC + && !fieldName.startsWith("_") + && !fieldName.endsWith("_")) { + Class<?> fieldType = field.getType(); + Object value = field.get(object); + + if (fieldType.isArray()) { + Class<?> arrayType = fieldType.getComponentType(); + + // bytes is special since it's not repeated, but is represented by an array + if (arrayType == byte.class) { + print(fieldName, value, indentBuf, buf); + } else { + int len = value == null ? 0 : Array.getLength(value); + for (int i = 0; i < len; i++) { + Object elem = Array.get(value, i); + print(fieldName, elem, indentBuf, buf); + } + } + } else { + print(fieldName, value, indentBuf, buf); + } + } + } + + // 2) Fields that are accessed via getter methods (when accessors + // mode is turned on) + // Find and print these using getter methods. + for (Method method : clazz.getMethods()) { + String name = method.getName(); + // Check for the setter accessor method since getters and hazzers both have + // non-proto-field name collisions (hashCode() and getSerializedSize()) + if (name.startsWith("set")) { + String subfieldName = name.substring(3); + + Method hazzer = null; + try { + hazzer = clazz.getMethod("has" + subfieldName); + } catch (NoSuchMethodException e) { + continue; + } + // If hazzer does't exist or returns false, no need to continue + if (!(Boolean) hazzer.invoke(object)) { + continue; + } + + Method getter = null; + try { + getter = clazz.getMethod("get" + subfieldName); + } catch (NoSuchMethodException e) { + continue; + } + + print(subfieldName, getter.invoke(object), indentBuf, buf); + } + } + if (identifier != null) { + indentBuf.setLength(origIndentBufLength); + buf.append(indentBuf).append(">\n"); + } + } else { + // Non-null primitive value + identifier = deCamelCaseify(identifier); + buf.append(indentBuf).append(identifier).append(": "); + if (object instanceof String) { + String stringMessage = sanitizeString((String) object); + buf.append("\"").append(stringMessage).append("\""); + } else if (object instanceof byte[]) { + appendQuotedBytes((byte[]) object, buf); + } else { + buf.append(object); + } + buf.append("\n"); + } + } + + /** + * Converts an identifier of the format "FieldName" into "field_name". + */ + private static String deCamelCaseify(String identifier) { + StringBuffer out = new StringBuffer(); + for (int i = 0; i < identifier.length(); i++) { + char currentChar = identifier.charAt(i); + if (i == 0) { + out.append(Character.toLowerCase(currentChar)); + } else if (Character.isUpperCase(currentChar)) { + out.append('_').append(Character.toLowerCase(currentChar)); + } else { + out.append(currentChar); + } + } + return out.toString(); + } + + /** + * Shortens and escapes the given string. + */ + private static String sanitizeString(String str) { + if (!str.startsWith("http") && str.length() > MAX_STRING_LEN) { + // Trim non-URL strings. + str = str.substring(0, MAX_STRING_LEN) + "[...]"; + } + return escapeString(str); + } + + /** + * Escape everything except for low ASCII code points. + */ + private static String escapeString(String str) { + int strLen = str.length(); + StringBuilder b = new StringBuilder(strLen); + for (int i = 0; i < strLen; i++) { + char original = str.charAt(i); + if (original >= ' ' && original <= '~' && original != '"' && original != '\'') { + b.append(original); + } else { + b.append(String.format("\\u%04x", (int) original)); + } + } + return b.toString(); + } + + /** + * Appends a quoted byte array to the provided {@code StringBuffer}. + */ + private static void appendQuotedBytes(byte[] bytes, StringBuffer builder) { + if (bytes == null) { + builder.append("\"\""); + return; + } + + builder.append('"'); + for (int i = 0; i < bytes.length; ++i) { + int ch = bytes[i] & 0xff; + if (ch == '\\' || ch == '"') { + builder.append('\\').append((char) ch); + } else if (ch >= 32 && ch < 127) { + builder.append((char) ch); + } else { + builder.append(String.format("\\%03o", ch)); + } + } + builder.append('"'); + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java b/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java new file mode 100644 index 00000000..2032e1a6 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/UnknownFieldData.java @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; +import java.util.Arrays; + +/** + * Stores unknown fields. These might be extensions or fields that the generated + * API doesn't know about yet. + * + * @author bduff@google.com (Brian Duff) + */ +final class UnknownFieldData { + + final int tag; + final byte[] bytes; + + UnknownFieldData(int tag, byte[] bytes) { + this.tag = tag; + this.bytes = bytes; + } + + int computeSerializedSize() { + int size = 0; + size += CodedOutputByteBufferNano.computeRawVarint32Size(tag); + size += bytes.length; + return size; + } + + void writeTo(CodedOutputByteBufferNano output) throws IOException { + output.writeRawVarint32(tag); + output.writeRawBytes(bytes); + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof UnknownFieldData)) { + return false; + } + + UnknownFieldData other = (UnknownFieldData) o; + return tag == other.tag && Arrays.equals(bytes, other.bytes); + } + + @Override + public int hashCode() { + int result = 17; + result = 31 * result + tag; + result = 31 * result + Arrays.hashCode(bytes); + return result; + } +} diff --git a/javanano/src/main/java/com/google/protobuf/nano/WireFormatNano.java b/javanano/src/main/java/com/google/protobuf/nano/WireFormatNano.java new file mode 100644 index 00000000..a3405e55 --- /dev/null +++ b/javanano/src/main/java/com/google/protobuf/nano/WireFormatNano.java @@ -0,0 +1,124 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import java.io.IOException; + +/** + * This class is used internally by the Protocol Buffer library and generated + * message implementations. It is public only because those generated messages + * do not reside in the {@code protobuf} package. Others should not use this + * class directly. + * + * This class contains constants and helper functions useful for dealing with + * the Protocol Buffer wire format. + * + * @author kenton@google.com Kenton Varda + */ +public final class WireFormatNano { + // Do not allow instantiation. + private WireFormatNano() {} + + static final int WIRETYPE_VARINT = 0; + static final int WIRETYPE_FIXED64 = 1; + static final int WIRETYPE_LENGTH_DELIMITED = 2; + static final int WIRETYPE_START_GROUP = 3; + static final int WIRETYPE_END_GROUP = 4; + static final int WIRETYPE_FIXED32 = 5; + + static final int TAG_TYPE_BITS = 3; + 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) { + return tag & TAG_TYPE_MASK; + } + + /** Given a tag value, determines the field number (the upper 29 bits). */ + public static int getTagFieldNumber(final int tag) { + return tag >>> TAG_TYPE_BITS; + } + + /** Makes a tag value given a field number and wire type. */ + static int makeTag(final int fieldNumber, final int wireType) { + return (fieldNumber << TAG_TYPE_BITS) | wireType; + } + + public static final int EMPTY_INT_ARRAY[] = {}; + public static final long EMPTY_LONG_ARRAY[] = {}; + public static final float EMPTY_FLOAT_ARRAY[] = {}; + public static final double EMPTY_DOUBLE_ARRAY[] = {}; + public static final boolean EMPTY_BOOLEAN_ARRAY[] = {}; + public static final String EMPTY_STRING_ARRAY[] = {}; + public static final byte[] EMPTY_BYTES_ARRAY[] = {}; + public static final byte[] EMPTY_BYTES = {}; + + /** + * Parses an unknown field. This implementation skips the field. + * + * <p>Generated messages will call this for unknown fields if the store_unknown_fields + * option is off. + * + * @return {@literal true} unless the tag is an end-group tag. + */ + public static boolean parseUnknownField( + final CodedInputByteBufferNano input, + final int tag) throws IOException { + return input.skipField(tag); + } + + /** + * Computes the array length of a repeated field. We assume that in the common case repeated + * fields are contiguously serialized but we still correctly handle interspersed values of a + * repeated field (but with extra allocations). + * + * Rewinds to current input position before returning. + * + * @param input stream input, pointing to the byte after the first tag + * @param tag repeated field tag just read + * @return length of array + * @throws IOException + */ + public static final int getRepeatedFieldArrayLength( + final CodedInputByteBufferNano input, + final int tag) throws IOException { + int arrayLength = 1; + int startPos = input.getPosition(); + input.skipField(tag); + while (input.readTag() == tag) { + input.skipField(tag); + arrayLength++; + } + input.rewindToPosition(startPos); + return arrayLength; + } + +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java new file mode 100644 index 00000000..5ac7edb6 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/NanoTest.java @@ -0,0 +1,3797 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2013 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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.nano; + +import com.google.protobuf.nano.CodedInputByteBufferNano; +import com.google.protobuf.nano.EnumClassNanoMultiple; +import com.google.protobuf.nano.EnumClassNanos; +import com.google.protobuf.nano.EnumValidity; +import com.google.protobuf.nano.EnumValidityAccessors; +import com.google.protobuf.nano.FileScopeEnumMultiple; +import com.google.protobuf.nano.FileScopeEnumRefNano; +import com.google.protobuf.nano.InternalNano; +import com.google.protobuf.nano.InvalidProtocolBufferNanoException; +import com.google.protobuf.nano.MessageNano; +import com.google.protobuf.nano.MessageScopeEnumRefNano; +import com.google.protobuf.nano.MultipleImportingNonMultipleNano1; +import com.google.protobuf.nano.MultipleImportingNonMultipleNano2; +import com.google.protobuf.nano.MultipleNameClashNano; +import com.google.protobuf.nano.NanoAccessorsOuterClass.TestNanoAccessors; +import com.google.protobuf.nano.NanoHasOuterClass.TestAllTypesNanoHas; +import com.google.protobuf.nano.NanoOuterClass; +import com.google.protobuf.nano.NanoOuterClass.TestAllTypesNano; +import com.google.protobuf.nano.NanoReferenceTypes; +import com.google.protobuf.nano.NanoRepeatedPackables; +import com.google.protobuf.nano.PackedExtensions; +import com.google.protobuf.nano.RepeatedExtensions; +import com.google.protobuf.nano.SingularExtensions; +import com.google.protobuf.nano.TestRepeatedMergeNano; +import com.google.protobuf.nano.UnittestMultipleNano; +import com.google.protobuf.nano.UnittestRecursiveNano.RecursiveMessageNano; +import com.google.protobuf.nano.UnittestSimpleNano.SimpleMessageNano; +import com.google.protobuf.nano.UnittestSingleNano.SingleMessageNano; +import com.google.protobuf.nano.testext.Extensions; +import com.google.protobuf.nano.testext.Extensions.AnotherMessage; +import com.google.protobuf.nano.testext.Extensions.MessageWithGroup; +import com.google.protobuf.nano.testimport.UnittestImportNano; + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.HashMap; + +/** + * Test nano runtime. + * + * @author ulas@google.com Ulas Kirazci + */ +public class NanoTest extends TestCase { + @Override + public void setUp() throws Exception { + } + + public void testSimpleMessageNano() throws Exception { + SimpleMessageNano msg = new SimpleMessageNano(); + assertEquals(123, msg.d); + assertEquals(null, msg.nestedMsg); + assertEquals(SimpleMessageNano.BAZ, msg.defaultNestedEnum); + + msg.d = 456; + assertEquals(456, msg.d); + + SimpleMessageNano.NestedMessage nestedMsg = new SimpleMessageNano.NestedMessage(); + nestedMsg.bb = 2; + assertEquals(2, nestedMsg.bb); + msg.nestedMsg = nestedMsg; + assertEquals(2, msg.nestedMsg.bb); + + msg.defaultNestedEnum = SimpleMessageNano.BAR; + assertEquals(SimpleMessageNano.BAR, msg.defaultNestedEnum); + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + SimpleMessageNano newMsg = SimpleMessageNano.parseFrom(result); + assertEquals(456, newMsg.d); + assertEquals(2, msg.nestedMsg.bb); + assertEquals(SimpleMessageNano.BAR, msg.defaultNestedEnum); + + msg.nestedMsg = null; + assertTrue(msgSerializedSize != msg.getSerializedSize()); + + msg.clear(); + assertEquals(0, msg.getSerializedSize()); + } + + public void testRecursiveMessageNano() throws Exception { + RecursiveMessageNano msg = new RecursiveMessageNano(); + assertTrue(msg.repeatedRecursiveMessageNano.length == 0); + + RecursiveMessageNano msg1 = new RecursiveMessageNano(); + msg1.id = 1; + assertEquals(1, msg1.id); + RecursiveMessageNano msg2 = new RecursiveMessageNano(); + msg2.id = 2; + RecursiveMessageNano msg3 = new RecursiveMessageNano(); + msg3.id = 3; + + RecursiveMessageNano.NestedMessage nestedMsg = new RecursiveMessageNano.NestedMessage(); + nestedMsg.a = msg1; + assertEquals(1, nestedMsg.a.id); + + msg.id = 0; + msg.nestedMessage = nestedMsg; + msg.optionalRecursiveMessageNano = msg2; + msg.repeatedRecursiveMessageNano = new RecursiveMessageNano[] { msg3 }; + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 16); + assertEquals(result.length, msgSerializedSize); + + RecursiveMessageNano newMsg = RecursiveMessageNano.parseFrom(result); + assertEquals(1, newMsg.repeatedRecursiveMessageNano.length); + + assertEquals(0, newMsg.id); + assertEquals(1, newMsg.nestedMessage.a.id); + assertEquals(2, newMsg.optionalRecursiveMessageNano.id); + assertEquals(3, newMsg.repeatedRecursiveMessageNano[0].id); + } + + public void testMessageNoFields() { + SingleMessageNano msg = new SingleMessageNano(); + assertEquals(0, msg.getSerializedSize()); + assertEquals(0, MessageNano.toByteArray(msg).length); + } + + public void testNanoRequiredInt32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.id = 123; + assertEquals(123, msg.id); + msg.clear().id = 456; + assertEquals(456, msg.id); + msg.clear(); + + msg.id = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 3); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.id); + } + + public void testNanoOptionalInt32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalInt32 = 123; + assertEquals(123, msg.optionalInt32); + msg.clear() + .optionalInt32 = 456; + assertEquals(456, msg.optionalInt32); + msg.clear(); + + msg.optionalInt32 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 5); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalInt32); + } + + public void testNanoOptionalInt64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalInt64 = 123; + assertEquals(123, msg.optionalInt64); + msg.clear() + .optionalInt64 = 456; + assertEquals(456, msg.optionalInt64); + msg.clear(); + assertEquals(0, msg.optionalInt64); + + msg.optionalInt64 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 5); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalInt64); + } + + public void testNanoOptionalUint32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalUint32 = 123; + assertEquals(123, msg.optionalUint32); + msg.clear() + .optionalUint32 = 456; + assertEquals(456, msg.optionalUint32); + msg.clear(); + assertEquals(0, msg.optionalUint32); + + msg.optionalUint32 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 5); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalUint32); + } + + public void testNanoOptionalUint64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalUint64 = 123; + assertEquals(123, msg.optionalUint64); + msg.clear() + .optionalUint64 = 456; + assertEquals(456, msg.optionalUint64); + msg.clear(); + assertEquals(0, msg.optionalUint64); + + msg.optionalUint64 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 5); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalUint64); + } + + public void testNanoOptionalSint32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalSint32 = 123; + assertEquals(123, msg.optionalSint32); + msg.clear() + .optionalSint32 = 456; + assertEquals(456, msg.optionalSint32); + msg.clear(); + assertEquals(0, msg.optionalSint32); + + msg.optionalSint32 = -123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(-123, newMsg.optionalSint32); + } + + public void testNanoOptionalSint64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalSint64 = 123; + assertEquals(123, msg.optionalSint64); + msg.clear() + .optionalSint64 = 456; + assertEquals(456, msg.optionalSint64); + msg.clear(); + assertEquals(0, msg.optionalSint64); + + msg.optionalSint64 = -123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(-123, newMsg.optionalSint64); + } + + public void testNanoOptionalFixed32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalFixed32 = 123; + assertEquals(123, msg.optionalFixed32); + msg.clear() + .optionalFixed32 = 456; + assertEquals(456, msg.optionalFixed32); + msg.clear(); + assertEquals(0, msg.optionalFixed32); + + msg.optionalFixed32 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalFixed32); + } + + public void testNanoOptionalFixed64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalFixed64 = 123; + assertEquals(123, msg.optionalFixed64); + msg.clear() + .optionalFixed64 = 456; + assertEquals(456, msg.optionalFixed64); + msg.clear(); + assertEquals(0, msg.optionalFixed64); + + msg.optionalFixed64 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 12); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalFixed64); + } + + public void testNanoOptionalSfixed32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalSfixed32 = 123; + assertEquals(123, msg.optionalSfixed32); + msg.clear() + .optionalSfixed32 = 456; + assertEquals(456, msg.optionalSfixed32); + msg.clear(); + assertEquals(0, msg.optionalSfixed32); + + msg.optionalSfixed32 = 123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(123, newMsg.optionalSfixed32); + } + + public void testNanoOptionalSfixed64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalSfixed64 = 123; + assertEquals(123, msg.optionalSfixed64); + msg.clear() + .optionalSfixed64 = 456; + assertEquals(456, msg.optionalSfixed64); + msg.clear(); + assertEquals(0, msg.optionalSfixed64); + + msg.optionalSfixed64 = -123; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 12); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(-123, newMsg.optionalSfixed64); + } + + public void testNanoOptionalFloat() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalFloat = 123f; + assertTrue(123.0f == msg.optionalFloat); + msg.clear() + .optionalFloat = 456.0f; + assertTrue(456.0f == msg.optionalFloat); + msg.clear(); + assertTrue(0.0f == msg.optionalFloat); + + msg.optionalFloat = -123.456f; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(-123.456f == newMsg.optionalFloat); + } + + public void testNanoOptionalDouble() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalDouble = 123; + assertTrue(123.0 == msg.optionalDouble); + msg.clear() + .optionalDouble = 456.0; + assertTrue(456.0 == msg.optionalDouble); + msg.clear(); + assertTrue(0.0 == msg.optionalDouble); + + msg.optionalDouble = -123.456; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 12); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(-123.456 == newMsg.optionalDouble); + } + + public void testNanoOptionalBool() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalBool = true; + assertTrue(msg.optionalBool); + msg.clear() + .optionalBool = true; + assertTrue(msg.optionalBool); + msg.clear(); + assertFalse(msg.optionalBool); + + msg.optionalBool = true; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 5); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalBool); + } + + public void testNanoOptionalString() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalString = "hello"; + assertEquals("hello", msg.optionalString); + msg.clear(); + assertTrue(msg.optionalString.isEmpty()); + msg.clear() + .optionalString = "hello2"; + assertEquals("hello2", msg.optionalString); + msg.clear(); + assertTrue(msg.optionalString.isEmpty()); + + msg.optionalString = "bye"; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalString != null); + assertEquals("bye", newMsg.optionalString); + } + + public void testNanoOptionalBytes() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertFalse(msg.optionalBytes.length > 0); + msg.optionalBytes = InternalNano.copyFromUtf8("hello"); + assertTrue(msg.optionalBytes.length > 0); + assertEquals("hello", new String(msg.optionalBytes, "UTF-8")); + msg.clear(); + assertFalse(msg.optionalBytes.length > 0); + msg.clear() + .optionalBytes = InternalNano.copyFromUtf8("hello"); + assertTrue(msg.optionalBytes.length > 0); + msg.clear(); + assertFalse(msg.optionalBytes.length > 0); + + msg.optionalBytes = InternalNano.copyFromUtf8("bye"); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalBytes.length > 0); + assertEquals("bye", new String(newMsg.optionalBytes, "UTF-8")); + } + + public void testNanoOptionalGroup() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + TestAllTypesNano.OptionalGroup grp = new TestAllTypesNano.OptionalGroup(); + grp.a = 1; + assertFalse(msg.optionalGroup != null); + msg.optionalGroup = grp; + assertTrue(msg.optionalGroup != null); + assertEquals(1, msg.optionalGroup.a); + msg.clear(); + assertFalse(msg.optionalGroup != null); + msg.clear() + .optionalGroup = new TestAllTypesNano.OptionalGroup(); + msg.optionalGroup.a = 2; + assertTrue(msg.optionalGroup != null); + msg.clear(); + assertFalse(msg.optionalGroup != null); + + msg.optionalGroup = grp; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalGroup != null); + assertEquals(1, newMsg.optionalGroup.a); + } + + public void testNanoOptionalGroupWithUnknownFieldsEnabled() throws Exception { + MessageWithGroup msg = new MessageWithGroup(); + MessageWithGroup.Group grp = new MessageWithGroup.Group(); + grp.a = 1; + msg.group = grp; + byte [] serialized = MessageNano.toByteArray(msg); + + MessageWithGroup parsed = MessageWithGroup.parseFrom(serialized); + assertEquals(1, parsed.group.a); + + byte [] serialized2 = MessageNano.toByteArray(parsed); + assertEquals(serialized.length, serialized2.length); + MessageWithGroup parsed2 = MessageWithGroup.parseFrom(serialized2); + assertEquals(1, parsed2.group.a); + } + + public void testNanoOptionalNestedMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + TestAllTypesNano.NestedMessage nestedMsg = new TestAllTypesNano.NestedMessage(); + nestedMsg.bb = 1; + assertFalse(msg.optionalNestedMessage != null); + msg.optionalNestedMessage = nestedMsg; + assertTrue(msg.optionalNestedMessage != null); + assertEquals(1, msg.optionalNestedMessage.bb); + msg.clear(); + assertFalse(msg.optionalNestedMessage != null); + msg.clear() + .optionalNestedMessage = new TestAllTypesNano.NestedMessage(); + msg.optionalNestedMessage.bb = 2; + assertTrue(msg.optionalNestedMessage != null); + msg.clear(); + assertFalse(msg.optionalNestedMessage != null); + + msg.optionalNestedMessage = nestedMsg; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalNestedMessage != null); + assertEquals(1, newMsg.optionalNestedMessage.bb); + } + + public void testNanoOptionalForeignMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + NanoOuterClass.ForeignMessageNano nestedMsg = new NanoOuterClass.ForeignMessageNano(); + nestedMsg.c = 1; + assertFalse(msg.optionalForeignMessage != null); + msg.optionalForeignMessage = nestedMsg; + assertTrue(msg.optionalForeignMessage != null); + assertEquals(1, msg.optionalForeignMessage.c); + msg.clear(); + assertFalse(msg.optionalForeignMessage != null); + msg.clear() + .optionalForeignMessage = new NanoOuterClass.ForeignMessageNano(); + msg.optionalForeignMessage.c = 2; + assertTrue(msg.optionalForeignMessage != null); + msg.clear(); + assertFalse(msg.optionalForeignMessage != null); + + msg.optionalForeignMessage = nestedMsg; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalForeignMessage != null); + assertEquals(1, newMsg.optionalForeignMessage.c); + } + + public void testNanoOptionalImportMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + UnittestImportNano.ImportMessageNano nestedMsg = new UnittestImportNano.ImportMessageNano(); + nestedMsg.d = 1; + assertFalse(msg.optionalImportMessage != null); + msg.optionalImportMessage = nestedMsg; + assertTrue(msg.optionalImportMessage != null); + assertEquals(1, msg.optionalImportMessage.d); + msg.clear(); + assertFalse(msg.optionalImportMessage != null); + msg.clear() + .optionalImportMessage = new UnittestImportNano.ImportMessageNano(); + msg.optionalImportMessage.d = 2; + assertTrue(msg.optionalImportMessage != null); + msg.clear(); + assertFalse(msg.optionalImportMessage != null); + + msg.optionalImportMessage = nestedMsg; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalImportMessage != null); + assertEquals(1, newMsg.optionalImportMessage.d); + } + + public void testNanoOptionalNestedEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalNestedEnum = TestAllTypesNano.BAR; + assertEquals(TestAllTypesNano.BAR, msg.optionalNestedEnum); + msg.clear() + .optionalNestedEnum = TestAllTypesNano.BAZ; + assertEquals(TestAllTypesNano.BAZ, msg.optionalNestedEnum); + msg.clear(); + assertEquals(TestAllTypesNano.FOO, msg.optionalNestedEnum); + + msg.optionalNestedEnum = TestAllTypesNano.BAR; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(TestAllTypesNano.BAR, newMsg.optionalNestedEnum); + } + + public void testNanoOptionalForeignEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalForeignEnum = NanoOuterClass.FOREIGN_NANO_BAR; + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, msg.optionalForeignEnum); + msg.clear() + .optionalForeignEnum = NanoOuterClass.FOREIGN_NANO_BAZ; + assertEquals(NanoOuterClass.FOREIGN_NANO_BAZ, msg.optionalForeignEnum); + msg.clear(); + assertEquals(NanoOuterClass.FOREIGN_NANO_FOO, msg.optionalForeignEnum); + + msg.optionalForeignEnum = NanoOuterClass.FOREIGN_NANO_BAR; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, newMsg.optionalForeignEnum); + } + + public void testNanoOptionalImportEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalImportEnum = UnittestImportNano.IMPORT_NANO_BAR; + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, msg.optionalImportEnum); + msg.clear() + .optionalImportEnum = UnittestImportNano.IMPORT_NANO_BAZ; + assertEquals(UnittestImportNano.IMPORT_NANO_BAZ, msg.optionalImportEnum); + msg.clear(); + assertEquals(UnittestImportNano.IMPORT_NANO_FOO, msg.optionalImportEnum); + + msg.optionalImportEnum = UnittestImportNano.IMPORT_NANO_BAR; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, newMsg.optionalImportEnum); + } + + public void testNanoOptionalStringPiece() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalStringPiece = "hello"; + assertEquals("hello", msg.optionalStringPiece); + msg.clear(); + assertTrue(msg.optionalStringPiece.isEmpty()); + msg.clear() + .optionalStringPiece = "hello2"; + assertEquals("hello2", msg.optionalStringPiece); + msg.clear(); + assertTrue(msg.optionalStringPiece.isEmpty()); + + msg.optionalStringPiece = "bye"; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalStringPiece != null); + assertEquals("bye", newMsg.optionalStringPiece); + } + + public void testNanoOptionalCord() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalCord = "hello"; + assertEquals("hello", msg.optionalCord); + msg.clear(); + assertTrue(msg.optionalCord.isEmpty()); + msg.clear() + .optionalCord = "hello2"; + assertEquals("hello2", msg.optionalCord); + msg.clear(); + assertTrue(msg.optionalCord.isEmpty()); + + msg.optionalCord = "bye"; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertTrue(newMsg.optionalCord != null); + assertEquals("bye", newMsg.optionalCord); + } + + public void testNanoRepeatedInt32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedInt32.length); + msg.repeatedInt32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedInt32[1]); + assertEquals(456, msg.repeatedInt32[2]); + msg.clear(); + assertEquals(0, msg.repeatedInt32.length); + msg.clear() + .repeatedInt32 = new int[] { 456 }; + assertEquals(1, msg.repeatedInt32.length); + assertEquals(456, msg.repeatedInt32[0]); + msg.clear(); + assertEquals(0, msg.repeatedInt32.length); + + // Test 1 entry + msg.clear() + .repeatedInt32 = new int[] { 123 }; + assertEquals(1, msg.repeatedInt32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedInt32.length); + assertEquals(123, newMsg.repeatedInt32[0]); + + // Test 2 entries + msg.clear() + .repeatedInt32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedInt32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedInt32.length); + assertEquals(123, newMsg.repeatedInt32[0]); + assertEquals(456, newMsg.repeatedInt32[1]); + } + + public void testNanoRepeatedInt64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedInt64.length); + msg.repeatedInt64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedInt64[1]); + assertEquals(456, msg.repeatedInt64[2]); + msg.clear(); + assertEquals(0, msg.repeatedInt64.length); + msg.clear() + .repeatedInt64 = new long[] { 456 }; + assertEquals(1, msg.repeatedInt64.length); + assertEquals(456, msg.repeatedInt64[0]); + msg.clear(); + assertEquals(0, msg.repeatedInt64.length); + + // Test 1 entry + msg.clear() + .repeatedInt64 = new long[] { 123 }; + assertEquals(1, msg.repeatedInt64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedInt64.length); + assertEquals(123, newMsg.repeatedInt64[0]); + + // Test 2 entries + msg.clear() + .repeatedInt64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedInt64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedInt64.length); + assertEquals(123, newMsg.repeatedInt64[0]); + assertEquals(456, newMsg.repeatedInt64[1]); + } + + public void testNanoRepeatedUint32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedUint32.length); + msg.repeatedUint32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedUint32[1]); + assertEquals(456, msg.repeatedUint32[2]); + msg.clear(); + assertEquals(0, msg.repeatedUint32.length); + msg.clear() + .repeatedUint32 = new int[] { 456 }; + assertEquals(1, msg.repeatedUint32.length); + assertEquals(456, msg.repeatedUint32[0]); + msg.clear(); + assertEquals(0, msg.repeatedUint32.length); + + // Test 1 entry + msg.clear() + .repeatedUint32 = new int[] { 123 }; + assertEquals(1, msg.repeatedUint32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedUint32.length); + assertEquals(123, newMsg.repeatedUint32[0]); + + // Test 2 entries + msg.clear() + .repeatedUint32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedUint32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedUint32.length); + assertEquals(123, newMsg.repeatedUint32[0]); + assertEquals(456, newMsg.repeatedUint32[1]); + } + + public void testNanoRepeatedUint64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedUint64.length); + msg.repeatedUint64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedUint64[1]); + assertEquals(456, msg.repeatedUint64[2]); + msg.clear(); + assertEquals(0, msg.repeatedUint64.length); + msg.clear() + .repeatedUint64 = new long[] { 456 }; + assertEquals(1, msg.repeatedUint64.length); + assertEquals(456, msg.repeatedUint64[0]); + msg.clear(); + assertEquals(0, msg.repeatedUint64.length); + + // Test 1 entry + msg.clear() + .repeatedUint64 = new long[] { 123 }; + assertEquals(1, msg.repeatedUint64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedUint64.length); + assertEquals(123, newMsg.repeatedUint64[0]); + + // Test 2 entries + msg.clear() + .repeatedUint64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedUint64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedUint64.length); + assertEquals(123, newMsg.repeatedUint64[0]); + assertEquals(456, newMsg.repeatedUint64[1]); + } + + public void testNanoRepeatedSint32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedSint32.length); + msg.repeatedSint32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedSint32[1]); + assertEquals(456, msg.repeatedSint32[2]); + msg.clear(); + assertEquals(0, msg.repeatedSint32.length); + msg.clear() + .repeatedSint32 = new int[] { 456 }; + assertEquals(1, msg.repeatedSint32.length); + assertEquals(456, msg.repeatedSint32[0]); + msg.clear(); + assertEquals(0, msg.repeatedSint32.length); + + // Test 1 entry + msg.clear() + .repeatedSint32 = new int[] { 123 }; + assertEquals(1, msg.repeatedSint32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 7); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedSint32.length); + assertEquals(123, newMsg.repeatedSint32[0]); + + // Test 2 entries + msg.clear() + .repeatedSint32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedSint32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedSint32.length); + assertEquals(123, newMsg.repeatedSint32[0]); + assertEquals(456, newMsg.repeatedSint32[1]); + } + + public void testNanoRepeatedSint64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedSint64.length); + msg.repeatedSint64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedSint64[1]); + assertEquals(456, msg.repeatedSint64[2]); + msg.clear(); + assertEquals(0, msg.repeatedSint64.length); + msg.clear() + .repeatedSint64 = new long[] { 456 }; + assertEquals(1, msg.repeatedSint64.length); + assertEquals(456, msg.repeatedSint64[0]); + msg.clear(); + assertEquals(0, msg.repeatedSint64.length); + + // Test 1 entry + msg.clear() + .repeatedSint64 = new long[] { 123 }; + assertEquals(1, msg.repeatedSint64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 7); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedSint64.length); + assertEquals(123, newMsg.repeatedSint64[0]); + + // Test 2 entries + msg.clear() + .repeatedSint64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedSint64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedSint64.length); + assertEquals(123, newMsg.repeatedSint64[0]); + assertEquals(456, newMsg.repeatedSint64[1]); + } + + public void testNanoRepeatedFixed32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedFixed32.length); + msg.repeatedFixed32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedFixed32[1]); + assertEquals(456, msg.repeatedFixed32[2]); + msg.clear(); + assertEquals(0, msg.repeatedFixed32.length); + msg.clear() + .repeatedFixed32 = new int[] { 456 }; + assertEquals(1, msg.repeatedFixed32.length); + assertEquals(456, msg.repeatedFixed32[0]); + msg.clear(); + assertEquals(0, msg.repeatedFixed32.length); + + // Test 1 entry + msg.clear() + .repeatedFixed32 = new int[] { 123 }; + assertEquals(1, msg.repeatedFixed32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedFixed32.length); + assertEquals(123, newMsg.repeatedFixed32[0]); + + // Test 2 entries + msg.clear() + .repeatedFixed32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedFixed32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 15); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedFixed32.length); + assertEquals(123, newMsg.repeatedFixed32[0]); + assertEquals(456, newMsg.repeatedFixed32[1]); + } + + public void testNanoRepeatedFixed64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedFixed64.length); + msg.repeatedFixed64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedFixed64[1]); + assertEquals(456, msg.repeatedFixed64[2]); + msg.clear(); + assertEquals(0, msg.repeatedFixed64.length); + msg.clear() + .repeatedFixed64 = new long[] { 456 }; + assertEquals(1, msg.repeatedFixed64.length); + assertEquals(456, msg.repeatedFixed64[0]); + msg.clear(); + assertEquals(0, msg.repeatedFixed64.length); + + // Test 1 entry + msg.clear() + .repeatedFixed64 = new long[] { 123 }; + assertEquals(1, msg.repeatedFixed64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 13); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedFixed64.length); + assertEquals(123, newMsg.repeatedFixed64[0]); + + // Test 2 entries + msg.clear() + .repeatedFixed64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedFixed64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 23); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedFixed64.length); + assertEquals(123, newMsg.repeatedFixed64[0]); + assertEquals(456, newMsg.repeatedFixed64[1]); + } + + public void testNanoRepeatedSfixed32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedSfixed32.length); + msg.repeatedSfixed32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedSfixed32[1]); + assertEquals(456, msg.repeatedSfixed32[2]); + msg.clear(); + assertEquals(0, msg.repeatedSfixed32.length); + msg.clear() + .repeatedSfixed32 = new int[] { 456 }; + assertEquals(1, msg.repeatedSfixed32.length); + assertEquals(456, msg.repeatedSfixed32[0]); + msg.clear(); + assertEquals(0, msg.repeatedSfixed32.length); + + // Test 1 entry + msg.clear() + .repeatedSfixed32 = new int[] { 123 }; + assertEquals(1, msg.repeatedSfixed32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedSfixed32.length); + assertEquals(123, newMsg.repeatedSfixed32[0]); + + // Test 2 entries + msg.clear() + .repeatedSfixed32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedSfixed32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 15); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedSfixed32.length); + assertEquals(123, newMsg.repeatedSfixed32[0]); + assertEquals(456, newMsg.repeatedSfixed32[1]); + } + + public void testNanoRepeatedSfixed64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedSfixed64.length); + msg.repeatedSfixed64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedSfixed64[1]); + assertEquals(456, msg.repeatedSfixed64[2]); + msg.clear(); + assertEquals(0, msg.repeatedSfixed64.length); + msg.clear() + .repeatedSfixed64 = new long[] { 456 }; + assertEquals(1, msg.repeatedSfixed64.length); + assertEquals(456, msg.repeatedSfixed64[0]); + msg.clear(); + assertEquals(0, msg.repeatedSfixed64.length); + + // Test 1 entry + msg.clear() + .repeatedSfixed64 = new long[] { 123 }; + assertEquals(1, msg.repeatedSfixed64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 13); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedSfixed64.length); + assertEquals(123, newMsg.repeatedSfixed64[0]); + + // Test 2 entries + msg.clear() + .repeatedSfixed64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedSfixed64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 23); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedSfixed64.length); + assertEquals(123, newMsg.repeatedSfixed64[0]); + assertEquals(456, newMsg.repeatedSfixed64[1]); + } + + public void testNanoRepeatedFloat() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedFloat.length); + msg.repeatedFloat = new float[] { 123f, 789f, 456f }; + assertEquals(789f, msg.repeatedFloat[1]); + assertEquals(456f, msg.repeatedFloat[2]); + msg.clear(); + assertEquals(0, msg.repeatedFloat.length); + msg.clear() + .repeatedFloat = new float[] { 456f }; + assertEquals(1, msg.repeatedFloat.length); + assertEquals(456f, msg.repeatedFloat[0]); + msg.clear(); + assertEquals(0, msg.repeatedFloat.length); + + // Test 1 entry + msg.clear() + .repeatedFloat = new float[] { 123f }; + assertEquals(1, msg.repeatedFloat.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedFloat.length); + assertEquals(123f, newMsg.repeatedFloat[0]); + + // Test 2 entries + msg.clear() + .repeatedFloat = new float[] { 123f, 456f }; + assertEquals(2, msg.repeatedFloat.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 15); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedFloat.length); + assertEquals(123f, newMsg.repeatedFloat[0]); + assertEquals(456f, newMsg.repeatedFloat[1]); + } + + public void testNanoRepeatedDouble() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedDouble.length); + msg.repeatedDouble = new double[] { 123.0, 789.0, 456.0 }; + assertEquals(789.0, msg.repeatedDouble[1]); + assertEquals(456.0, msg.repeatedDouble[2]); + msg.clear(); + assertEquals(0, msg.repeatedDouble.length); + msg.clear() + .repeatedDouble = new double[] { 456.0 }; + assertEquals(1, msg.repeatedDouble.length); + assertEquals(456.0, msg.repeatedDouble[0]); + msg.clear(); + assertEquals(0, msg.repeatedDouble.length); + + // Test 1 entry + msg.clear() + .repeatedDouble = new double[] { 123.0 }; + assertEquals(1, msg.repeatedDouble.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 13); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedDouble.length); + assertEquals(123.0, newMsg.repeatedDouble[0]); + + // Test 2 entries + msg.clear() + .repeatedDouble = new double[] { 123.0, 456.0 }; + assertEquals(2, msg.repeatedDouble.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 23); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedDouble.length); + assertEquals(123.0, newMsg.repeatedDouble[0]); + assertEquals(456.0, newMsg.repeatedDouble[1]); + } + + public void testNanoRepeatedBool() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedBool.length); + msg.repeatedBool = new boolean[] { false, true, false }; + assertTrue(msg.repeatedBool[1]); + assertFalse(msg.repeatedBool[2]); + msg.clear(); + assertEquals(0, msg.repeatedBool.length); + msg.clear() + .repeatedBool = new boolean[] { true }; + assertEquals(1, msg.repeatedBool.length); + assertTrue(msg.repeatedBool[0]); + msg.clear(); + assertEquals(0, msg.repeatedBool.length); + + // Test 1 entry + msg.clear() + .repeatedBool = new boolean[] { false }; + assertEquals(1, msg.repeatedBool.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedBool.length); + assertFalse(newMsg.repeatedBool[0]); + + // Test 2 entries + msg.clear() + .repeatedBool = new boolean[] { true, false }; + assertEquals(2, msg.repeatedBool.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedBool.length); + assertTrue(newMsg.repeatedBool[0]); + assertFalse(newMsg.repeatedBool[1]); + } + + public void testNanoRepeatedString() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedString.length); + msg.repeatedString = new String[] { "hello", "bye", "boo" }; + assertEquals("bye", msg.repeatedString[1]); + assertEquals("boo", msg.repeatedString[2]); + msg.clear(); + assertEquals(0, msg.repeatedString.length); + msg.clear() + .repeatedString = new String[] { "boo" }; + assertEquals(1, msg.repeatedString.length); + assertEquals("boo", msg.repeatedString[0]); + msg.clear(); + assertEquals(0, msg.repeatedString.length); + + // Test 1 entry + msg.clear() + .repeatedString = new String[] { "" }; + assertEquals(1, msg.repeatedString.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedString.length); + assertTrue(newMsg.repeatedString[0].isEmpty()); + + // Test 2 entries + msg.clear() + .repeatedString = new String[] { "hello", "world" }; + assertEquals(2, msg.repeatedString.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 19); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedString.length); + assertEquals("hello", newMsg.repeatedString[0]); + assertEquals("world", newMsg.repeatedString[1]); + } + + public void testNanoRepeatedBytes() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedBytes.length); + msg.repeatedBytes = new byte[][] { + InternalNano.copyFromUtf8("hello"), + InternalNano.copyFromUtf8("bye"), + InternalNano.copyFromUtf8("boo") + }; + assertEquals("bye", new String(msg.repeatedBytes[1], "UTF-8")); + assertEquals("boo", new String(msg.repeatedBytes[2], "UTF-8")); + msg.clear(); + assertEquals(0, msg.repeatedBytes.length); + msg.clear() + .repeatedBytes = new byte[][] { InternalNano.copyFromUtf8("boo") }; + assertEquals(1, msg.repeatedBytes.length); + assertEquals("boo", new String(msg.repeatedBytes[0], "UTF-8")); + msg.clear(); + assertEquals(0, msg.repeatedBytes.length); + + // Test 1 entry + msg.clear() + .repeatedBytes = new byte[][] { InternalNano.copyFromUtf8("") }; + assertEquals(1, msg.repeatedBytes.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedBytes.length); + assertTrue(newMsg.repeatedBytes[0].length == 0); + + // Test 2 entries + msg.clear() + .repeatedBytes = new byte[][] { + InternalNano.copyFromUtf8("hello"), + InternalNano.copyFromUtf8("world") + }; + assertEquals(2, msg.repeatedBytes.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 19); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedBytes.length); + assertEquals("hello", new String(newMsg.repeatedBytes[0], "UTF-8")); + assertEquals("world", new String(newMsg.repeatedBytes[1], "UTF-8")); + } + + public void testNanoRepeatedGroup() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + TestAllTypesNano.RepeatedGroup group0 = + new TestAllTypesNano.RepeatedGroup(); + group0.a = 0; + TestAllTypesNano.RepeatedGroup group1 = + new TestAllTypesNano.RepeatedGroup(); + group1.a = 1; + TestAllTypesNano.RepeatedGroup group2 = + new TestAllTypesNano.RepeatedGroup(); + group2.a = 2; + + msg.repeatedGroup = new TestAllTypesNano.RepeatedGroup[] { group0, group1, group2 }; + assertEquals(3, msg.repeatedGroup.length); + assertEquals(0, msg.repeatedGroup[0].a); + assertEquals(1, msg.repeatedGroup[1].a); + assertEquals(2, msg.repeatedGroup[2].a); + msg.clear(); + assertEquals(0, msg.repeatedGroup.length); + msg.clear() + .repeatedGroup = new TestAllTypesNano.RepeatedGroup[] { group1 }; + assertEquals(1, msg.repeatedGroup.length); + assertEquals(1, msg.repeatedGroup[0].a); + msg.clear(); + assertEquals(0, msg.repeatedGroup.length); + + // Test 1 entry + msg.clear() + .repeatedGroup = new TestAllTypesNano.RepeatedGroup[] { group0 }; + assertEquals(1, msg.repeatedGroup.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 7); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedGroup.length); + assertEquals(0, newMsg.repeatedGroup[0].a); + + // Test 2 entries + msg.clear() + .repeatedGroup = new TestAllTypesNano.RepeatedGroup[] { group0, group1 }; + assertEquals(2, msg.repeatedGroup.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 14); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedGroup.length); + assertEquals(0, newMsg.repeatedGroup[0].a); + assertEquals(1, newMsg.repeatedGroup[1].a); + } + + public void testNanoRepeatedNestedMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + TestAllTypesNano.NestedMessage nestedMsg0 = + new TestAllTypesNano.NestedMessage(); + nestedMsg0.bb = 0; + TestAllTypesNano.NestedMessage nestedMsg1 = + new TestAllTypesNano.NestedMessage(); + nestedMsg1.bb = 1; + TestAllTypesNano.NestedMessage nestedMsg2 = + new TestAllTypesNano.NestedMessage(); + nestedMsg2.bb = 2; + + msg.repeatedNestedMessage = + new TestAllTypesNano.NestedMessage[] { nestedMsg0, nestedMsg1, nestedMsg2 }; + assertEquals(3, msg.repeatedNestedMessage.length); + assertEquals(0, msg.repeatedNestedMessage[0].bb); + assertEquals(1, msg.repeatedNestedMessage[1].bb); + assertEquals(2, msg.repeatedNestedMessage[2].bb); + msg.clear(); + assertEquals(0, msg.repeatedNestedMessage.length); + msg.clear() + .repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { nestedMsg1 }; + assertEquals(1, msg.repeatedNestedMessage.length); + assertEquals(1, msg.repeatedNestedMessage[0].bb); + msg.clear(); + assertEquals(0, msg.repeatedNestedMessage.length); + + // Test 1 entry + msg.clear() + .repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { nestedMsg0 }; + assertEquals(1, msg.repeatedNestedMessage.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedNestedMessage.length); + assertEquals(0, newMsg.repeatedNestedMessage[0].bb); + + // Test 2 entries + msg.clear() + .repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { nestedMsg0, nestedMsg1 }; + assertEquals(2, msg.repeatedNestedMessage.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedNestedMessage.length); + assertEquals(0, newMsg.repeatedNestedMessage[0].bb); + assertEquals(1, newMsg.repeatedNestedMessage[1].bb); + } + + public void testNanoRepeatedForeignMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + NanoOuterClass.ForeignMessageNano foreignMsg0 = + new NanoOuterClass.ForeignMessageNano(); + foreignMsg0.c = 0; + NanoOuterClass.ForeignMessageNano foreignMsg1 = + new NanoOuterClass.ForeignMessageNano(); + foreignMsg1.c = 1; + NanoOuterClass.ForeignMessageNano foreignMsg2 = + new NanoOuterClass.ForeignMessageNano(); + foreignMsg2.c = 2; + + msg.repeatedForeignMessage = + new NanoOuterClass.ForeignMessageNano[] { foreignMsg0, foreignMsg1, foreignMsg2 }; + assertEquals(3, msg.repeatedForeignMessage.length); + assertEquals(0, msg.repeatedForeignMessage[0].c); + assertEquals(1, msg.repeatedForeignMessage[1].c); + assertEquals(2, msg.repeatedForeignMessage[2].c); + msg.clear(); + assertEquals(0, msg.repeatedForeignMessage.length); + msg.clear() + .repeatedForeignMessage = new NanoOuterClass.ForeignMessageNano[] { foreignMsg1 }; + assertEquals(1, msg.repeatedForeignMessage.length); + assertEquals(1, msg.repeatedForeignMessage[0].c); + msg.clear(); + assertEquals(0, msg.repeatedForeignMessage.length); + + // Test 1 entry + msg.clear() + .repeatedForeignMessage = new NanoOuterClass.ForeignMessageNano[] { foreignMsg0 }; + assertEquals(1, msg.repeatedForeignMessage.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedForeignMessage.length); + assertEquals(0, newMsg.repeatedForeignMessage[0].c); + + // Test 2 entries + msg.clear() + .repeatedForeignMessage = new NanoOuterClass.ForeignMessageNano[] { foreignMsg0, foreignMsg1 }; + assertEquals(2, msg.repeatedForeignMessage.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedForeignMessage.length); + assertEquals(0, newMsg.repeatedForeignMessage[0].c); + assertEquals(1, newMsg.repeatedForeignMessage[1].c); + } + + public void testNanoRepeatedImportMessage() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + UnittestImportNano.ImportMessageNano foreignMsg0 = + new UnittestImportNano.ImportMessageNano(); + foreignMsg0.d = 0; + UnittestImportNano.ImportMessageNano foreignMsg1 = + new UnittestImportNano.ImportMessageNano(); + foreignMsg1.d = 1; + UnittestImportNano.ImportMessageNano foreignMsg2 = + new UnittestImportNano.ImportMessageNano(); + foreignMsg2.d = 2; + + msg.repeatedImportMessage = + new UnittestImportNano.ImportMessageNano[] { foreignMsg0, foreignMsg1, foreignMsg2 }; + assertEquals(3, msg.repeatedImportMessage.length); + assertEquals(0, msg.repeatedImportMessage[0].d); + assertEquals(1, msg.repeatedImportMessage[1].d); + assertEquals(2, msg.repeatedImportMessage[2].d); + msg.clear(); + assertEquals(0, msg.repeatedImportMessage.length); + msg.clear() + .repeatedImportMessage = new UnittestImportNano.ImportMessageNano[] { foreignMsg1 }; + assertEquals(1, msg.repeatedImportMessage.length); + assertEquals(1, msg.repeatedImportMessage[0].d); + msg.clear(); + assertEquals(0, msg.repeatedImportMessage.length); + + // Test 1 entry + msg.clear() + .repeatedImportMessage = new UnittestImportNano.ImportMessageNano[] { foreignMsg0 }; + assertEquals(1, msg.repeatedImportMessage.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedImportMessage.length); + assertEquals(0, newMsg.repeatedImportMessage[0].d); + + // Test 2 entries + msg.clear() + .repeatedImportMessage = new UnittestImportNano.ImportMessageNano[] { foreignMsg0, foreignMsg1 }; + assertEquals(2, msg.repeatedImportMessage.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedImportMessage.length); + assertEquals(0, newMsg.repeatedImportMessage[0].d); + assertEquals(1, newMsg.repeatedImportMessage[1].d); + } + + public void testNanoRepeatedNestedEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedNestedEnum = new int[] { + TestAllTypesNano.FOO, + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + assertEquals(3, msg.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedNestedEnum[0]); + assertEquals(TestAllTypesNano.BAR, msg.repeatedNestedEnum[1]); + assertEquals(TestAllTypesNano.BAZ, msg.repeatedNestedEnum[2]); + msg.clear(); + assertEquals(0, msg.repeatedNestedEnum.length); + msg.clear() + .repeatedNestedEnum = new int[] { TestAllTypesNano.BAR }; + assertEquals(1, msg.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.BAR, msg.repeatedNestedEnum[0]); + msg.clear(); + assertEquals(0, msg.repeatedNestedEnum.length); + + // Test 1 entry + msg.clear() + .repeatedNestedEnum = new int[] { TestAllTypesNano.FOO }; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedNestedEnum[0]); + + // Test 2 entries + msg.clear() + .repeatedNestedEnum = new int[] { TestAllTypesNano.FOO, TestAllTypesNano.BAR }; + assertEquals(2, msg.repeatedNestedEnum.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedNestedEnum[0]); + assertEquals(TestAllTypesNano.BAR, msg.repeatedNestedEnum[1]); + } + + public void testNanoRepeatedForeignEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedForeignEnum = new int[] { + NanoOuterClass.FOREIGN_NANO_FOO, + NanoOuterClass.FOREIGN_NANO_BAR, + NanoOuterClass.FOREIGN_NANO_BAZ + }; + assertEquals(3, msg.repeatedForeignEnum.length); + assertEquals(NanoOuterClass.FOREIGN_NANO_FOO, msg.repeatedForeignEnum[0]); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, msg.repeatedForeignEnum[1]); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAZ, msg.repeatedForeignEnum[2]); + msg.clear(); + assertEquals(0, msg.repeatedForeignEnum.length); + msg.clear() + .repeatedForeignEnum = new int[] { NanoOuterClass.FOREIGN_NANO_BAR }; + assertEquals(1, msg.repeatedForeignEnum.length); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, msg.repeatedForeignEnum[0]); + msg.clear(); + assertEquals(0, msg.repeatedForeignEnum.length); + + // Test 1 entry + msg.clear() + .repeatedForeignEnum = new int[] { NanoOuterClass.FOREIGN_NANO_FOO }; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedForeignEnum.length); + assertEquals(NanoOuterClass.FOREIGN_NANO_FOO, msg.repeatedForeignEnum[0]); + + // Test 2 entries + msg.clear() + .repeatedForeignEnum = new int[] { + NanoOuterClass.FOREIGN_NANO_FOO, + NanoOuterClass.FOREIGN_NANO_BAR + }; + assertEquals(2, msg.repeatedForeignEnum.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedForeignEnum.length); + assertEquals(NanoOuterClass.FOREIGN_NANO_FOO, msg.repeatedForeignEnum[0]); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, msg.repeatedForeignEnum[1]); + } + + public void testNanoRepeatedImportEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedImportEnum = new int[] { + UnittestImportNano.IMPORT_NANO_FOO, + UnittestImportNano.IMPORT_NANO_BAR, + UnittestImportNano.IMPORT_NANO_BAZ + }; + assertEquals(3, msg.repeatedImportEnum.length); + assertEquals(UnittestImportNano.IMPORT_NANO_FOO, msg.repeatedImportEnum[0]); + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, msg.repeatedImportEnum[1]); + assertEquals(UnittestImportNano.IMPORT_NANO_BAZ, msg.repeatedImportEnum[2]); + msg.clear(); + assertEquals(0, msg.repeatedImportEnum.length); + msg.clear() + .repeatedImportEnum = new int[] { UnittestImportNano.IMPORT_NANO_BAR }; + assertEquals(1, msg.repeatedImportEnum.length); + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, msg.repeatedImportEnum[0]); + msg.clear(); + assertEquals(0, msg.repeatedImportEnum.length); + + // Test 1 entry + msg.clear() + .repeatedImportEnum = new int[] { UnittestImportNano.IMPORT_NANO_FOO }; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedImportEnum.length); + assertEquals(UnittestImportNano.IMPORT_NANO_FOO, msg.repeatedImportEnum[0]); + + // Test 2 entries + msg.clear() + .repeatedImportEnum = new int[] { + UnittestImportNano.IMPORT_NANO_FOO, + UnittestImportNano.IMPORT_NANO_BAR + }; + assertEquals(2, msg.repeatedImportEnum.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedImportEnum.length); + assertEquals(UnittestImportNano.IMPORT_NANO_FOO, msg.repeatedImportEnum[0]); + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, msg.repeatedImportEnum[1]); + } + + public void testNanoRepeatedStringPiece() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedStringPiece.length); + msg.repeatedStringPiece = new String[] { "hello", "bye", "boo" }; + assertEquals("bye", msg.repeatedStringPiece[1]); + assertEquals("boo", msg.repeatedStringPiece[2]); + msg.clear(); + assertEquals(0, msg.repeatedStringPiece.length); + msg.clear() + .repeatedStringPiece = new String[] { "boo" }; + assertEquals(1, msg.repeatedStringPiece.length); + assertEquals("boo", msg.repeatedStringPiece[0]); + msg.clear(); + assertEquals(0, msg.repeatedStringPiece.length); + + // Test 1 entry + msg.clear() + .repeatedStringPiece = new String[] { "" }; + assertEquals(1, msg.repeatedStringPiece.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedStringPiece.length); + assertTrue(newMsg.repeatedStringPiece[0].isEmpty()); + + // Test 2 entries + msg.clear() + .repeatedStringPiece = new String[] { "hello", "world" }; + assertEquals(2, msg.repeatedStringPiece.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 19); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedStringPiece.length); + assertEquals("hello", newMsg.repeatedStringPiece[0]); + assertEquals("world", newMsg.repeatedStringPiece[1]); + } + + public void testNanoRepeatedCord() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedCord.length); + msg.repeatedCord = new String[] { "hello", "bye", "boo" }; + assertEquals("bye", msg.repeatedCord[1]); + assertEquals("boo", msg.repeatedCord[2]); + msg.clear(); + assertEquals(0, msg.repeatedCord.length); + msg.clear() + .repeatedCord = new String[] { "boo" }; + assertEquals(1, msg.repeatedCord.length); + assertEquals("boo", msg.repeatedCord[0]); + msg.clear(); + assertEquals(0, msg.repeatedCord.length); + + // Test 1 entry + msg.clear() + .repeatedCord = new String[] { "" }; + assertEquals(1, msg.repeatedCord.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 6); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedCord.length); + assertTrue(newMsg.repeatedCord[0].isEmpty()); + + // Test 2 entries + msg.clear() + .repeatedCord = new String[] { "hello", "world" }; + assertEquals(2, msg.repeatedCord.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 19); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedCord.length); + assertEquals("hello", newMsg.repeatedCord[0]); + assertEquals("world", newMsg.repeatedCord[1]); + } + + public void testNanoRepeatedPackedInt32() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedPackedInt32.length); + msg.repeatedPackedInt32 = new int[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedPackedInt32[1]); + assertEquals(456, msg.repeatedPackedInt32[2]); + msg.clear(); + assertEquals(0, msg.repeatedPackedInt32.length); + msg.clear() + .repeatedPackedInt32 = new int[] { 456 }; + assertEquals(1, msg.repeatedPackedInt32.length); + assertEquals(456, msg.repeatedPackedInt32[0]); + msg.clear(); + assertEquals(0, msg.repeatedPackedInt32.length); + + // Test 1 entry + msg.clear() + .repeatedPackedInt32 = new int[] { 123 }; + assertEquals(1, msg.repeatedPackedInt32.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 7); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedPackedInt32.length); + assertEquals(123, newMsg.repeatedPackedInt32[0]); + + // Test 2 entries + msg.clear() + .repeatedPackedInt32 = new int[] { 123, 456 }; + assertEquals(2, msg.repeatedPackedInt32.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 9); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedPackedInt32.length); + assertEquals(123, newMsg.repeatedPackedInt32[0]); + assertEquals(456, newMsg.repeatedPackedInt32[1]); + } + + public void testNanoRepeatedPackedSfixed64() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + assertEquals(0, msg.repeatedPackedSfixed64.length); + msg.repeatedPackedSfixed64 = new long[] { 123, 789, 456 }; + assertEquals(789, msg.repeatedPackedSfixed64[1]); + assertEquals(456, msg.repeatedPackedSfixed64[2]); + msg.clear(); + assertEquals(0, msg.repeatedPackedSfixed64.length); + msg.clear() + .repeatedPackedSfixed64 = new long[] { 456 }; + assertEquals(1, msg.repeatedPackedSfixed64.length); + assertEquals(456, msg.repeatedPackedSfixed64[0]); + msg.clear(); + assertEquals(0, msg.repeatedPackedSfixed64.length); + + // Test 1 entry + msg.clear() + .repeatedPackedSfixed64 = new long[] { 123 }; + assertEquals(1, msg.repeatedPackedSfixed64.length); + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 14); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedPackedSfixed64.length); + assertEquals(123, newMsg.repeatedPackedSfixed64[0]); + + // Test 2 entries + msg.clear() + .repeatedPackedSfixed64 = new long[] { 123, 456 }; + assertEquals(2, msg.repeatedPackedSfixed64.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 22); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedPackedSfixed64.length); + assertEquals(123, newMsg.repeatedPackedSfixed64[0]); + assertEquals(456, newMsg.repeatedPackedSfixed64[1]); + } + + public void testNanoRepeatedPackedNestedEnum() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedPackedNestedEnum = new int[] { + TestAllTypesNano.FOO, + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + assertEquals(3, msg.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedPackedNestedEnum[0]); + assertEquals(TestAllTypesNano.BAR, msg.repeatedPackedNestedEnum[1]); + assertEquals(TestAllTypesNano.BAZ, msg.repeatedPackedNestedEnum[2]); + msg.clear(); + assertEquals(0, msg.repeatedPackedNestedEnum.length); + msg.clear() + .repeatedPackedNestedEnum = new int[] { TestAllTypesNano.BAR }; + assertEquals(1, msg.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.BAR, msg.repeatedPackedNestedEnum[0]); + msg.clear(); + assertEquals(0, msg.repeatedPackedNestedEnum.length); + + // Test 1 entry + msg.clear() + .repeatedPackedNestedEnum = new int[] { TestAllTypesNano.FOO }; + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 7); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(1, newMsg.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedPackedNestedEnum[0]); + + // Test 2 entries + msg.clear() + .repeatedPackedNestedEnum = new int[] { TestAllTypesNano.FOO, TestAllTypesNano.BAR }; + assertEquals(2, msg.repeatedPackedNestedEnum.length); + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 8); + assertEquals(result.length, msgSerializedSize); + + newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(2, newMsg.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, msg.repeatedPackedNestedEnum[0]); + assertEquals(TestAllTypesNano.BAR, msg.repeatedPackedNestedEnum[1]); + } + + public void testNanoRepeatedPackedSerializedSize() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedPackedInt32 = new int[] { 123, 789, 456 }; + int msgSerializedSize = msg.getSerializedSize(); + byte [] result = MessageNano.toByteArray(msg); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 11); + assertEquals(result.length, msgSerializedSize); + TestAllTypesNano msg2 = new TestAllTypesNano(); + msg2.repeatedPackedInt32 = new int[] { 123, 789, 456 }; + byte [] result2 = new byte[msgSerializedSize]; + MessageNano.toByteArray(msg2, result2, 0, msgSerializedSize); + + // Check equal size and content. + assertEquals(msgSerializedSize, msg2.getSerializedSize()); + assertTrue(Arrays.equals(result, result2)); + } + + public void testNanoRepeatedInt32ReMerge() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedInt32 = new int[] { 234 }; + byte [] result1 = MessageNano.toByteArray(msg); + + msg.clear().optionalInt32 = 789; + byte [] result2 = MessageNano.toByteArray(msg); + + msg.clear().repeatedInt32 = new int[] { 123, 456 }; + byte [] result3 = MessageNano.toByteArray(msg); + + // Concatenate the three serializations and read as one message. + byte [] result = new byte[result1.length + result2.length + result3.length]; + System.arraycopy(result1, 0, result, 0, result1.length); + System.arraycopy(result2, 0, result, result1.length, result2.length); + System.arraycopy(result3, 0, result, result1.length + result2.length, result3.length); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(789, newMsg.optionalInt32); + assertEquals(3, newMsg.repeatedInt32.length); + assertEquals(234, newMsg.repeatedInt32[0]); + assertEquals(123, newMsg.repeatedInt32[1]); + assertEquals(456, newMsg.repeatedInt32[2]); + } + + public void testNanoRepeatedNestedEnumReMerge() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.repeatedNestedEnum = new int[] { TestAllTypesNano.FOO }; + byte [] result1 = MessageNano.toByteArray(msg); + + msg.clear().optionalInt32 = 789; + byte [] result2 = MessageNano.toByteArray(msg); + + msg.clear().repeatedNestedEnum = new int[] { TestAllTypesNano.BAR, TestAllTypesNano.FOO }; + byte [] result3 = MessageNano.toByteArray(msg); + + // Concatenate the three serializations and read as one message. + byte [] result = new byte[result1.length + result2.length + result3.length]; + System.arraycopy(result1, 0, result, 0, result1.length); + System.arraycopy(result2, 0, result, result1.length, result2.length); + System.arraycopy(result3, 0, result, result1.length + result2.length, result3.length); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(789, newMsg.optionalInt32); + assertEquals(3, newMsg.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, newMsg.repeatedNestedEnum[0]); + assertEquals(TestAllTypesNano.BAR, newMsg.repeatedNestedEnum[1]); + assertEquals(TestAllTypesNano.FOO, newMsg.repeatedNestedEnum[2]); + } + + public void testNanoRepeatedNestedMessageReMerge() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + TestAllTypesNano.NestedMessage nestedMsg0 = + new TestAllTypesNano.NestedMessage(); + nestedMsg0.bb = 0; + TestAllTypesNano.NestedMessage nestedMsg1 = + new TestAllTypesNano.NestedMessage(); + nestedMsg1.bb = 1; + TestAllTypesNano.NestedMessage nestedMsg2 = + new TestAllTypesNano.NestedMessage(); + nestedMsg2.bb = 2; + + msg.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { nestedMsg0 }; + byte [] result1 = MessageNano.toByteArray(msg); + + msg.clear().optionalInt32 = 789; + byte [] result2 = MessageNano.toByteArray(msg); + + msg.clear().repeatedNestedMessage = + new TestAllTypesNano.NestedMessage[] { nestedMsg1, nestedMsg2 }; + byte [] result3 = MessageNano.toByteArray(msg); + + // Concatenate the three serializations and read as one message. + byte [] result = new byte[result1.length + result2.length + result3.length]; + System.arraycopy(result1, 0, result, 0, result1.length); + System.arraycopy(result2, 0, result, result1.length, result2.length); + System.arraycopy(result3, 0, result, result1.length + result2.length, result3.length); + + TestAllTypesNano newMsg = TestAllTypesNano.parseFrom(result); + assertEquals(789, newMsg.optionalInt32); + assertEquals(3, newMsg.repeatedNestedMessage.length); + assertEquals(nestedMsg0.bb, newMsg.repeatedNestedMessage[0].bb); + assertEquals(nestedMsg1.bb, newMsg.repeatedNestedMessage[1].bb); + assertEquals(nestedMsg2.bb, newMsg.repeatedNestedMessage[2].bb); + } + + /** + * Tests that invalid enum values from the wire are not accepted. + */ + public void testNanoEnumValidity() throws Exception { + final int invalid = 120; + final int alsoInvalid = 121; + + EnumValidity.M m = new EnumValidity.M(); + // Sanity check & baseline of the assertions for the first case below. + assertEquals(EnumValidity.E.default_, m.optionalE); + assertEquals(EnumValidity.E.BAZ, m.defaultE); + + m.optionalE = invalid; + m.defaultE = invalid; + // E contains all valid values + m.repeatedE = new int[] {EnumValidity.E.FOO, EnumValidity.E.BAR}; + m.packedE = new int[] {EnumValidity.E.FOO, EnumValidity.E.BAZ}; + // E2 contains some invalid values + m.repeatedE2 = new int[] {invalid, EnumValidity.E.BAR, alsoInvalid}; + m.packedE2 = new int[] {EnumValidity.E.FOO, invalid, alsoInvalid}; + // E3 contains all invalid values + m.repeatedE3 = new int[] {invalid, invalid}; + m.packedE3 = new int[] {alsoInvalid, alsoInvalid}; + byte[] serialized = MessageNano.toByteArray(m); + // Sanity check that we do have all data in the byte array. + assertEquals(31, serialized.length); + + // Test 1: tests that invalid values aren't included in the deserialized message. + EnumValidity.M deserialized = MessageNano.mergeFrom(new EnumValidity.M(), serialized); + assertEquals(EnumValidity.E.default_, deserialized.optionalE); + assertEquals(EnumValidity.E.BAZ, deserialized.defaultE); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.FOO, EnumValidity.E.BAR}, deserialized.repeatedE)); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.FOO, EnumValidity.E.BAZ}, deserialized.packedE)); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.BAR}, deserialized.repeatedE2)); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.FOO}, deserialized.packedE2)); + assertEquals(0, deserialized.repeatedE3.length); + assertEquals(0, deserialized.packedE3.length); + + // Test 2: tests that invalid values do not override previous values in the field, including + // arrays, including pre-existing invalid values. + deserialized.optionalE = EnumValidity.E.BAR; + deserialized.defaultE = alsoInvalid; + deserialized.repeatedE = new int[] {EnumValidity.E.BAZ}; + deserialized.packedE = new int[] {EnumValidity.E.BAZ, alsoInvalid}; + deserialized.repeatedE2 = new int[] {invalid, alsoInvalid}; + deserialized.packedE2 = null; + deserialized.repeatedE3 = null; + deserialized.packedE3 = new int[0]; + MessageNano.mergeFrom(deserialized, serialized); + assertEquals(EnumValidity.E.BAR, deserialized.optionalE); + assertEquals(alsoInvalid, deserialized.defaultE); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.BAZ, /* + */ EnumValidity.E.FOO, EnumValidity.E.BAR}, + deserialized.repeatedE)); + assertTrue(Arrays.equals( + new int[] {EnumValidity.E.BAZ, alsoInvalid, /* + */ EnumValidity.E.FOO, EnumValidity.E.BAZ}, + deserialized.packedE)); + assertTrue(Arrays.equals( + new int[] {invalid, alsoInvalid, /* + */ EnumValidity.E.BAR}, + deserialized.repeatedE2)); + assertTrue(Arrays.equals( + new int[] {/* <null> + */ EnumValidity.E.FOO}, + deserialized.packedE2)); + assertNull(deserialized.repeatedE3); // null + all invalid == null + assertEquals(0, deserialized.packedE3.length); // empty + all invalid == empty + + // Test 3: reading by alternative forms + EnumValidity.Alt alt = MessageNano.mergeFrom(new EnumValidity.Alt(), serialized); + assertEquals(EnumValidity.E.BAR, // last valid value in m.repeatedE2 + alt.repeatedE2AsOptional); + assertTrue(Arrays.equals(new int[] {EnumValidity.E.FOO}, alt.packedE2AsNonPacked)); + assertEquals(0, alt.nonPackedE3AsPacked.length); + } + + /** + * Tests the same as {@link #testNanoEnumValidity()} with accessor style. Repeated fields are + * not re-tested here because they are not affected by the accessor style. + */ + public void testNanoEnumValidityAccessors() throws Exception { + final int invalid = 120; + final int alsoInvalid = 121; + + EnumValidityAccessors.M m = new EnumValidityAccessors.M(); + // Sanity check & baseline of the assertions for the first case below. + assertEquals(EnumValidityAccessors.default_, m.getOptionalE()); + assertEquals(EnumValidityAccessors.BAZ, m.getDefaultE()); + + m.setOptionalE(invalid); + m.setDefaultE(invalid); + // Set repeatedE2 for Alt.repeatedE2AsOptional + m.repeatedE2 = new int[] {invalid, EnumValidityAccessors.BAR, alsoInvalid}; + byte[] serialized = MessageNano.toByteArray(m); + // Sanity check that we do have all data in the byte array. + assertEquals(10, serialized.length); + + // Test 1: tests that invalid values aren't included in the deserialized message. + EnumValidityAccessors.M deserialized = + MessageNano.mergeFrom(new EnumValidityAccessors.M(), serialized); + assertEquals(EnumValidityAccessors.default_, deserialized.getOptionalE()); + assertEquals(EnumValidityAccessors.BAZ, deserialized.getDefaultE()); + + // Test 2: tests that invalid values do not override previous values in the field, including + // pre-existing invalid values. + deserialized.setOptionalE(EnumValidityAccessors.BAR); + deserialized.setDefaultE(alsoInvalid); + MessageNano.mergeFrom(deserialized, serialized); + assertEquals(EnumValidityAccessors.BAR, deserialized.getOptionalE()); + assertEquals(alsoInvalid, deserialized.getDefaultE()); + + // Test 3: reading by alternative forms + EnumValidityAccessors.Alt alt = + MessageNano.mergeFrom(new EnumValidityAccessors.Alt(), serialized); + assertEquals(EnumValidityAccessors.BAR, // last valid value in m.repeatedE2 + alt.getRepeatedE2AsOptional()); + } + + /** + * Tests that code generation correctly wraps a single message into its outer + * class. The class {@code SingleMessageNano} is imported from the outer + * class {@code UnittestSingleNano}, whose name is implicit. Any error would + * cause this method to fail compilation. + */ + public void testNanoSingle() throws Exception { + SingleMessageNano msg = new SingleMessageNano(); + assertNotNull(msg); + } + + /** + * Tests that code generation correctly skips generating the outer class if + * unnecessary, letting a file-scope entity have the same name. The class + * {@code MultipleNameClashNano} shares the same name with the file's outer + * class defined explicitly, but the file contains no other entities and has + * java_multiple_files set. Any error would cause this method to fail + * compilation. + */ + public void testNanoMultipleNameClash() throws Exception { + MultipleNameClashNano msg = new MultipleNameClashNano(); + msg.field = 0; + } + + /** + * Tests that code generation correctly handles enums in different scopes in + * a source file with the option java_multiple_files set to true. Any error + * would cause this method to fail compilation. + */ + public void testNanoMultipleEnumScoping() throws Exception { + FileScopeEnumRefNano msg1 = new FileScopeEnumRefNano(); + msg1.enumField = UnittestMultipleNano.ONE; + MessageScopeEnumRefNano msg2 = new MessageScopeEnumRefNano(); + msg2.enumField = MessageScopeEnumRefNano.TWO; + } + + /** + * Tests that code generation with mixed values of the java_multiple_files + * options between the main source file and the imported source files would + * generate correct references. Any error would cause this method to fail + * compilation. + */ + public void testNanoMultipleImportingNonMultiple() throws Exception { + UnittestImportNano.ImportMessageNano importMsg = new UnittestImportNano.ImportMessageNano(); + MultipleImportingNonMultipleNano1 nano1 = new MultipleImportingNonMultipleNano1(); + nano1.field = importMsg; + MultipleImportingNonMultipleNano2 nano2 = new MultipleImportingNonMultipleNano2(); + nano2.nano1 = nano1; + } + + public void testNanoDefaults() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + for (int i = 0; i < 2; i++) { + assertEquals(41, msg.defaultInt32); + assertEquals(42, msg.defaultInt64); + assertEquals(43, msg.defaultUint32); + assertEquals(44, msg.defaultUint64); + assertEquals(-45, msg.defaultSint32); + assertEquals(46, msg.defaultSint64); + assertEquals(47, msg.defaultFixed32); + assertEquals(48, msg.defaultFixed64); + assertEquals(49, msg.defaultSfixed32); + assertEquals(-50, msg.defaultSfixed64); + assertTrue(51.5f == msg.defaultFloat); + assertTrue(52.0e3 == msg.defaultDouble); + assertEquals(true, msg.defaultBool); + assertEquals("hello", msg.defaultString); + assertEquals("world", new String(msg.defaultBytes, "UTF-8")); + assertEquals("dünya", msg.defaultStringNonascii); + assertEquals("dünyab", new String(msg.defaultBytesNonascii, "UTF-8")); + assertEquals(TestAllTypesNano.BAR, msg.defaultNestedEnum); + assertEquals(NanoOuterClass.FOREIGN_NANO_BAR, msg.defaultForeignEnum); + assertEquals(UnittestImportNano.IMPORT_NANO_BAR, msg.defaultImportEnum); + assertEquals(Float.POSITIVE_INFINITY, msg.defaultFloatInf); + assertEquals(Float.NEGATIVE_INFINITY, msg.defaultFloatNegInf); + assertEquals(Float.NaN, msg.defaultFloatNan); + assertEquals(Double.POSITIVE_INFINITY, msg.defaultDoubleInf); + assertEquals(Double.NEGATIVE_INFINITY, msg.defaultDoubleNegInf); + assertEquals(Double.NaN, msg.defaultDoubleNan); + + // Default values are not output, except for required fields. + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 3); + assertEquals(result.length, msgSerializedSize); + msg.clear(); + } + } + + public void testNanoWithHasParseFrom() throws Exception { + TestAllTypesNanoHas msg = null; + // Test false on creation, after clear and upon empty parse. + for (int i = 0; i < 3; i++) { + if (i == 0) { + msg = new TestAllTypesNanoHas(); + } else if (i == 1) { + msg.clear(); + } else if (i == 2) { + msg = TestAllTypesNanoHas.parseFrom(new byte[0]); + } + assertFalse(msg.hasOptionalInt32); + assertFalse(msg.hasOptionalString); + assertFalse(msg.hasOptionalBytes); + assertFalse(msg.hasOptionalNestedEnum); + assertFalse(msg.hasDefaultInt32); + assertFalse(msg.hasDefaultString); + assertFalse(msg.hasDefaultBytes); + assertFalse(msg.hasDefaultFloatNan); + assertFalse(msg.hasDefaultNestedEnum); + assertFalse(msg.hasId); + assertFalse(msg.hasRequiredEnum); + msg.optionalInt32 = 123; + msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage(); + msg.optionalNestedMessage.bb = 2; + msg.optionalNestedEnum = TestAllTypesNano.BAZ; + } + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 10); + assertEquals(result.length, msgSerializedSize); + + // Has fields true upon parse. + TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result); + assertEquals(123, newMsg.optionalInt32); + assertTrue(newMsg.hasOptionalInt32); + assertEquals(2, newMsg.optionalNestedMessage.bb); + assertTrue(newMsg.optionalNestedMessage.hasBb); + assertEquals(TestAllTypesNanoHas.BAZ, newMsg.optionalNestedEnum); + assertTrue(newMsg.hasOptionalNestedEnum); + } + + public void testNanoWithHasSerialize() throws Exception { + TestAllTypesNanoHas msg = new TestAllTypesNanoHas(); + msg.hasOptionalInt32 = true; + msg.hasOptionalString = true; + msg.hasOptionalBytes = true; + msg.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage(); + msg.optionalNestedMessage.hasBb = true; + msg.hasOptionalNestedEnum = true; + msg.hasDefaultInt32 = true; + msg.hasDefaultString = true; + msg.hasDefaultBytes = true; + msg.hasDefaultFloatNan = true; + msg.hasDefaultNestedEnum = true; + msg.hasId = true; + msg.hasRequiredEnum = true; + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + assertEquals(result.length, msgSerializedSize); + + // Now deserialize and find that all fields are set and equal to their defaults. + TestAllTypesNanoHas newMsg = TestAllTypesNanoHas.parseFrom(result); + assertTrue(newMsg.hasOptionalInt32); + assertTrue(newMsg.hasOptionalString); + assertTrue(newMsg.hasOptionalBytes); + assertTrue(newMsg.optionalNestedMessage.hasBb); + assertTrue(newMsg.hasOptionalNestedEnum); + assertTrue(newMsg.hasDefaultInt32); + assertTrue(newMsg.hasDefaultString); + assertTrue(newMsg.hasDefaultBytes); + assertTrue(newMsg.hasDefaultFloatNan); + assertTrue(newMsg.hasDefaultNestedEnum); + assertTrue(newMsg.hasId); + assertTrue(newMsg.hasRequiredEnum); + assertEquals(0, newMsg.optionalInt32); + assertEquals(0, newMsg.optionalString.length()); + assertEquals(0, newMsg.optionalBytes.length); + assertEquals(0, newMsg.optionalNestedMessage.bb); + assertEquals(TestAllTypesNanoHas.FOO, newMsg.optionalNestedEnum); + assertEquals(41, newMsg.defaultInt32); + assertEquals("hello", newMsg.defaultString); + assertEquals("world", new String(newMsg.defaultBytes, "UTF-8")); + assertEquals(TestAllTypesNanoHas.BAR, newMsg.defaultNestedEnum); + assertEquals(Float.NaN, newMsg.defaultFloatNan); + assertEquals(0, newMsg.id); + assertEquals(TestAllTypesNanoHas.FOO, newMsg.requiredEnum); + } + + public void testNanoWithAccessorsBasic() throws Exception { + TestNanoAccessors msg = new TestNanoAccessors(); + + // Makes sure required, repeated, and message fields are still public + msg.id = 3; + msg.repeatedBytes = new byte[2][3]; + msg.optionalNestedMessage = null; + + // Test accessors + assertEquals(0, msg.getOptionalInt32()); + assertFalse(msg.hasOptionalInt32()); + msg.setOptionalInt32(135); + assertEquals(135, msg.getOptionalInt32()); + assertTrue(msg.hasOptionalInt32()); + msg.clearOptionalInt32(); + assertFalse(msg.hasOptionalInt32()); + msg.setOptionalInt32(0); // default value + assertTrue(msg.hasOptionalInt32()); + + // Test NPE + try { + msg.setOptionalBytes(null); + fail(); + } catch (NullPointerException expected) {} + try { + msg.setOptionalString(null); + fail(); + } catch (NullPointerException expected) {} + + // Test has bit on bytes field with defaults and clear() re-clones the default array + assertFalse(msg.hasDefaultBytes()); + byte[] defaultBytes = msg.getDefaultBytes(); + msg.setDefaultBytes(defaultBytes); + assertTrue(msg.hasDefaultBytes()); + msg.clearDefaultBytes(); + assertFalse(msg.hasDefaultBytes()); + defaultBytes[0]++; // modify original array + assertFalse(Arrays.equals(defaultBytes, msg.getDefaultBytes())); + + // Test has bits that require additional bit fields + assertFalse(msg.hasBitFieldCheck()); + msg.setBitFieldCheck(0); + assertTrue(msg.hasBitFieldCheck()); + assertFalse(msg.hasBeforeBitFieldCheck()); // checks bit field does not leak + assertFalse(msg.hasAfterBitFieldCheck()); + + // Test clear() clears has bits + msg.setOptionalString("hi"); + msg.setDefaultString("there"); + msg.clear(); + assertFalse(msg.hasOptionalString()); + assertFalse(msg.hasDefaultString()); + assertFalse(msg.hasBitFieldCheck()); + + // Test set() and clear() returns itself (compiles = success) + msg.clear() + .setOptionalInt32(3) + .clearDefaultBytes() + .setOptionalString("4"); + } + + public void testNanoWithAccessorsParseFrom() throws Exception { + TestNanoAccessors msg = null; + // Test false on creation, after clear and upon empty parse. + for (int i = 0; i < 3; i++) { + if (i == 0) { + msg = new TestNanoAccessors(); + } else if (i == 1) { + msg.clear(); + } else if (i == 2) { + msg = TestNanoAccessors.parseFrom(new byte[0]); + } + assertFalse(msg.hasOptionalInt32()); + assertFalse(msg.hasOptionalString()); + assertFalse(msg.hasOptionalBytes()); + assertFalse(msg.hasOptionalNestedEnum()); + assertFalse(msg.hasDefaultInt32()); + assertFalse(msg.hasDefaultString()); + assertFalse(msg.hasDefaultBytes()); + assertFalse(msg.hasDefaultFloatNan()); + assertFalse(msg.hasDefaultNestedEnum()); + msg.optionalNestedMessage = new TestNanoAccessors.NestedMessage(); + msg.optionalNestedMessage.setBb(2); + msg.setOptionalNestedEnum(TestNanoAccessors.BAZ); + msg.setDefaultInt32(msg.getDefaultInt32()); + } + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + //System.out.printf("mss=%d result.length=%d\n", msgSerializedSize, result.length); + assertTrue(msgSerializedSize == 14); + assertEquals(result.length, msgSerializedSize); + + // Has fields true upon parse. + TestNanoAccessors newMsg = TestNanoAccessors.parseFrom(result); + assertEquals(2, newMsg.optionalNestedMessage.getBb()); + assertTrue(newMsg.optionalNestedMessage.hasBb()); + assertEquals(TestNanoAccessors.BAZ, newMsg.getOptionalNestedEnum()); + assertTrue(newMsg.hasOptionalNestedEnum()); + + // Has field true on fields with explicit default values from wire. + assertTrue(newMsg.hasDefaultInt32()); + assertEquals(41, newMsg.getDefaultInt32()); + } + + public void testNanoWithAccessorsPublicFieldTypes() throws Exception { + TestNanoAccessors msg = new TestNanoAccessors(); + assertNull(msg.optionalNestedMessage); + assertEquals(0, msg.id); + assertEquals(0, msg.repeatedNestedEnum.length); + + TestNanoAccessors newMsg = TestNanoAccessors.parseFrom(MessageNano.toByteArray(msg)); + assertNull(newMsg.optionalNestedMessage); + assertEquals(0, newMsg.id); + assertEquals(0, newMsg.repeatedNestedEnum.length); + + TestNanoAccessors.NestedMessage nestedMessage = new TestNanoAccessors.NestedMessage(); + nestedMessage.setBb(5); + newMsg.optionalNestedMessage = nestedMessage; + newMsg.id = -1; + newMsg.repeatedNestedEnum = new int[] { TestAllTypesNano.FOO }; + + TestNanoAccessors newMsg2 = TestNanoAccessors.parseFrom(MessageNano.toByteArray(newMsg)); + assertEquals(nestedMessage.getBb(), newMsg2.optionalNestedMessage.getBb()); + assertEquals(-1, newMsg2.id); + assertEquals(TestAllTypesNano.FOO, newMsg2.repeatedNestedEnum[0]); + + newMsg2.optionalNestedMessage = null; + newMsg2.id = 0; + newMsg2.repeatedNestedEnum = null; + + TestNanoAccessors newMsg3 = TestNanoAccessors.parseFrom(MessageNano.toByteArray(newMsg2)); + assertNull(newMsg3.optionalNestedMessage); + assertEquals(0, newMsg3.id); + assertEquals(0, newMsg3.repeatedNestedEnum.length); + } + + public void testNanoWithAccessorsSerialize() throws Exception { + TestNanoAccessors msg = new TestNanoAccessors(); + msg.setOptionalInt32(msg.getOptionalInt32()); + msg.setOptionalString(msg.getOptionalString()); + msg.setOptionalBytes(msg.getOptionalBytes()); + TestNanoAccessors.NestedMessage nestedMessage = new TestNanoAccessors.NestedMessage(); + nestedMessage.setBb(nestedMessage.getBb()); + msg.optionalNestedMessage = nestedMessage; + msg.setOptionalNestedEnum(msg.getOptionalNestedEnum()); + msg.setDefaultInt32(msg.getDefaultInt32()); + msg.setDefaultString(msg.getDefaultString()); + msg.setDefaultBytes(msg.getDefaultBytes()); + msg.setDefaultFloatNan(msg.getDefaultFloatNan()); + msg.setDefaultNestedEnum(msg.getDefaultNestedEnum()); + + byte [] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + assertEquals(result.length, msgSerializedSize); + + // Now deserialize and find that all fields are set and equal to their defaults. + TestNanoAccessors newMsg = TestNanoAccessors.parseFrom(result); + assertTrue(newMsg.hasOptionalInt32()); + assertTrue(newMsg.hasOptionalString()); + assertTrue(newMsg.hasOptionalBytes()); + assertTrue(newMsg.optionalNestedMessage.hasBb()); + assertTrue(newMsg.hasOptionalNestedEnum()); + assertTrue(newMsg.hasDefaultInt32()); + assertTrue(newMsg.hasDefaultString()); + assertTrue(newMsg.hasDefaultBytes()); + assertTrue(newMsg.hasDefaultFloatNan()); + assertTrue(newMsg.hasDefaultNestedEnum()); + assertEquals(0, newMsg.getOptionalInt32()); + assertEquals(0, newMsg.getOptionalString().length()); + assertEquals(0, newMsg.getOptionalBytes().length); + assertEquals(0, newMsg.optionalNestedMessage.getBb()); + assertEquals(TestNanoAccessors.FOO, newMsg.getOptionalNestedEnum()); + assertEquals(41, newMsg.getDefaultInt32()); + assertEquals("hello", newMsg.getDefaultString()); + assertEquals("world", new String(newMsg.getDefaultBytes(), "UTF-8")); + assertEquals(TestNanoAccessors.BAR, newMsg.getDefaultNestedEnum()); + assertEquals(Float.NaN, newMsg.getDefaultFloatNan()); + assertEquals(0, newMsg.id); + } + + public void testNanoJavaEnumStyle() throws Exception { + EnumClassNanos.EnumClassNano msg = new EnumClassNanos.EnumClassNano(); + assertEquals(EnumClassNanos.FileScopeEnum.ONE, msg.one); + assertEquals(EnumClassNanos.EnumClassNano.MessageScopeEnum.TWO, msg.two); + + EnumClassNanoMultiple msg2 = new EnumClassNanoMultiple(); + assertEquals(FileScopeEnumMultiple.THREE, msg2.three); + assertEquals(EnumClassNanoMultiple.MessageScopeEnumMultiple.FOUR, msg2.four); + } + + /** + * Tests that fields with a default value of NaN are not serialized when + * set to NaN. This is a special case as NaN != NaN, so normal equality + * checks don't work. + */ + public void testNanoNotANumberDefaults() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.defaultDoubleNan = 0; + msg.defaultFloatNan = 0; + byte[] result = MessageNano.toByteArray(msg); + int msgSerializedSize = msg.getSerializedSize(); + assertTrue(result.length == msgSerializedSize); + assertTrue(msgSerializedSize > 3); + + msg.defaultDoubleNan = Double.NaN; + msg.defaultFloatNan = Float.NaN; + result = MessageNano.toByteArray(msg); + msgSerializedSize = msg.getSerializedSize(); + assertEquals(3, result.length); + assertEquals(3, msgSerializedSize); + } + + /** + * Test that a bug in skipRawBytes() has been fixed: if the skip skips + * exactly up to a limit, this should not break things. + */ + public void testSkipRawBytesBug() throws Exception { + byte[] rawBytes = new byte[] { 1, 2 }; + CodedInputByteBufferNano input = CodedInputByteBufferNano.newInstance(rawBytes); + + int limit = input.pushLimit(1); + input.skipRawBytes(1); + input.popLimit(limit); + assertEquals(2, input.readRawByte()); + } + + /** + * Test that a bug in skipRawBytes() has been fixed: if the skip skips + * past the end of a buffer with a limit that has been set past the end of + * that buffer, this should not break things. + */ + public void testSkipRawBytesPastEndOfBufferWithLimit() throws Exception { + byte[] rawBytes = new byte[] { 1, 2, 3, 4, 5 }; + CodedInputByteBufferNano input = CodedInputByteBufferNano.newInstance(rawBytes); + + int limit = input.pushLimit(4); + // In order to expose the bug we need to read at least one byte to prime the + // buffer inside the CodedInputStream. + assertEquals(1, input.readRawByte()); + // Skip to the end of the limit. + input.skipRawBytes(3); + assertTrue(input.isAtEnd()); + input.popLimit(limit); + assertEquals(5, input.readRawByte()); + } + + // Test a smattering of various proto types for printing + public void testMessageNanoPrinter() { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.optionalInt32 = 14; + msg.optionalFloat = 42.3f; + msg.optionalString = "String \"with' both quotes"; + msg.optionalBytes = new byte[] {'"', '\0', 1, 8}; + msg.optionalGroup = new TestAllTypesNano.OptionalGroup(); + msg.optionalGroup.a = 15; + msg.repeatedInt64 = new long[2]; + msg.repeatedInt64[0] = 1L; + msg.repeatedInt64[1] = -1L; + msg.repeatedBytes = new byte[2][]; + msg.repeatedBytes[1] = new byte[] {'h', 'e', 'l', 'l', 'o'}; + msg.repeatedGroup = new TestAllTypesNano.RepeatedGroup[2]; + msg.repeatedGroup[0] = new TestAllTypesNano.RepeatedGroup(); + msg.repeatedGroup[0].a = -27; + msg.repeatedGroup[1] = new TestAllTypesNano.RepeatedGroup(); + msg.repeatedGroup[1].a = -72; + msg.optionalNestedMessage = new TestAllTypesNano.NestedMessage(); + msg.optionalNestedMessage.bb = 7; + msg.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[2]; + msg.repeatedNestedMessage[0] = new TestAllTypesNano.NestedMessage(); + msg.repeatedNestedMessage[0].bb = 77; + msg.repeatedNestedMessage[1] = new TestAllTypesNano.NestedMessage(); + msg.repeatedNestedMessage[1].bb = 88; + msg.optionalNestedEnum = TestAllTypesNano.BAZ; + msg.repeatedNestedEnum = new int[2]; + msg.repeatedNestedEnum[0] = TestAllTypesNano.BAR; + msg.repeatedNestedEnum[1] = TestAllTypesNano.FOO; + msg.repeatedStringPiece = new String[] {null, "world"}; + + String protoPrint = msg.toString(); + assertTrue(protoPrint.contains("optional_int32: 14")); + assertTrue(protoPrint.contains("optional_float: 42.3")); + assertTrue(protoPrint.contains("optional_double: 0.0")); + assertTrue(protoPrint.contains("optional_string: \"String \\u0022with\\u0027 both quotes\"")); + assertTrue(protoPrint.contains("optional_bytes: \"\\\"\\000\\001\\010\"")); + assertTrue(protoPrint.contains("optional_group <\n a: 15\n>")); + + assertTrue(protoPrint.contains("repeated_int64: 1\nrepeated_int64: -1")); + assertFalse(protoPrint.contains("repeated_bytes: \"\"")); // null should be dropped + assertTrue(protoPrint.contains("repeated_bytes: \"hello\"")); + assertTrue(protoPrint.contains("repeated_group <\n a: -27\n>\n" + + "repeated_group <\n a: -72\n>")); + assertTrue(protoPrint.contains("optional_nested_message <\n bb: 7\n>")); + assertTrue(protoPrint.contains("repeated_nested_message <\n bb: 77\n>\n" + + "repeated_nested_message <\n bb: 88\n>")); + assertTrue(protoPrint.contains("optional_nested_enum: 3")); + assertTrue(protoPrint.contains("repeated_nested_enum: 2\nrepeated_nested_enum: 1")); + assertTrue(protoPrint.contains("default_int32: 41")); + assertTrue(protoPrint.contains("default_string: \"hello\"")); + assertFalse(protoPrint.contains("repeated_string_piece: \"\"")); // null should be dropped + assertTrue(protoPrint.contains("repeated_string_piece: \"world\"")); + } + + public void testMessageNanoPrinterAccessors() throws Exception { + TestNanoAccessors msg = new TestNanoAccessors(); + msg.setOptionalInt32(13); + msg.setOptionalString("foo"); + msg.setOptionalBytes(new byte[] {'"', '\0', 1, 8}); + msg.optionalNestedMessage = new TestNanoAccessors.NestedMessage(); + msg.optionalNestedMessage.setBb(7); + msg.setOptionalNestedEnum(TestNanoAccessors.BAZ); + msg.repeatedInt32 = new int[] { 1, -1 }; + msg.repeatedString = new String[] { "Hello", "world" }; + msg.repeatedBytes = new byte[2][]; + msg.repeatedBytes[1] = new byte[] {'h', 'e', 'l', 'l', 'o'}; + msg.repeatedNestedMessage = new TestNanoAccessors.NestedMessage[2]; + msg.repeatedNestedMessage[0] = new TestNanoAccessors.NestedMessage(); + msg.repeatedNestedMessage[0].setBb(5); + msg.repeatedNestedMessage[1] = new TestNanoAccessors.NestedMessage(); + msg.repeatedNestedMessage[1].setBb(6); + msg.repeatedNestedEnum = new int[] { TestNanoAccessors.FOO, TestNanoAccessors.BAR }; + msg.id = 33; + + String protoPrint = msg.toString(); + assertTrue(protoPrint.contains("optional_int32: 13")); + assertTrue(protoPrint.contains("optional_string: \"foo\"")); + assertTrue(protoPrint.contains("optional_bytes: \"\\\"\\000\\001\\010\"")); + assertTrue(protoPrint.contains("optional_nested_message <\n bb: 7\n>")); + assertTrue(protoPrint.contains("optional_nested_enum: 3")); + assertTrue(protoPrint.contains("repeated_int32: 1\nrepeated_int32: -1")); + assertTrue(protoPrint.contains("repeated_string: \"Hello\"\nrepeated_string: \"world\"")); + assertFalse(protoPrint.contains("repeated_bytes: \"\"")); // null should be dropped + assertTrue(protoPrint.contains("repeated_bytes: \"hello\"")); + assertTrue(protoPrint.contains("repeated_nested_message <\n bb: 5\n>\n" + + "repeated_nested_message <\n bb: 6\n>")); + assertTrue(protoPrint.contains("repeated_nested_enum: 1\nrepeated_nested_enum: 2")); + assertTrue(protoPrint.contains("id: 33")); + } + + public void testExtensions() throws Exception { + Extensions.ExtendableMessage message = new Extensions.ExtendableMessage(); + message.field = 5; + int[] int32s = {1, 2}; + int[] uint32s = {3, 4}; + int[] sint32s = {-5, -6}; + long[] int64s = {7, 8}; + long[] uint64s = {9, 10}; + long[] sint64s = {-11, -12}; + int[] fixed32s = {13, 14}; + int[] sfixed32s = {-15, -16}; + long[] fixed64s = {17, 18}; + long[] sfixed64s = {-19, -20}; + boolean[] bools = {true, false}; + float[] floats = {2.1f, 2.2f}; + double[] doubles = {2.3, 2.4}; + int[] enums = {Extensions.SECOND_VALUE, Extensions.FIRST_VALUE}; + String[] strings = {"vijfentwintig", "twenty-six"}; + byte[][] bytess = {{2, 7}, {2, 8}}; + AnotherMessage another1 = new AnotherMessage(); + another1.string = "er shi jiu"; + another1.value = false; + AnotherMessage another2 = new AnotherMessage(); + another2.string = "trente"; + another2.value = true; + AnotherMessage[] messages = {another1, another2}; + RepeatedExtensions.RepeatedGroup group1 = new RepeatedExtensions.RepeatedGroup(); + group1.a = 31; + RepeatedExtensions.RepeatedGroup group2 = new RepeatedExtensions.RepeatedGroup(); + group2.a = 32; + RepeatedExtensions.RepeatedGroup[] groups = {group1, group2}; + assertFalse(message.hasExtension(RepeatedExtensions.repeatedInt32)); + message.setExtension(RepeatedExtensions.repeatedInt32, int32s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedInt32)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedUint32)); + message.setExtension(RepeatedExtensions.repeatedUint32, uint32s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedUint32)); + message.setExtension(RepeatedExtensions.repeatedSint32, sint32s); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedInt64)); + message.setExtension(RepeatedExtensions.repeatedInt64, int64s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedInt64)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedUint64)); + message.setExtension(RepeatedExtensions.repeatedUint64, uint64s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedUint64)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedSint64)); + message.setExtension(RepeatedExtensions.repeatedSint64, sint64s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedSint64)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedFixed32)); + message.setExtension(RepeatedExtensions.repeatedFixed32, fixed32s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedFixed32)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedSfixed32)); + message.setExtension(RepeatedExtensions.repeatedSfixed32, sfixed32s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedSfixed32)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedFixed64)); + message.setExtension(RepeatedExtensions.repeatedFixed64, fixed64s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedFixed64)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedSfixed64)); + message.setExtension(RepeatedExtensions.repeatedSfixed64, sfixed64s); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedSfixed64)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedBool)); + message.setExtension(RepeatedExtensions.repeatedBool, bools); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedBool)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedFloat)); + message.setExtension(RepeatedExtensions.repeatedFloat, floats); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedFloat)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedDouble)); + message.setExtension(RepeatedExtensions.repeatedDouble, doubles); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedDouble)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedEnum)); + message.setExtension(RepeatedExtensions.repeatedEnum, enums); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedEnum)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedString)); + message.setExtension(RepeatedExtensions.repeatedString, strings); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedString)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedBytes)); + message.setExtension(RepeatedExtensions.repeatedBytes, bytess); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedBytes)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedMessage)); + message.setExtension(RepeatedExtensions.repeatedMessage, messages); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedMessage)); + assertFalse(message.hasExtension(RepeatedExtensions.repeatedGroup)); + message.setExtension(RepeatedExtensions.repeatedGroup, groups); + assertTrue(message.hasExtension(RepeatedExtensions.repeatedGroup)); + + byte[] data = MessageNano.toByteArray(message); + message = Extensions.ExtendableMessage.parseFrom(data); + assertEquals(5, message.field); + + // Test reading back using SingularExtensions: the retrieved value should equal the last + // in each array. + assertEquals(int32s[1], (int) message.getExtension(SingularExtensions.someInt32)); + assertEquals(uint32s[1], (int) message.getExtension(SingularExtensions.someUint32)); + assertEquals(sint32s[1], (int) message.getExtension(SingularExtensions.someSint32)); + assertEquals(int64s[1], (long) message.getExtension(SingularExtensions.someInt64)); + assertEquals(uint64s[1], (long) message.getExtension(SingularExtensions.someUint64)); + assertEquals(sint64s[1], (long) message.getExtension(SingularExtensions.someSint64)); + assertEquals(fixed32s[1], (int) message.getExtension(SingularExtensions.someFixed32)); + assertEquals(sfixed32s[1], (int) message.getExtension(SingularExtensions.someSfixed32)); + assertEquals(fixed64s[1], (long) message.getExtension(SingularExtensions.someFixed64)); + assertEquals(sfixed64s[1], (long) message.getExtension(SingularExtensions.someSfixed64)); + assertEquals(bools[1], (boolean) message.getExtension(SingularExtensions.someBool)); + assertEquals(floats[1], (float) message.getExtension(SingularExtensions.someFloat)); + assertEquals(doubles[1], (double) message.getExtension(SingularExtensions.someDouble)); + assertEquals(enums[1], (int) message.getExtension(SingularExtensions.someEnum)); + assertEquals(strings[1], message.getExtension(SingularExtensions.someString)); + assertTrue(Arrays.equals(bytess[1], message.getExtension(SingularExtensions.someBytes))); + AnotherMessage deserializedMessage = message.getExtension(SingularExtensions.someMessage); + assertEquals(another2.string, deserializedMessage.string); + assertEquals(another2.value, deserializedMessage.value); + assertEquals(group2.a, message.getExtension(SingularExtensions.someGroup).a); + + // Test reading back using RepeatedExtensions: the arrays should be equal. + message = Extensions.ExtendableMessage.parseFrom(data); + assertEquals(5, message.field); + assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); + assertTrue(Arrays.equals(strings, message.getExtension(RepeatedExtensions.repeatedString))); + byte[][] deserializedRepeatedBytes = message.getExtension(RepeatedExtensions.repeatedBytes); + assertEquals(2, deserializedRepeatedBytes.length); + assertTrue(Arrays.equals(bytess[0], deserializedRepeatedBytes[0])); + assertTrue(Arrays.equals(bytess[1], deserializedRepeatedBytes[1])); + AnotherMessage[] deserializedRepeatedMessage = + message.getExtension(RepeatedExtensions.repeatedMessage); + assertEquals(2, deserializedRepeatedMessage.length); + assertEquals(another1.string, deserializedRepeatedMessage[0].string); + assertEquals(another1.value, deserializedRepeatedMessage[0].value); + assertEquals(another2.string, deserializedRepeatedMessage[1].string); + assertEquals(another2.value, deserializedRepeatedMessage[1].value); + RepeatedExtensions.RepeatedGroup[] deserializedRepeatedGroup = + message.getExtension(RepeatedExtensions.repeatedGroup); + assertEquals(2, deserializedRepeatedGroup.length); + assertEquals(group1.a, deserializedRepeatedGroup[0].a); + assertEquals(group2.a, deserializedRepeatedGroup[1].a); + + message = Extensions.ExtendableMessage.parseFrom(data); + assertEquals(5, message.field); + // Test hasExtension using PackedExtensions. + assertTrue(message.hasExtension(PackedExtensions.packedInt32)); + assertTrue(message.hasExtension(PackedExtensions.packedUint32)); + assertTrue(message.hasExtension(PackedExtensions.packedSint32)); + assertTrue(message.hasExtension(PackedExtensions.packedInt64)); + assertTrue(message.hasExtension(PackedExtensions.packedUint64)); + assertTrue(message.hasExtension(PackedExtensions.packedSint64)); + assertTrue(message.hasExtension(PackedExtensions.packedFixed32)); + assertTrue(message.hasExtension(PackedExtensions.packedSfixed32)); + assertTrue(message.hasExtension(PackedExtensions.packedFixed64)); + assertTrue(message.hasExtension(PackedExtensions.packedSfixed64)); + assertTrue(message.hasExtension(PackedExtensions.packedBool)); + assertTrue(message.hasExtension(PackedExtensions.packedFloat)); + assertTrue(message.hasExtension(PackedExtensions.packedDouble)); + assertTrue(message.hasExtension(PackedExtensions.packedEnum)); + + // Test reading back using PackedExtensions: the arrays should be equal, even the fields + // are non-packed. + assertTrue(Arrays.equals(int32s, message.getExtension(PackedExtensions.packedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(PackedExtensions.packedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(PackedExtensions.packedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(PackedExtensions.packedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(PackedExtensions.packedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(PackedExtensions.packedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(PackedExtensions.packedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(PackedExtensions.packedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(PackedExtensions.packedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(PackedExtensions.packedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(PackedExtensions.packedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(PackedExtensions.packedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(PackedExtensions.packedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(PackedExtensions.packedEnum))); + + // Now set the packable extension values using PackedExtensions so they're serialized packed. + message.setExtension(PackedExtensions.packedInt32, int32s); + message.setExtension(PackedExtensions.packedUint32, uint32s); + message.setExtension(PackedExtensions.packedSint32, sint32s); + message.setExtension(PackedExtensions.packedInt64, int64s); + message.setExtension(PackedExtensions.packedUint64, uint64s); + message.setExtension(PackedExtensions.packedSint64, sint64s); + message.setExtension(PackedExtensions.packedFixed32, fixed32s); + message.setExtension(PackedExtensions.packedSfixed32, sfixed32s); + message.setExtension(PackedExtensions.packedFixed64, fixed64s); + message.setExtension(PackedExtensions.packedSfixed64, sfixed64s); + message.setExtension(PackedExtensions.packedBool, bools); + message.setExtension(PackedExtensions.packedFloat, floats); + message.setExtension(PackedExtensions.packedDouble, doubles); + message.setExtension(PackedExtensions.packedEnum, enums); + + // And read back using non-packed RepeatedExtensions. + byte[] data2 = MessageNano.toByteArray(message); + message = MessageNano.mergeFrom(new Extensions.ExtendableMessage(), data2); + assertTrue(Arrays.equals(int32s, message.getExtension(RepeatedExtensions.repeatedInt32))); + assertTrue(Arrays.equals(uint32s, message.getExtension(RepeatedExtensions.repeatedUint32))); + assertTrue(Arrays.equals(sint32s, message.getExtension(RepeatedExtensions.repeatedSint32))); + assertTrue(Arrays.equals(int64s, message.getExtension(RepeatedExtensions.repeatedInt64))); + assertTrue(Arrays.equals(uint64s, message.getExtension(RepeatedExtensions.repeatedUint64))); + assertTrue(Arrays.equals(sint64s, message.getExtension(RepeatedExtensions.repeatedSint64))); + assertTrue(Arrays.equals(fixed32s, message.getExtension(RepeatedExtensions.repeatedFixed32))); + assertTrue(Arrays.equals(sfixed32s, message.getExtension(RepeatedExtensions.repeatedSfixed32))); + assertTrue(Arrays.equals(fixed64s, message.getExtension(RepeatedExtensions.repeatedFixed64))); + assertTrue(Arrays.equals(sfixed64s, message.getExtension(RepeatedExtensions.repeatedSfixed64))); + assertTrue(Arrays.equals(bools, message.getExtension(RepeatedExtensions.repeatedBool))); + assertTrue(Arrays.equals(floats, message.getExtension(RepeatedExtensions.repeatedFloat))); + assertTrue(Arrays.equals(doubles, message.getExtension(RepeatedExtensions.repeatedDouble))); + assertTrue(Arrays.equals(enums, message.getExtension(RepeatedExtensions.repeatedEnum))); + } + + public void testNullExtensions() throws Exception { + // Check that clearing the extension on an empty message is a no-op. + Extensions.ExtendableMessage message = new Extensions.ExtendableMessage(); + assertFalse(message.hasExtension(SingularExtensions.someMessage)); + message.setExtension(SingularExtensions.someMessage, null); + assertFalse(message.hasExtension(SingularExtensions.someMessage)); + assertEquals(0, MessageNano.toByteArray(message).length); + + // Check that the message is empty after setting and clearing an extension. + AnotherMessage another = new AnotherMessage(); + assertFalse(message.hasExtension(SingularExtensions.someMessage)); + message.setExtension(SingularExtensions.someMessage, another); + assertTrue(message.hasExtension(SingularExtensions.someMessage)); + assertTrue(MessageNano.toByteArray(message).length > 0); + message.setExtension(SingularExtensions.someMessage, null); + assertFalse(message.hasExtension(SingularExtensions.someMessage)); + assertEquals(0, MessageNano.toByteArray(message).length); + } + + public void testExtensionsMutation() { + Extensions.ExtendableMessage extendableMessage = new Extensions.ExtendableMessage(); + extendableMessage.setExtension(SingularExtensions.someMessage, + new Extensions.AnotherMessage()); + + extendableMessage.getExtension(SingularExtensions.someMessage).string = "not empty"; + + assertEquals("not empty", + extendableMessage.getExtension(SingularExtensions.someMessage).string); + } + + public void testExtensionsMutation_Equals() throws InvalidProtocolBufferNanoException { + Extensions.ExtendableMessage extendableMessage = new Extensions.ExtendableMessage(); + extendableMessage.field = 5; + int int32 = 42; + int[] uint32s = {3, 4}; + int[] sint32s = {-5, -6}; + long[] int64s = {7, 8}; + long[] uint64s = {9, 10}; + long[] sint64s = {-11, -12}; + int[] fixed32s = {13, 14}; + int[] sfixed32s = {-15, -16}; + long[] fixed64s = {17, 18}; + long[] sfixed64s = {-19, -20}; + boolean[] bools = {true, false}; + float[] floats = {2.1f, 2.2f}; + double[] doubles = {2.3, 2.4}; + int[] enums = {Extensions.SECOND_VALUE, Extensions.FIRST_VALUE}; + String[] strings = {"vijfentwintig", "twenty-six"}; + byte[][] bytess = {{2, 7}, {2, 8}}; + AnotherMessage another1 = new AnotherMessage(); + another1.string = "er shi jiu"; + another1.value = false; + AnotherMessage another2 = new AnotherMessage(); + another2.string = "trente"; + another2.value = true; + AnotherMessage[] messages = {another1, another2}; + RepeatedExtensions.RepeatedGroup group1 = new RepeatedExtensions.RepeatedGroup(); + group1.a = 31; + RepeatedExtensions.RepeatedGroup group2 = new RepeatedExtensions.RepeatedGroup(); + group2.a = 32; + RepeatedExtensions.RepeatedGroup[] groups = {group1, group2}; + extendableMessage.setExtension(SingularExtensions.someInt32, int32); + extendableMessage.setExtension(RepeatedExtensions.repeatedUint32, uint32s); + extendableMessage.setExtension(RepeatedExtensions.repeatedSint32, sint32s); + extendableMessage.setExtension(RepeatedExtensions.repeatedInt64, int64s); + extendableMessage.setExtension(RepeatedExtensions.repeatedUint64, uint64s); + extendableMessage.setExtension(RepeatedExtensions.repeatedSint64, sint64s); + extendableMessage.setExtension(RepeatedExtensions.repeatedFixed32, fixed32s); + extendableMessage.setExtension(RepeatedExtensions.repeatedSfixed32, sfixed32s); + extendableMessage.setExtension(RepeatedExtensions.repeatedFixed64, fixed64s); + extendableMessage.setExtension(RepeatedExtensions.repeatedSfixed64, sfixed64s); + extendableMessage.setExtension(RepeatedExtensions.repeatedBool, bools); + extendableMessage.setExtension(RepeatedExtensions.repeatedFloat, floats); + extendableMessage.setExtension(RepeatedExtensions.repeatedDouble, doubles); + extendableMessage.setExtension(RepeatedExtensions.repeatedEnum, enums); + extendableMessage.setExtension(RepeatedExtensions.repeatedString, strings); + extendableMessage.setExtension(RepeatedExtensions.repeatedBytes, bytess); + extendableMessage.setExtension(RepeatedExtensions.repeatedMessage, messages); + extendableMessage.setExtension(RepeatedExtensions.repeatedGroup, groups); + + byte[] data = MessageNano.toByteArray(extendableMessage); + + extendableMessage = Extensions.ExtendableMessage.parseFrom(data); + Extensions.ExtendableMessage messageCopy = Extensions.ExtendableMessage.parseFrom(data); + + // Without deserialising. + assertEquals(extendableMessage, messageCopy); + assertEquals(extendableMessage.hashCode(), messageCopy.hashCode()); + + // Only one deserialized. + extendableMessage.getExtension(SingularExtensions.someInt32); + extendableMessage.getExtension(RepeatedExtensions.repeatedUint32); + extendableMessage.getExtension(RepeatedExtensions.repeatedSint32); + extendableMessage.getExtension(RepeatedExtensions.repeatedInt64); + extendableMessage.getExtension(RepeatedExtensions.repeatedUint64); + extendableMessage.getExtension(RepeatedExtensions.repeatedSint64); + extendableMessage.getExtension(RepeatedExtensions.repeatedFixed32); + extendableMessage.getExtension(RepeatedExtensions.repeatedSfixed32); + extendableMessage.getExtension(RepeatedExtensions.repeatedFixed64); + extendableMessage.getExtension(RepeatedExtensions.repeatedSfixed64); + extendableMessage.getExtension(RepeatedExtensions.repeatedBool); + extendableMessage.getExtension(RepeatedExtensions.repeatedFloat); + extendableMessage.getExtension(RepeatedExtensions.repeatedDouble); + extendableMessage.getExtension(RepeatedExtensions.repeatedEnum); + extendableMessage.getExtension(RepeatedExtensions.repeatedString); + extendableMessage.getExtension(RepeatedExtensions.repeatedBytes); + extendableMessage.getExtension(RepeatedExtensions.repeatedMessage); + extendableMessage.getExtension(RepeatedExtensions.repeatedGroup); + assertEquals(extendableMessage, messageCopy); + assertEquals(extendableMessage.hashCode(), messageCopy.hashCode()); + + // Both deserialized. + messageCopy.getExtension(SingularExtensions.someInt32); + messageCopy.getExtension(RepeatedExtensions.repeatedUint32); + messageCopy.getExtension(RepeatedExtensions.repeatedSint32); + messageCopy.getExtension(RepeatedExtensions.repeatedInt64); + messageCopy.getExtension(RepeatedExtensions.repeatedUint64); + messageCopy.getExtension(RepeatedExtensions.repeatedSint64); + messageCopy.getExtension(RepeatedExtensions.repeatedFixed32); + messageCopy.getExtension(RepeatedExtensions.repeatedSfixed32); + messageCopy.getExtension(RepeatedExtensions.repeatedFixed64); + messageCopy.getExtension(RepeatedExtensions.repeatedSfixed64); + messageCopy.getExtension(RepeatedExtensions.repeatedBool); + messageCopy.getExtension(RepeatedExtensions.repeatedFloat); + messageCopy.getExtension(RepeatedExtensions.repeatedDouble); + messageCopy.getExtension(RepeatedExtensions.repeatedEnum); + messageCopy.getExtension(RepeatedExtensions.repeatedString); + messageCopy.getExtension(RepeatedExtensions.repeatedBytes); + messageCopy.getExtension(RepeatedExtensions.repeatedMessage); + messageCopy.getExtension(RepeatedExtensions.repeatedGroup); + assertEquals(extendableMessage, messageCopy); + assertEquals(extendableMessage.hashCode(), messageCopy.hashCode()); + + // Change one, make sure they are still different. + messageCopy.getExtension(RepeatedExtensions.repeatedMessage)[0].string = "not empty"; + assertFalse(extendableMessage.equals(messageCopy)); + + // Even if the extension hasn't been deserialized. + extendableMessage = Extensions.ExtendableMessage.parseFrom(data); + assertFalse(extendableMessage.equals(messageCopy)); + } + + public void testExtensionsCaching() { + Extensions.ExtendableMessage extendableMessage = new Extensions.ExtendableMessage(); + extendableMessage.setExtension(SingularExtensions.someMessage, + new Extensions.AnotherMessage()); + assertSame("Consecutive calls to getExtensions should return the same object", + extendableMessage.getExtension(SingularExtensions.someMessage), + extendableMessage.getExtension(SingularExtensions.someMessage)); + } + + public void testUnknownFields() throws Exception { + // Check that we roundtrip (serialize and deserialize) unrecognized fields. + AnotherMessage message = new AnotherMessage(); + message.string = "Hello World"; + message.value = false; + + byte[] bytes = MessageNano.toByteArray(message); + int extraFieldSize = CodedOutputByteBufferNano.computeStringSize( + 1001, "This is an unknown field"); + byte[] newBytes = new byte[bytes.length + extraFieldSize]; + System.arraycopy(bytes, 0, newBytes, 0, bytes.length); + CodedOutputByteBufferNano.newInstance(newBytes, bytes.length, extraFieldSize) + .writeString(1001, "This is an unknown field"); + + // Deserialize with an unknown field. + AnotherMessage deserialized = AnotherMessage.parseFrom(newBytes); + byte[] serialized = MessageNano.toByteArray(deserialized); + + assertEquals(newBytes.length, serialized.length); + + // Clear, and make sure it clears everything. + deserialized.clear(); + assertEquals(0, MessageNano.toByteArray(deserialized).length); + } + + public void testMergeFrom() throws Exception { + SimpleMessageNano message = new SimpleMessageNano(); + message.d = 123; + byte[] bytes = MessageNano.toByteArray(message); + + SimpleMessageNano newMessage = MessageNano.mergeFrom(new SimpleMessageNano(), bytes); + assertEquals(message.d, newMessage.d); + } + + public void testJavaKeyword() throws Exception { + TestAllTypesNano msg = new TestAllTypesNano(); + msg.synchronized_ = 123; + assertEquals(123, msg.synchronized_); + } + + public void testReferenceTypesForPrimitives() throws Exception { + NanoReferenceTypes.TestAllTypesNano message = new NanoReferenceTypes.TestAllTypesNano(); + + // Base check - when nothing is set, we serialize nothing. + assertHasWireData(message, false); + + message.defaultBool = true; + assertHasWireData(message, true); + + message.defaultBool = false; + assertHasWireData(message, true); + + message.defaultBool = null; + assertHasWireData(message, false); + + message.defaultInt32 = 5; + assertHasWireData(message, true); + + message.defaultInt32 = null; + assertHasWireData(message, false); + + message.defaultInt64 = 123456L; + assertHasWireData(message, true); + + message.defaultInt64 = null; + assertHasWireData(message, false); + + message.defaultFloat = 1f; + assertHasWireData(message, true); + + message.defaultFloat = null; + assertHasWireData(message, false); + + message.defaultDouble = 2.1; + assertHasWireData(message, true); + + message.defaultDouble = null; + assertHasWireData(message, false); + + message.defaultString = "hello"; + assertHasWireData(message, true); + + message.defaultString = null; + assertHasWireData(message, false); + + message.defaultBytes = new byte[] { 1, 2, 3 }; + assertHasWireData(message, true); + + message.defaultBytes = null; + assertHasWireData(message, false); + } + + public void testHashCodeEquals() throws Exception { + // Complete equality: + TestAllTypesNano a = createMessageForHashCodeEqualsTest(); + TestAllTypesNano aEquivalent = createMessageForHashCodeEqualsTest(); + + assertTrue(MessageNano.messageNanoEquals(a, aEquivalent)); + assertFalse(MessageNano.messageNanoEquals(a, new TestAllTypesNano())); + + // Null and empty array for repeated fields equality: + TestAllTypesNano b = createMessageForHashCodeEqualsTest(); + b.repeatedBool = null; + b.repeatedFloat = new float[0]; + TestAllTypesNano bEquivalent = createMessageForHashCodeEqualsTest(); + bEquivalent.repeatedBool = new boolean[0]; + bEquivalent.repeatedFloat = null; + + // Ref-element-type repeated fields use non-null subsequence equality: + TestAllTypesNano c = createMessageForHashCodeEqualsTest(); + c.repeatedString = null; + c.repeatedStringPiece = new String[] {null, "one", null, "two"}; + c.repeatedBytes = new byte[][] {{3, 4}, null}; + TestAllTypesNano cEquivalent = createMessageForHashCodeEqualsTest(); + cEquivalent.repeatedString = new String[3]; + cEquivalent.repeatedStringPiece = new String[] {"one", "two", null}; + cEquivalent.repeatedBytes = new byte[][] {{3, 4}}; + + // Complete equality for messages with has fields: + TestAllTypesNanoHas d = createMessageWithHasForHashCodeEqualsTest(); + TestAllTypesNanoHas dEquivalent = createMessageWithHasForHashCodeEqualsTest(); + + // If has-fields exist, fields with the same default values but + // different has-field values are different. + TestAllTypesNanoHas e = createMessageWithHasForHashCodeEqualsTest(); + e.optionalInt32++; // make different from d + e.hasDefaultString = false; + TestAllTypesNanoHas eDifferent = createMessageWithHasForHashCodeEqualsTest(); + eDifferent.optionalInt32 = e.optionalInt32; + eDifferent.hasDefaultString = true; + + // Complete equality for messages with accessors: + TestNanoAccessors f = createMessageWithAccessorsForHashCodeEqualsTest(); + TestNanoAccessors fEquivalent = createMessageWithAccessorsForHashCodeEqualsTest(); + + // If using accessors, explicitly setting a field to its default value + // should make the message different. + TestNanoAccessors g = createMessageWithAccessorsForHashCodeEqualsTest(); + g.setOptionalInt32(g.getOptionalInt32() + 1); // make different from f + g.clearDefaultString(); + TestNanoAccessors gDifferent = createMessageWithAccessorsForHashCodeEqualsTest(); + gDifferent.setOptionalInt32(g.getOptionalInt32()); + gDifferent.setDefaultString(g.getDefaultString()); + + // Complete equality for reference typed messages: + NanoReferenceTypes.TestAllTypesNano h = createRefTypedMessageForHashCodeEqualsTest(); + NanoReferenceTypes.TestAllTypesNano hEquivalent = createRefTypedMessageForHashCodeEqualsTest(); + + // Inequality of null and default value for reference typed messages: + NanoReferenceTypes.TestAllTypesNano i = createRefTypedMessageForHashCodeEqualsTest(); + i.optionalInt32 = 1; // make different from h + i.optionalFloat = null; + NanoReferenceTypes.TestAllTypesNano iDifferent = createRefTypedMessageForHashCodeEqualsTest(); + iDifferent.optionalInt32 = i.optionalInt32; + iDifferent.optionalFloat = 0.0f; + + HashMap<MessageNano, String> hashMap = new HashMap<MessageNano, String>(); + hashMap.put(a, "a"); + hashMap.put(b, "b"); + hashMap.put(c, "c"); + hashMap.put(d, "d"); + hashMap.put(e, "e"); + hashMap.put(f, "f"); + hashMap.put(g, "g"); + hashMap.put(h, "h"); + hashMap.put(i, "i"); + + assertEquals(9, hashMap.size()); // a-i should be different from each other. + + assertEquals("a", hashMap.get(a)); + assertEquals("a", hashMap.get(aEquivalent)); + + assertEquals("b", hashMap.get(b)); + assertEquals("b", hashMap.get(bEquivalent)); + + assertEquals("c", hashMap.get(c)); + assertEquals("c", hashMap.get(cEquivalent)); + + assertEquals("d", hashMap.get(d)); + assertEquals("d", hashMap.get(dEquivalent)); + + assertEquals("e", hashMap.get(e)); + assertNull(hashMap.get(eDifferent)); + + assertEquals("f", hashMap.get(f)); + assertEquals("f", hashMap.get(fEquivalent)); + + assertEquals("g", hashMap.get(g)); + assertNull(hashMap.get(gDifferent)); + + assertEquals("h", hashMap.get(h)); + assertEquals("h", hashMap.get(hEquivalent)); + + assertEquals("i", hashMap.get(i)); + assertNull(hashMap.get(iDifferent)); + } + + private TestAllTypesNano createMessageForHashCodeEqualsTest() { + TestAllTypesNano message = new TestAllTypesNano(); + message.optionalInt32 = 5; + message.optionalInt64 = 777; + message.optionalFloat = 1.0f; + message.optionalDouble = 2.0; + message.optionalBool = true; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = new TestAllTypesNano.NestedMessage(); + message.optionalNestedMessage.bb = 27; + message.optionalNestedEnum = TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedInt64 = new long[] { 27L, 28L, 29L }; + message.repeatedFloat = new float[] { 5.0f, 6.0f }; + message.repeatedDouble = new double[] { 99.1, 22.5 }; + message.repeatedBool = new boolean[] { true, false, true }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + return message; + } + + private TestAllTypesNanoHas createMessageWithHasForHashCodeEqualsTest() { + TestAllTypesNanoHas message = new TestAllTypesNanoHas(); + message.optionalInt32 = 5; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = new TestAllTypesNanoHas.NestedMessage(); + message.optionalNestedMessage.bb = 27; + message.optionalNestedEnum = TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestAllTypesNanoHas.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + return message; + } + + private TestNanoAccessors createMessageWithAccessorsForHashCodeEqualsTest() { + TestNanoAccessors message = new TestNanoAccessors() + .setOptionalInt32(5) + .setOptionalString("Hello") + .setOptionalBytes(new byte[] {1, 2, 3}) + .setOptionalNestedEnum(TestNanoAccessors.BAR); + message.optionalNestedMessage = new TestNanoAccessors.NestedMessage().setBb(27); + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = new TestNanoAccessors.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + TestAllTypesNano.BAR, + TestAllTypesNano.BAZ + }; + return message; + } + + private NanoReferenceTypes.TestAllTypesNano createRefTypedMessageForHashCodeEqualsTest() { + NanoReferenceTypes.TestAllTypesNano message = new NanoReferenceTypes.TestAllTypesNano(); + message.optionalInt32 = 5; + message.optionalInt64 = 777L; + message.optionalFloat = 1.0f; + message.optionalDouble = 2.0; + message.optionalBool = true; + message.optionalString = "Hello"; + message.optionalBytes = new byte[] { 1, 2, 3 }; + message.optionalNestedMessage = + new NanoReferenceTypes.TestAllTypesNano.NestedMessage(); + message.optionalNestedMessage.foo = 27; + message.optionalNestedEnum = NanoReferenceTypes.TestAllTypesNano.BAR; + message.repeatedInt32 = new int[] { 5, 6, 7, 8 }; + message.repeatedInt64 = new long[] { 27L, 28L, 29L }; + message.repeatedFloat = new float[] { 5.0f, 6.0f }; + message.repeatedDouble = new double[] { 99.1, 22.5 }; + message.repeatedBool = new boolean[] { true, false, true }; + message.repeatedString = new String[] { "One", "Two" }; + message.repeatedBytes = new byte[][] { { 2, 7 }, { 2, 7 } }; + message.repeatedNestedMessage = + new NanoReferenceTypes.TestAllTypesNano.NestedMessage[] { + message.optionalNestedMessage, + message.optionalNestedMessage + }; + message.repeatedNestedEnum = new int[] { + NanoReferenceTypes.TestAllTypesNano.BAR, + NanoReferenceTypes.TestAllTypesNano.BAZ + }; + return message; + } + + public void testEqualsWithSpecialFloatingPointValues() throws Exception { + // Checks that the nano implementation complies with Object.equals() when treating + // floating point numbers, i.e. NaN == NaN and +0.0 != -0.0. + // This test assumes that the generated equals() implementations are symmetric, so + // there will only be one direction for each equality check. + + TestAllTypesNano m1 = new TestAllTypesNano(); + m1.optionalFloat = Float.NaN; + m1.optionalDouble = Double.NaN; + TestAllTypesNano m2 = new TestAllTypesNano(); + m2.optionalFloat = Float.NaN; + m2.optionalDouble = Double.NaN; + assertTrue(m1.equals(m2)); + assertTrue(m1.equals( + MessageNano.mergeFrom(new TestAllTypesNano(), MessageNano.toByteArray(m1)))); + + m1.optionalFloat = +0f; + m2.optionalFloat = -0f; + assertFalse(m1.equals(m2)); + + m1.optionalFloat = -0f; + m1.optionalDouble = +0d; + m2.optionalDouble = -0d; + assertFalse(m1.equals(m2)); + + m1.optionalDouble = -0d; + assertTrue(m1.equals(m2)); + assertFalse(m1.equals(new TestAllTypesNano())); // -0 does not equals() the default +0 + assertTrue(m1.equals( + MessageNano.mergeFrom(new TestAllTypesNano(), MessageNano.toByteArray(m1)))); + + // ------- + + TestAllTypesNanoHas m3 = new TestAllTypesNanoHas(); + m3.optionalFloat = Float.NaN; + m3.hasOptionalFloat = true; + m3.optionalDouble = Double.NaN; + m3.hasOptionalDouble = true; + TestAllTypesNanoHas m4 = new TestAllTypesNanoHas(); + m4.optionalFloat = Float.NaN; + m4.hasOptionalFloat = true; + m4.optionalDouble = Double.NaN; + m4.hasOptionalDouble = true; + assertTrue(m3.equals(m4)); + assertTrue(m3.equals( + MessageNano.mergeFrom(new TestAllTypesNanoHas(), MessageNano.toByteArray(m3)))); + + m3.optionalFloat = +0f; + m4.optionalFloat = -0f; + assertFalse(m3.equals(m4)); + + m3.optionalFloat = -0f; + m3.optionalDouble = +0d; + m4.optionalDouble = -0d; + assertFalse(m3.equals(m4)); + + m3.optionalDouble = -0d; + m3.hasOptionalFloat = false; // -0 does not equals() the default +0, + m3.hasOptionalDouble = false; // so these incorrect 'has' flags should be disregarded. + assertTrue(m3.equals(m4)); // note: m4 has the 'has' flags set. + assertFalse(m3.equals(new TestAllTypesNanoHas())); // note: the new message has +0 defaults + assertTrue(m3.equals( + MessageNano.mergeFrom(new TestAllTypesNanoHas(), MessageNano.toByteArray(m3)))); + // note: the deserialized message has the 'has' flags set. + + // ------- + + TestNanoAccessors m5 = new TestNanoAccessors(); + m5.setOptionalFloat(Float.NaN); + m5.setOptionalDouble(Double.NaN); + TestNanoAccessors m6 = new TestNanoAccessors(); + m6.setOptionalFloat(Float.NaN); + m6.setOptionalDouble(Double.NaN); + assertTrue(m5.equals(m6)); + assertTrue(m5.equals( + MessageNano.mergeFrom(new TestNanoAccessors(), MessageNano.toByteArray(m6)))); + + m5.setOptionalFloat(+0f); + m6.setOptionalFloat(-0f); + assertFalse(m5.equals(m6)); + + m5.setOptionalFloat(-0f); + m5.setOptionalDouble(+0d); + m6.setOptionalDouble(-0d); + assertFalse(m5.equals(m6)); + + m5.setOptionalDouble(-0d); + assertTrue(m5.equals(m6)); + assertFalse(m5.equals(new TestNanoAccessors())); + assertTrue(m5.equals( + MessageNano.mergeFrom(new TestNanoAccessors(), MessageNano.toByteArray(m6)))); + + // ------- + + NanoReferenceTypes.TestAllTypesNano m7 = new NanoReferenceTypes.TestAllTypesNano(); + m7.optionalFloat = Float.NaN; + m7.optionalDouble = Double.NaN; + NanoReferenceTypes.TestAllTypesNano m8 = new NanoReferenceTypes.TestAllTypesNano(); + m8.optionalFloat = Float.NaN; + m8.optionalDouble = Double.NaN; + assertTrue(m7.equals(m8)); + assertTrue(m7.equals(MessageNano.mergeFrom( + new NanoReferenceTypes.TestAllTypesNano(), MessageNano.toByteArray(m7)))); + + m7.optionalFloat = +0f; + m8.optionalFloat = -0f; + assertFalse(m7.equals(m8)); + + m7.optionalFloat = -0f; + m7.optionalDouble = +0d; + m8.optionalDouble = -0d; + assertFalse(m7.equals(m8)); + + m7.optionalDouble = -0d; + assertTrue(m7.equals(m8)); + assertFalse(m7.equals(new NanoReferenceTypes.TestAllTypesNano())); + assertTrue(m7.equals(MessageNano.mergeFrom( + new NanoReferenceTypes.TestAllTypesNano(), MessageNano.toByteArray(m7)))); + } + + public void testNullRepeatedFields() throws Exception { + // Check that serialization after explicitly setting a repeated field + // to null doesn't NPE. + TestAllTypesNano message = new TestAllTypesNano(); + message.repeatedInt32 = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + message.repeatedNestedEnum = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + message.repeatedBytes = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + message.repeatedNestedMessage = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + message.repeatedPackedInt32 = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + message.repeatedPackedNestedEnum = null; + MessageNano.toByteArray(message); // should not NPE + message.toString(); // should not NPE + + // Create a second message to merge into message. + TestAllTypesNano secondMessage = new TestAllTypesNano(); + secondMessage.repeatedInt32 = new int[] {1, 2, 3}; + secondMessage.repeatedNestedEnum = new int[] { + TestAllTypesNano.FOO, TestAllTypesNano.BAR + }; + secondMessage.repeatedBytes = new byte[][] {{1, 2}, {3, 4}}; + TestAllTypesNano.NestedMessage nested = + new TestAllTypesNano.NestedMessage(); + nested.bb = 55; + secondMessage.repeatedNestedMessage = + new TestAllTypesNano.NestedMessage[] {nested}; + secondMessage.repeatedPackedInt32 = new int[] {1, 2, 3}; + secondMessage.repeatedPackedNestedEnum = new int[] { + TestAllTypesNano.FOO, TestAllTypesNano.BAR + }; + + // Should not NPE + message.mergeFrom(CodedInputByteBufferNano.newInstance( + MessageNano.toByteArray(secondMessage))); + assertEquals(3, message.repeatedInt32.length); + assertEquals(3, message.repeatedInt32[2]); + assertEquals(2, message.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.FOO, message.repeatedNestedEnum[0]); + assertEquals(2, message.repeatedBytes.length); + assertEquals(4, message.repeatedBytes[1][1]); + assertEquals(1, message.repeatedNestedMessage.length); + assertEquals(55, message.repeatedNestedMessage[0].bb); + assertEquals(3, message.repeatedPackedInt32.length); + assertEquals(2, message.repeatedPackedInt32[1]); + assertEquals(2, message.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.BAR, message.repeatedPackedNestedEnum[1]); + } + + public void testNullRepeatedFieldElements() throws Exception { + // Check that serialization with null array elements doesn't NPE. + String string1 = "1"; + String string2 = "2"; + byte[] bytes1 = {3, 4}; + byte[] bytes2 = {5, 6}; + TestAllTypesNano.NestedMessage msg1 = new TestAllTypesNano.NestedMessage(); + msg1.bb = 7; + TestAllTypesNano.NestedMessage msg2 = new TestAllTypesNano.NestedMessage(); + msg2.bb = 8; + + TestAllTypesNano message = new TestAllTypesNano(); + message.repeatedString = new String[] {null, string1, string2}; + message.repeatedBytes = new byte[][] {bytes1, null, bytes2}; + message.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] {msg1, msg2, null}; + message.repeatedGroup = new TestAllTypesNano.RepeatedGroup[] {null, null, null}; + + byte[] serialized = MessageNano.toByteArray(message); // should not NPE + TestAllTypesNano deserialized = MessageNano.mergeFrom(new TestAllTypesNano(), serialized); + assertEquals(2, deserialized.repeatedString.length); + assertEquals(string1, deserialized.repeatedString[0]); + assertEquals(string2, deserialized.repeatedString[1]); + assertEquals(2, deserialized.repeatedBytes.length); + assertTrue(Arrays.equals(bytes1, deserialized.repeatedBytes[0])); + assertTrue(Arrays.equals(bytes2, deserialized.repeatedBytes[1])); + assertEquals(2, deserialized.repeatedNestedMessage.length); + assertEquals(msg1.bb, deserialized.repeatedNestedMessage[0].bb); + assertEquals(msg2.bb, deserialized.repeatedNestedMessage[1].bb); + assertEquals(0, deserialized.repeatedGroup.length); + } + + public void testRepeatedMerge() throws Exception { + // Check that merging repeated fields cause the arrays to expand with + // new data. + TestAllTypesNano first = new TestAllTypesNano(); + first.repeatedInt32 = new int[] {1, 2, 3}; + TestAllTypesNano second = new TestAllTypesNano(); + second.repeatedInt32 = new int[] {4, 5}; + MessageNano.mergeFrom(first, MessageNano.toByteArray(second)); + assertEquals(5, first.repeatedInt32.length); + assertEquals(1, first.repeatedInt32[0]); + assertEquals(4, first.repeatedInt32[3]); + + first = new TestAllTypesNano(); + first.repeatedNestedEnum = new int[] {TestAllTypesNano.BAR}; + second = new TestAllTypesNano(); + second.repeatedNestedEnum = new int[] {TestAllTypesNano.FOO}; + MessageNano.mergeFrom(first, MessageNano.toByteArray(second)); + assertEquals(2, first.repeatedNestedEnum.length); + assertEquals(TestAllTypesNano.BAR, first.repeatedNestedEnum[0]); + assertEquals(TestAllTypesNano.FOO, first.repeatedNestedEnum[1]); + + first = new TestAllTypesNano(); + first.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { + new TestAllTypesNano.NestedMessage() + }; + first.repeatedNestedMessage[0].bb = 3; + second = new TestAllTypesNano(); + second.repeatedNestedMessage = new TestAllTypesNano.NestedMessage[] { + new TestAllTypesNano.NestedMessage() + }; + second.repeatedNestedMessage[0].bb = 5; + MessageNano.mergeFrom(first, MessageNano.toByteArray(second)); + assertEquals(2, first.repeatedNestedMessage.length); + assertEquals(3, first.repeatedNestedMessage[0].bb); + assertEquals(5, first.repeatedNestedMessage[1].bb); + + first = new TestAllTypesNano(); + first.repeatedPackedSfixed64 = new long[] {-1, -2, -3}; + second = new TestAllTypesNano(); + second.repeatedPackedSfixed64 = new long[] {-4, -5}; + MessageNano.mergeFrom(first, MessageNano.toByteArray(second)); + assertEquals(5, first.repeatedPackedSfixed64.length); + assertEquals(-1, first.repeatedPackedSfixed64[0]); + assertEquals(-4, first.repeatedPackedSfixed64[3]); + + first = new TestAllTypesNano(); + first.repeatedPackedNestedEnum = new int[] {TestAllTypesNano.BAR}; + second = new TestAllTypesNano(); + second.repeatedPackedNestedEnum = new int[] {TestAllTypesNano.FOO}; + MessageNano.mergeFrom(first, MessageNano.toByteArray(second)); + assertEquals(2, first.repeatedPackedNestedEnum.length); + assertEquals(TestAllTypesNano.BAR, first.repeatedPackedNestedEnum[0]); + assertEquals(TestAllTypesNano.FOO, first.repeatedPackedNestedEnum[1]); + + // Now test repeated merging in a nested scope + TestRepeatedMergeNano firstContainer = new TestRepeatedMergeNano(); + firstContainer.contained = new TestAllTypesNano(); + firstContainer.contained.repeatedInt32 = new int[] {10, 20}; + TestRepeatedMergeNano secondContainer = new TestRepeatedMergeNano(); + secondContainer.contained = new TestAllTypesNano(); + secondContainer.contained.repeatedInt32 = new int[] {30}; + MessageNano.mergeFrom(firstContainer, MessageNano.toByteArray(secondContainer)); + assertEquals(3, firstContainer.contained.repeatedInt32.length); + assertEquals(20, firstContainer.contained.repeatedInt32[1]); + assertEquals(30, firstContainer.contained.repeatedInt32[2]); + } + + public void testRepeatedPackables() throws Exception { + // Check that repeated fields with packable types can accept both packed and unpacked + // serialized forms. + NanoRepeatedPackables.NonPacked nonPacked = new NanoRepeatedPackables.NonPacked(); + // Exaggerates the first values of varint-typed arrays. This is to test that the parsing code + // of packed fields handles non-packed data correctly. If the code incorrectly thinks it is + // reading from a packed tag, it will read the first value as the byte length of the field, + // and the large number will cause the input to go out of bounds, thus capturing the error. + nonPacked.int32S = new int[] {1000, 2, 3}; + nonPacked.int64S = new long[] {4000, 5, 6}; + nonPacked.uint32S = new int[] {7000, 8, 9}; + nonPacked.uint64S = new long[] {10000, 11, 12}; + nonPacked.sint32S = new int[] {13000, 14, 15}; + nonPacked.sint64S = new long[] {16000, 17, 18}; + nonPacked.fixed32S = new int[] {19, 20, 21}; + nonPacked.fixed64S = new long[] {22, 23, 24}; + nonPacked.sfixed32S = new int[] {25, 26, 27}; + nonPacked.sfixed64S = new long[] {28, 29, 30}; + nonPacked.floats = new float[] {31, 32, 33}; + nonPacked.doubles = new double[] {34, 35, 36}; + nonPacked.bools = new boolean[] {false, true}; + nonPacked.enums = new int[] { + NanoRepeatedPackables.Enum.OPTION_ONE, + NanoRepeatedPackables.Enum.OPTION_TWO, + }; + nonPacked.noise = 13579; + + byte[] nonPackedSerialized = MessageNano.toByteArray(nonPacked); + + NanoRepeatedPackables.Packed packed = + MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), nonPackedSerialized); + assertRepeatedPackablesEqual(nonPacked, packed); + + byte[] packedSerialized = MessageNano.toByteArray(packed); + // Just a cautious check that the two serialized forms are different, + // to make sure the remaining of this test is useful: + assertFalse(Arrays.equals(nonPackedSerialized, packedSerialized)); + + nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), packedSerialized); + assertRepeatedPackablesEqual(nonPacked, packed); + + // Test mixed serialized form. + byte[] mixedSerialized = new byte[nonPackedSerialized.length + packedSerialized.length]; + System.arraycopy(nonPackedSerialized, 0, mixedSerialized, 0, nonPackedSerialized.length); + System.arraycopy(packedSerialized, 0, + mixedSerialized, nonPackedSerialized.length, packedSerialized.length); + + nonPacked = MessageNano.mergeFrom(new NanoRepeatedPackables.NonPacked(), mixedSerialized); + packed = MessageNano.mergeFrom(new NanoRepeatedPackables.Packed(), mixedSerialized); + assertRepeatedPackablesEqual(nonPacked, packed); + assertTrue(Arrays.equals(new int[] {1000, 2, 3, 1000, 2, 3}, nonPacked.int32S)); + assertTrue(Arrays.equals(new int[] {13000, 14, 15, 13000, 14, 15}, nonPacked.sint32S)); + assertTrue(Arrays.equals(new int[] {25, 26, 27, 25, 26, 27}, nonPacked.sfixed32S)); + assertTrue(Arrays.equals(new boolean[] {false, true, false, true}, nonPacked.bools)); + } + + private void assertRepeatedPackablesEqual( + NanoRepeatedPackables.NonPacked nonPacked, NanoRepeatedPackables.Packed packed) { + // Not using MessageNano.equals() -- that belongs to a separate test. + assertTrue(Arrays.equals(nonPacked.int32S, packed.int32S)); + assertTrue(Arrays.equals(nonPacked.int64S, packed.int64S)); + assertTrue(Arrays.equals(nonPacked.uint32S, packed.uint32S)); + assertTrue(Arrays.equals(nonPacked.uint64S, packed.uint64S)); + assertTrue(Arrays.equals(nonPacked.sint32S, packed.sint32S)); + assertTrue(Arrays.equals(nonPacked.sint64S, packed.sint64S)); + assertTrue(Arrays.equals(nonPacked.fixed32S, packed.fixed32S)); + assertTrue(Arrays.equals(nonPacked.fixed64S, packed.fixed64S)); + assertTrue(Arrays.equals(nonPacked.sfixed32S, packed.sfixed32S)); + assertTrue(Arrays.equals(nonPacked.sfixed64S, packed.sfixed64S)); + assertTrue(Arrays.equals(nonPacked.floats, packed.floats)); + assertTrue(Arrays.equals(nonPacked.doubles, packed.doubles)); + assertTrue(Arrays.equals(nonPacked.bools, packed.bools)); + assertTrue(Arrays.equals(nonPacked.enums, packed.enums)); + } + + private void assertHasWireData(MessageNano message, boolean expected) { + byte[] bytes = MessageNano.toByteArray(message); + int wireLength = bytes.length; + if (expected) { + assertFalse(wireLength == 0); + } else { + if (wireLength != 0) { + fail("Expected no wire data for message \n" + message + + "\nBut got:\n" + + hexDump(bytes)); + } + } + } + + private static String hexDump(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + sb.append(String.format("%02x ", b)); + } + return sb.toString(); + } +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_accessors_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_accessors_nano.proto new file mode 100644 index 00000000..f1d4d343 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_accessors_nano.proto @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoAccessorsOuterClass"; + +message TestNanoAccessors { + + message NestedMessage { + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + // Singular + optional int32 optional_int32 = 1; + optional float optional_float = 11; + optional double optional_double = 12; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional NestedMessage optional_nested_message = 18; + + optional NestedEnum optional_nested_enum = 21; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated NestedMessage repeated_nested_message = 48; + + repeated NestedEnum repeated_nested_enum = 51; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + optional float default_float_nan = 99 [default = nan]; + + optional NestedEnum default_nested_enum = 81 [default = BAR]; + + // Required + required int32 id = 86; + + // Add enough optional fields to make 2 bit fields in total + optional int32 filler100 = 100; + optional int32 filler101 = 101; + optional int32 filler102 = 102; + optional int32 filler103 = 103; + optional int32 filler104 = 104; + optional int32 filler105 = 105; + optional int32 filler106 = 106; + optional int32 filler107 = 107; + optional int32 filler108 = 108; + optional int32 filler109 = 109; + optional int32 filler110 = 110; + optional int32 filler111 = 111; + optional int32 filler112 = 112; + optional int32 filler113 = 113; + optional int32 filler114 = 114; + optional int32 filler115 = 115; + optional int32 filler116 = 116; + optional int32 filler117 = 117; + optional int32 filler118 = 118; + optional int32 filler119 = 119; + optional int32 filler120 = 120; + optional int32 filler121 = 121; + optional int32 filler122 = 122; + optional int32 filler123 = 123; + optional int32 filler124 = 124; + optional int32 filler125 = 125; + optional int32 filler126 = 126; + optional int32 filler127 = 127; + optional int32 filler128 = 128; + optional int32 filler129 = 129; + optional int32 filler130 = 130; + + optional int32 before_bit_field_check = 139; + optional int32 bit_field_check = 140; + optional int32 after_bit_field_check = 141; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_multiple_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_multiple_nano.proto new file mode 100644 index 00000000..8adb7560 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_multiple_nano.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_multiple_files = true; + +enum FileScopeEnumMultiple { + THREE = 3; +} + +message EnumClassNanoMultiple { + enum MessageScopeEnumMultiple { + FOUR = 4; + } + optional FileScopeEnumMultiple three = 3 [ default = THREE ]; + optional MessageScopeEnumMultiple four = 4 [ default = FOUR ]; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto new file mode 100644 index 00000000..3727d68d --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_class_nano.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "EnumClassNanos"; + +enum FileScopeEnum { + ONE = 1; +} + +message EnumClassNano { + enum MessageScopeEnum { + TWO = 2; + } + optional FileScopeEnum one = 1 [ default = ONE ]; + optional MessageScopeEnum two = 2 [ default = TWO ]; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_validity_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_validity_nano.proto new file mode 100644 index 00000000..f7f57427 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_enum_validity_nano.proto @@ -0,0 +1,28 @@ +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "EnumValidity"; + +enum E { + default = 1; // test java keyword renaming + FOO = 2; + BAR = 3; + BAZ = 4; +} + +message M { + optional E optional_e = 1; + optional E default_e = 2 [ default = BAZ ]; + repeated E repeated_e = 3; + repeated E packed_e = 4 [ packed = true ]; + repeated E repeated_e2 = 5; + repeated E packed_e2 = 6 [ packed = true ]; + repeated E repeated_e3 = 7; + repeated E packed_e3 = 8 [ packed = true ]; +} + +message Alt { + optional E repeated_e2_as_optional = 5; + repeated E packed_e2_as_non_packed = 6; + repeated E non_packed_e3_as_packed = 7 [ packed = true ]; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto new file mode 100644 index 00000000..2a678a80 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_nano.proto @@ -0,0 +1,33 @@ +syntax = "proto2"; + +option java_outer_classname = "Extensions"; +option java_package = "com.google.protobuf.nano.testext"; + +message ExtendableMessage { + optional int32 field = 1; + extensions 10 to max; +} + +enum AnEnum { + FIRST_VALUE = 1; + SECOND_VALUE = 2; +} + +message AnotherMessage { + optional string string = 1; + optional bool value = 2; +} + +message ContainerMessage { + extend ExtendableMessage { + optional bool another_thing = 100; + } +} + +// For testNanoOptionalGroupWithUnknownFieldsEnabled; +// not part of the extensions tests. +message MessageWithGroup { + optional group Group = 1 { + optional int32 a = 2; + } +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto new file mode 100644 index 00000000..7d47682d --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_packed_nano.proto @@ -0,0 +1,29 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/nano/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message PackedExtensions { + extend ExtendableMessage { + repeated int32 packed_int32 = 10 [ packed = true ]; + repeated uint32 packed_uint32 = 11 [ packed = true ]; + repeated sint32 packed_sint32 = 12 [ packed = true ]; + repeated int64 packed_int64 = 13 [ packed = true ]; + repeated uint64 packed_uint64 = 14 [ packed = true ]; + repeated sint64 packed_sint64 = 15 [ packed = true ]; + repeated fixed32 packed_fixed32 = 16 [ packed = true ]; + repeated sfixed32 packed_sfixed32 = 17 [ packed = true ]; + repeated fixed64 packed_fixed64 = 18 [ packed = true ]; + repeated sfixed64 packed_sfixed64 = 19 [ packed = true ]; + repeated bool packed_bool = 20 [ packed = true ]; + repeated float packed_float = 21 [ packed = true ]; + repeated double packed_double = 22 [ packed = true ]; + repeated AnEnum packed_enum = 23 [ packed = true ]; + // Non-packable types omitted. + } +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto new file mode 100644 index 00000000..6d4b5dfb --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_repeated_nano.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/nano/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message RepeatedExtensions { + extend ExtendableMessage { + repeated int32 repeated_int32 = 10; + repeated uint32 repeated_uint32 = 11; + repeated sint32 repeated_sint32 = 12; + repeated int64 repeated_int64 = 13; + repeated uint64 repeated_uint64 = 14; + repeated sint64 repeated_sint64 = 15; + repeated fixed32 repeated_fixed32 = 16; + repeated sfixed32 repeated_sfixed32 = 17; + repeated fixed64 repeated_fixed64 = 18; + repeated sfixed64 repeated_sfixed64 = 19; + repeated bool repeated_bool = 20; + repeated float repeated_float = 21; + repeated double repeated_double = 22; + repeated AnEnum repeated_enum = 23; + repeated string repeated_string = 24; + repeated bytes repeated_bytes = 25; + repeated AnotherMessage repeated_message = 26; + repeated group RepeatedGroup = 27 { + optional int32 a = 1; + } + } +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto new file mode 100644 index 00000000..589754e7 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_extension_singular_nano.proto @@ -0,0 +1,34 @@ +syntax = "proto2"; + +option java_multiple_files = true; +option java_package = "com.google.protobuf.nano"; + +import "google/protobuf/nano/unittest_extension_nano.proto"; + +// Must be compiled separately due to extension number reuse. +// The reuse is deliberate, for testing wire compatibility. + +message SingularExtensions { + extend ExtendableMessage { + optional int32 some_int32 = 10; + optional uint32 some_uint32 = 11; + optional sint32 some_sint32 = 12; + optional int64 some_int64 = 13; + optional uint64 some_uint64 = 14; + optional sint64 some_sint64 = 15; + optional fixed32 some_fixed32 = 16; + optional sfixed32 some_sfixed32 = 17; + optional fixed64 some_fixed64 = 18; + optional sfixed64 some_sfixed64 = 19; + optional bool some_bool = 20; + optional float some_float = 21; + optional double some_double = 22; + optional AnEnum some_enum = 23; + optional string some_string = 24; + optional bytes some_bytes = 25; + optional AnotherMessage some_message = 26; + optional group SomeGroup = 27 { + optional int32 a = 1; + } + } +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_has_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_has_nano.proto new file mode 100644 index 00000000..289d08ae --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_has_nano.proto @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: ulas@google.com (Ulas Kirazci) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoHasOuterClass"; + +message TestAllTypesNanoHas { + + message NestedMessage { + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + // Singular + optional int32 optional_int32 = 1; + optional float optional_float = 11; + optional double optional_double = 12; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional NestedMessage optional_nested_message = 18; + + optional NestedEnum optional_nested_enum = 21; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated NestedMessage repeated_nested_message = 48; + + repeated NestedEnum repeated_nested_enum = 51; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + optional float default_float_nan = 99 [default = nan]; + + optional NestedEnum default_nested_enum = 81 [default = BAR]; + + required int32 id = 86; + required NestedEnum required_enum = 87; + +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_import_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_import_nano.proto new file mode 100644 index 00000000..bcd4db7b --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_import_nano.proto @@ -0,0 +1,48 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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) +// +// This is like unittest_import.proto but with optimize_for = NANO_RUNTIME. + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano.testimport"; +option java_outer_classname = "UnittestImportNano"; + +message ImportMessageNano { + optional int32 d = 1; +} + +enum ImportEnumNano { + IMPORT_NANO_FOO = 7; + IMPORT_NANO_BAR = 8; + IMPORT_NANO_BAZ = 9; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto new file mode 100644 index 00000000..f2f62c4e --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nameclash_nano.proto @@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "MultipleNameClashNano"; +option java_multiple_files = true; + +message MultipleNameClashNano { + optional int32 field = 1; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nano.proto new file mode 100644 index 00000000..be84cad1 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_multiple_nano.proto @@ -0,0 +1,63 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest_import; + +import "google/protobuf/nano/unittest_import_nano.proto"; + +option java_package = "com.google.protobuf.nano"; +option java_multiple_files = true; + +enum FileScopeEnum { + ONE = 1; + TWO = 2; +} + +message FileScopeEnumRefNano { + optional FileScopeEnum enum_field = 1; +} + +message MessageScopeEnumRefNano { + enum MessageScopeEnum { + ONE = 1; + TWO = 2; + } + optional MessageScopeEnum enum_field = 1; +} + +message MultipleImportingNonMultipleNano1 { + optional ImportMessageNano field = 1; +} + +message MultipleImportingNonMultipleNano2 { + optional MultipleImportingNonMultipleNano1 nano1 = 1; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_nano.proto new file mode 100644 index 00000000..cba52c02 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_nano.proto @@ -0,0 +1,186 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: wink@google.com (Wink Saville) + +package protobuf_unittest; + +import "google/protobuf/nano/unittest_import_nano.proto"; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoOuterClass"; + +// Same as TestAllTypes but with the nano runtime. +message TestAllTypesNano { + + message NestedMessage { + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + // Singular + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional NestedMessage optional_nested_message = 18; + optional ForeignMessageNano optional_foreign_message = 19; + optional protobuf_unittest_import.ImportMessageNano + optional_import_message = 20; + + optional NestedEnum optional_nested_enum = 21; + optional ForeignEnumNano optional_foreign_enum = 22; + optional protobuf_unittest_import.ImportEnumNano optional_import_enum = 23; + + optional string optional_string_piece = 24 [ctype=STRING_PIECE]; + optional string optional_cord = 25 [ctype=CORD]; + + // 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 group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessageNano repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessageNano + repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnumNano repeated_foreign_enum = 52; + repeated protobuf_unittest_import.ImportEnumNano repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // Repeated packed + repeated int32 repeated_packed_int32 = 87 [packed=true]; + repeated sfixed64 repeated_packed_sfixed64 = 88 [packed=true]; + + repeated NestedEnum repeated_packed_nested_enum = 89 [packed=true]; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional int64 default_int64 = 62 [default = 42 ]; + optional uint32 default_uint32 = 63 [default = 43 ]; + optional uint64 default_uint64 = 64 [default = 44 ]; + optional sint32 default_sint32 = 65 [default = -45 ]; + optional sint64 default_sint64 = 66 [default = 46 ]; + optional fixed32 default_fixed32 = 67 [default = 47 ]; + optional fixed64 default_fixed64 = 68 [default = 48 ]; + optional sfixed32 default_sfixed32 = 69 [default = 49 ]; + optional sfixed64 default_sfixed64 = 70 [default = -50 ]; + optional float default_float = 71 [default = 51.5 ]; + optional double default_double = 72 [default = 52e3 ]; + optional bool default_bool = 73 [default = true ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + optional string default_string_nonascii = 76 [default = "dünya"]; + optional bytes default_bytes_nonascii = 77 [default = "dünyab"]; + + optional float default_float_inf = 97 [default = inf]; + optional float default_float_neg_inf = 98 [default = -inf]; + optional float default_float_nan = 99 [default = nan]; + optional double default_double_inf = 100 [default = inf]; + optional double default_double_neg_inf = 101 [default = -inf]; + optional double default_double_nan = 102 [default = nan]; + + optional NestedEnum default_nested_enum = 81 [default = BAR]; + optional ForeignEnumNano default_foreign_enum = 82 + [default = FOREIGN_NANO_BAR]; + optional protobuf_unittest_import.ImportEnumNano + default_import_enum = 83 [default = IMPORT_NANO_BAR]; + + optional string default_string_piece = 84 [ctype=STRING_PIECE,default="abc"]; + optional string default_cord = 85 [ctype=CORD,default="123"]; + + required int32 id = 86; + + // Try to cause conflicts. + optional int32 tag = 93; + optional int32 get_serialized_size = 94; + optional int32 write_to = 95; + + // Try to fail with java reserved keywords + optional int32 synchronized = 96; +} + +message ForeignMessageNano { + optional int32 c = 1; +} + +enum ForeignEnumNano { + FOREIGN_NANO_FOO = 4; + FOREIGN_NANO_BAR = 5; + FOREIGN_NANO_BAZ = 6; +} + +// Test that deprecated fields work. We only verify that they compile (at one +// point this failed). +message TestDeprecatedNano { + optional int32 deprecated_field = 1 [deprecated = true]; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_recursive_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_recursive_nano.proto new file mode 100644 index 00000000..3d3a6aa4 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_recursive_nano.proto @@ -0,0 +1,49 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: wink@google.com (Wink Saville) +// + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano"; +// Explicit outer classname to suppress legacy info. +option java_outer_classname = "UnittestRecursiveNano"; + +message RecursiveMessageNano { + message NestedMessage { + optional RecursiveMessageNano a = 1; + } + + required int32 id = 1; + optional NestedMessage nested_message = 2; + optional RecursiveMessageNano optional_recursive_message_nano = 3; + repeated RecursiveMessageNano repeated_recursive_message_nano = 4; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto new file mode 100644 index 00000000..2b246150 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_reference_types_nano.proto @@ -0,0 +1,116 @@ +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoReferenceTypes"; + +message TestAllTypesNano { + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + message NestedMessage { + optional int32 foo = 1; + } + + // Singular + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional NestedMessage optional_nested_message = 18; + + optional NestedEnum optional_nested_enum = 21; + + optional string optional_string_piece = 24 [ctype=STRING_PIECE]; + optional string optional_cord = 25 [ctype=CORD]; + + // 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 group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated NestedMessage repeated_nested_message = 48; + + repeated NestedEnum repeated_nested_enum = 51; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // Repeated packed + repeated int32 repeated_packed_int32 = 87 [packed=true]; + repeated sfixed64 repeated_packed_sfixed64 = 88 [packed=true]; + + repeated NestedEnum repeated_packed_nested_enum = 89 [packed=true]; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional int64 default_int64 = 62 [default = 42 ]; + optional uint32 default_uint32 = 63 [default = 43 ]; + optional uint64 default_uint64 = 64 [default = 44 ]; + optional sint32 default_sint32 = 65 [default = -45 ]; + optional sint64 default_sint64 = 66 [default = 46 ]; + optional fixed32 default_fixed32 = 67 [default = 47 ]; + optional fixed64 default_fixed64 = 68 [default = 48 ]; + optional sfixed32 default_sfixed32 = 69 [default = 49 ]; + optional sfixed64 default_sfixed64 = 70 [default = -50 ]; + optional float default_float = 71 [default = 51.5 ]; + optional double default_double = 72 [default = 52e3 ]; + optional bool default_bool = 73 [default = true ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + + optional float default_float_inf = 97 [default = inf]; + optional float default_float_neg_inf = 98 [default = -inf]; + optional float default_float_nan = 99 [default = nan]; + optional double default_double_inf = 100 [default = inf]; + optional double default_double_neg_inf = 101 [default = -inf]; + optional double default_double_nan = 102 [default = nan]; + +} + +message ForeignMessageNano { + optional int32 c = 1; +} + +enum ForeignEnumNano { + FOREIGN_NANO_FOO = 4; + FOREIGN_NANO_BAR = 5; + FOREIGN_NANO_BAZ = 6; +} + diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto new file mode 100644 index 00000000..aea48ef7 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_merge_nano.proto @@ -0,0 +1,47 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +import "google/protobuf/nano/unittest_nano.proto"; + +option java_package = "com.google.protobuf.nano"; +option java_multiple_files = true; + +// A container message for testing the merging of repeated fields at a +// nested level. Other tests will be done using the repeated fields in +// TestAllTypesNano. +message TestRepeatedMergeNano { + + optional TestAllTypesNano contained = 1; + +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_packables_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_packables_nano.proto new file mode 100644 index 00000000..1c78918f --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_repeated_packables_nano.proto @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest; + +option java_package = "com.google.protobuf.nano"; +option java_outer_classname = "NanoRepeatedPackables"; + +enum Enum { + OPTION_ONE = 1; + OPTION_TWO = 2; +} + +// Two almost identical messages with all packable repeated field types. +// One with none marked as packed and the other all packed. For +// compatibility, they should be able to parse each other's serialized +// forms. + +message NonPacked { + + // All packable types, none marked as packed. + + repeated int32 int32s = 1; + repeated int64 int64s = 2; + repeated uint32 uint32s = 3; + repeated uint64 uint64s = 4; + repeated sint32 sint32s = 5; + repeated sint64 sint64s = 6; + repeated fixed32 fixed32s = 7; + repeated fixed64 fixed64s = 8; + repeated sfixed32 sfixed32s = 9; + repeated sfixed64 sfixed64s = 10; + repeated float floats = 11; + repeated double doubles = 12; + repeated bool bools = 13; + repeated Enum enums = 14; + + // Noise for testing merged deserialization. + optional int32 noise = 15; + +} + +message Packed { + + // All packable types, all matching the field numbers in NonPacked, + // all marked as packed. + + repeated int32 int32s = 1 [ packed = true ]; + repeated int64 int64s = 2 [ packed = true ]; + repeated uint32 uint32s = 3 [ packed = true ]; + repeated uint64 uint64s = 4 [ packed = true ]; + repeated sint32 sint32s = 5 [ packed = true ]; + repeated sint64 sint64s = 6 [ packed = true ]; + repeated fixed32 fixed32s = 7 [ packed = true ]; + repeated fixed64 fixed64s = 8 [ packed = true ]; + repeated sfixed32 sfixed32s = 9 [ packed = true ]; + repeated sfixed64 sfixed64s = 10 [ packed = true ]; + repeated float floats = 11 [ packed = true ]; + repeated double doubles = 12 [ packed = true ]; + repeated bool bools = 13 [ packed = true ]; + repeated Enum enums = 14 [ packed = true ]; + + // Noise for testing merged deserialization. + optional int32 noise = 15; + +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_simple_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_simple_nano.proto new file mode 100644 index 00000000..0c780c72 --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_simple_nano.proto @@ -0,0 +1,54 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: wink@google.com (Wink Saville) +// + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano"; +// Explicit outer classname to suppress legacy info. +option java_outer_classname = "UnittestSimpleNano"; + +message SimpleMessageNano { + message NestedMessage { + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + optional int32 d = 1 [default = 123]; + optional NestedMessage nested_msg = 2; + optional NestedEnum default_nested_enum = 3 [default = BAZ]; +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_single_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_single_nano.proto new file mode 100644 index 00000000..fcb1539d --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_single_nano.proto @@ -0,0 +1,38 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: maxtroy@google.com (Max Cai) + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano"; + +message SingleMessageNano { +} diff --git a/javanano/src/test/java/com/google/protobuf/nano/unittest_stringutf8_nano.proto b/javanano/src/test/java/com/google/protobuf/nano/unittest_stringutf8_nano.proto new file mode 100644 index 00000000..2bbf2b7b --- /dev/null +++ b/javanano/src/test/java/com/google/protobuf/nano/unittest_stringutf8_nano.proto @@ -0,0 +1,43 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: wink@google.com (Wink Saville) +// + +package protobuf_unittest_import; + +option java_package = "com.google.protobuf.nano"; +// Explicit outer classname to suppress legacy info. +option java_outer_classname = "UnittestStringutf8Nano"; + +message StringUtf8 { + optional string id = 1; + repeated string rs = 2; +} diff --git a/src/google/protobuf/compiler/javanano/javanano_enum.cc b/src/google/protobuf/compiler/javanano/javanano_enum.cc new file mode 100644 index 00000000..f934b05f --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_enum.cc @@ -0,0 +1,111 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <map> +#include <string> + +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/compiler/javanano/javanano_enum.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, const Params& params) + : params_(params), descriptor_(descriptor) { + 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); + } + } +} + +EnumGenerator::~EnumGenerator() {} + +void EnumGenerator::Generate(io::Printer* printer) { + printer->Print( + "\n" + "// enum $classname$\n", + "classname", descriptor_->name()); + + // Start of container interface + bool use_shell_class = params_.java_enum_style(); + if (use_shell_class) { + printer->Print( + "public interface $classname$ {\n", + "classname", RenameJavaKeywords(descriptor_->name())); + printer->Indent(); + } + + // Canonical values + for (int i = 0; i < canonical_values_.size(); i++) { + printer->Print( + "public static final int $name$ = $canonical_value$;\n", + "name", RenameJavaKeywords(canonical_values_[i]->name()), + "canonical_value", SimpleItoa(canonical_values_[i]->number())); + } + + // Aliases + for (int i = 0; i < aliases_.size(); i++) { + printer->Print( + "public static final int $name$ = $canonical_name$;\n", + "name", RenameJavaKeywords(aliases_[i].value->name()), + "canonical_name", RenameJavaKeywords(aliases_[i].canonical_value->name())); + } + + // End of container interface + if (use_shell_class) { + printer->Outdent(); + printer->Print("}\n"); + } +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_enum.h b/src/google/protobuf/compiler/javanano/javanano_enum.h new file mode 100644 index 00000000..10dd3648 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_enum.h @@ -0,0 +1,87 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_ENUM_H__ + +#include <string> +#include <vector> + +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace javanano { + +class EnumGenerator { + public: + explicit EnumGenerator(const EnumDescriptor* descriptor, const Params& params); + ~EnumGenerator(); + + void Generate(io::Printer* printer); + + private: + const Params& params_; + const EnumDescriptor* descriptor_; + + // The proto language allows multiple enum constants to have the same numeric + // value. Java, however, does not allow multiple enum constants to be + // considered equivalent. We treat the first defined constant for any + // given numeric value as "canonical" and the rest as aliases of that + // canonical value. + vector<const EnumValueDescriptor*> canonical_values_; + + struct Alias { + const EnumValueDescriptor* value; + const EnumValueDescriptor* canonical_value; + }; + vector<Alias> aliases_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_ENUM_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.cc b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc new file mode 100644 index 00000000..8a59d323 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.cc @@ -0,0 +1,520 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <map> +#include <string> + +#include <google/protobuf/compiler/javanano/javanano_enum_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const Params& params, + const FieldDescriptor* descriptor, map<string, string>* variables) { + (*variables)["name"] = + RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); + (*variables)["capitalized_name"] = + RenameJavaKeywords(UnderscoresToCapitalizedCamelCase(descriptor)); + (*variables)["number"] = SimpleItoa(descriptor->number()); + if (params.use_reference_types_for_primitives() + && !params.reftypes_primitive_enums() + && !descriptor->is_repeated()) { + (*variables)["type"] = "java.lang.Integer"; + (*variables)["default"] = "null"; + } else { + (*variables)["type"] = "int"; + (*variables)["default"] = DefaultValue(params, descriptor); + } + (*variables)["repeated_default"] = + "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; + (*variables)["tag"] = SimpleItoa(internal::WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + internal::WireFormat::TagSize(descriptor->number(), descriptor->type())); + (*variables)["non_packed_tag"] = SimpleItoa( + internal::WireFormatLite::MakeTag(descriptor->number(), + internal::WireFormat::WireTypeForFieldType(descriptor->type()))); + (*variables)["message_name"] = descriptor->containing_type()->name(); +} + +void LoadEnumValues(const Params& params, + const EnumDescriptor* enum_descriptor, vector<string>* canonical_values) { + string enum_class_name = ClassName(params, enum_descriptor); + for (int i = 0; i < enum_descriptor->value_count(); i++) { + const EnumValueDescriptor* value = enum_descriptor->value(i); + const EnumValueDescriptor* canonical_value = + enum_descriptor->FindValueByNumber(value->number()); + if (value == canonical_value) { + canonical_values->push_back( + enum_class_name + "." + RenameJavaKeywords(value->name())); + } + } +} + +void PrintCaseLabels( + io::Printer* printer, const vector<string>& canonical_values) { + for (int i = 0; i < canonical_values.size(); i++) { + printer->Print( + " case $value$:\n", + "value", canonical_values[i]); + } +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetEnumVariables(params, descriptor, &variables_); + LoadEnumValues(params, descriptor->enum_type(), &canonical_values_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { + printer->Print(variables_, + "public $type$ $name$;\n"); + + if (params_.generate_has()) { + printer->Print(variables_, + "public boolean has$capitalized_name$;\n"); + } +} + +void EnumFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = $default$;\n"); + + if (params_.generate_has()) { + printer->Print(variables_, + "has$capitalized_name$ = false;\n"); + } +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "int value = input.readInt32();\n" + "switch (value) {\n"); + PrintCaseLabels(printer, canonical_values_); + printer->Print(variables_, + " this.$name$ = value;\n"); + if (params_.generate_has()) { + printer->Print(variables_, + " has$capitalized_name$ = true;\n"); + } + printer->Print( + " break;\n" + "}\n"); + // No default case: in case of invalid value from the wire, preserve old + // field value. Also we are not storing the invalid value into the unknown + // fields, because there is no way to get the value out. +} + +void EnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->is_required() && !params_.generate_has()) { + // Always serialize a required field if we don't have the 'has' signal. + printer->Print(variables_, + "output.writeInt32($number$, this.$name$);\n"); + } else { + if (params_.generate_has()) { + printer->Print(variables_, + "if (this.$name$ != $default$ || has$capitalized_name$) {\n"); + } else { + printer->Print(variables_, + "if (this.$name$ != $default$) {\n"); + } + printer->Print(variables_, + " output.writeInt32($number$, this.$name$);\n" + "}\n"); + } +} + +void EnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + if (descriptor_->is_required() && !params_.generate_has()) { + printer->Print(variables_, + "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeInt32Size($number$, this.$name$);\n"); + } else { + if (params_.generate_has()) { + printer->Print(variables_, + "if (this.$name$ != $default$ || has$capitalized_name$) {\n"); + } else { + printer->Print(variables_, + "if (this.$name$ != $default$) {\n"); + } + printer->Print(variables_, + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeInt32Size($number$, this.$name$);\n" + "}\n"); + } +} + +void EnumFieldGenerator::GenerateEqualsCode(io::Printer* printer) const { + if (params_.use_reference_types_for_primitives() + && !params_.reftypes_primitive_enums()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)) {\n" + " return false;" + "}\n"); + } else { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void EnumFieldGenerator::GenerateHashCodeCode(io::Printer* printer) const { + printer->Print( + "result = 31 * result + "); + if (params_.use_reference_types_for_primitives() + && !params_.reftypes_primitive_enums()) { + printer->Print(variables_, + "(this.$name$ == null ? 0 : this.$name$)"); + } else { + printer->Print(variables_, + "this.$name$"); + } + printer->Print(";\n"); +} + +// =================================================================== + +AccessorEnumFieldGenerator:: +AccessorEnumFieldGenerator(const FieldDescriptor* descriptor, + const Params& params, int has_bit_index) + : FieldGenerator(params), descriptor_(descriptor) { + SetEnumVariables(params, descriptor, &variables_); + LoadEnumValues(params, descriptor->enum_type(), &canonical_values_); + SetBitOperationVariables("has", has_bit_index, &variables_); +} + +AccessorEnumFieldGenerator::~AccessorEnumFieldGenerator() {} + +void AccessorEnumFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { + printer->Print(variables_, + "private int $name$_;\n" + "public int get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n" + "public $message_name$ set$capitalized_name$(int value) {\n" + " $name$_ = value;\n" + " $set_has$;\n" + " return this;\n" + "}\n" + "public boolean has$capitalized_name$() {\n" + " return $get_has$;\n" + "}\n" + "public $message_name$ clear$capitalized_name$() {\n" + " $name$_ = $default$;\n" + " $clear_has$;\n" + " return this;\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $default$;\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "int value = input.readInt32();\n" + "switch (value) {\n"); + PrintCaseLabels(printer, canonical_values_); + printer->Print(variables_, + " $name$_ = value;\n" + " $set_has$;\n" + " break;\n" + "}\n"); + // No default case: in case of invalid value from the wire, preserve old + // field value. Also we are not storing the invalid value into the unknown + // fields, because there is no way to get the value out. +} + +void AccessorEnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has$) {\n" + " output.writeInt32($number$, $name$_);\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has$) {\n" + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeInt32Size($number$, $name$_);\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); +} + +void AccessorEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetEnumVariables(params, descriptor, &variables_); + LoadEnumValues(params, descriptor->enum_type(), &canonical_values_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { + printer->Print(variables_, + "public $type$[] $name$;\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = $repeated_default$;\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // First, figure out the maximum length of the array, then parse, + // and finally copy the valid values to the field. + printer->Print(variables_, + "int length = com.google.protobuf.nano.WireFormatNano\n" + " .getRepeatedFieldArrayLength(input, $non_packed_tag$);\n" + "int[] validValues = new int[length];\n" + "int validCount = 0;\n" + "for (int i = 0; i < length; i++) {\n" + " if (i != 0) { // tag for first value already consumed.\n" + " input.readTag();\n" + " }\n" + " int value = input.readInt32();\n" + " switch (value) {\n"); + printer->Indent(); + PrintCaseLabels(printer, canonical_values_); + printer->Outdent(); + printer->Print(variables_, + " validValues[validCount++] = value;\n" + " break;\n" + " }\n" + "}\n" + "if (validCount != 0) {\n" + " int i = this.$name$ == null ? 0 : this.$name$.length;\n" + " if (i == 0 && validCount == validValues.length) {\n" + " this.$name$ = validValues;\n" + " } else {\n" + " int[] newArray = new int[i + validCount];\n" + " if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + " }\n" + " java.lang.System.arraycopy(validValues, 0, newArray, i, validCount);\n" + " this.$name$ = newArray;\n" + " }\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCodeFromPacked(io::Printer* printer) const { + printer->Print(variables_, + "int bytes = input.readRawVarint32();\n" + "int limit = input.pushLimit(bytes);\n" + "// First pass to compute array length.\n" + "int arrayLength = 0;\n" + "int startPos = input.getPosition();\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " switch (input.readInt32()) {\n"); + printer->Indent(); + PrintCaseLabels(printer, canonical_values_); + printer->Outdent(); + printer->Print(variables_, + " arrayLength++;\n" + " break;\n" + " }\n" + "}\n" + "if (arrayLength != 0) {\n" + " input.rewindToPosition(startPos);\n" + " int i = this.$name$ == null ? 0 : this.$name$.length;\n" + " int[] newArray = new int[i + arrayLength];\n" + " if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + " }\n" + " while (input.getBytesUntilLimit() > 0) {\n" + " int value = input.readInt32();\n" + " switch (value) {\n"); + printer->Indent(); + printer->Indent(); + PrintCaseLabels(printer, canonical_values_); + printer->Outdent(); + printer->Outdent(); + printer->Print(variables_, + " newArray[i++] = value;\n" + " break;\n" + " }\n" + " }\n" + " this.$name$ = newArray;\n" + "}\n" + "input.popLimit(limit);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateRepeatedDataSizeCode(io::Printer* printer) const { + // Creates a variable dataSize and puts the serialized size in there. + printer->Print(variables_, + "int dataSize = 0;\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " int element = this.$name$[i];\n" + " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeInt32SizeNoTag(element);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n"); + printer->Indent(); + + if (descriptor_->options().packed()) { + GenerateRepeatedDataSizeCode(printer); + printer->Print(variables_, + "output.writeRawVarint32($tag$);\n" + "output.writeRawVarint32(dataSize);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.writeRawVarint32(this.$name$[i]);\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.writeInt32($number$, this.$name$[i]);\n" + "}\n"); + } + + printer->Outdent(); + printer->Print(variables_, + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n"); + printer->Indent(); + + GenerateRepeatedDataSizeCode(printer); + + printer->Print( + "size += dataSize;\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + "size += $tag_size$;\n" + "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeRawVarint32Size(dataSize);\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * this.$name$.length;\n"); + } + + printer->Outdent(); + + printer->Print( + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_enum_field.h b/src/google/protobuf/compiler/javanano/javanano_enum_field.h new file mode 100644 index 00000000..00adc61f --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_enum_field.h @@ -0,0 +1,125 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_ENUM_FIELD_H__ + +#include <map> +#include <string> +#include <vector> +#include <google/protobuf/compiler/javanano/javanano_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator( + const FieldDescriptor* descriptor, const Params& params); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + vector<string> canonical_values_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class AccessorEnumFieldGenerator : public FieldGenerator { + public: + explicit AccessorEnumFieldGenerator(const FieldDescriptor* descriptor, + const Params& params, int has_bit_index); + ~AccessorEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + vector<string> canonical_values_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AccessorEnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator( + const FieldDescriptor* descriptor, const Params& params); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateMergingCodeFromPacked(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + void GenerateRepeatedDataSizeCode(io::Printer* printer) const; + + const FieldDescriptor* descriptor_; + map<string, string> variables_; + vector<string> canonical_values_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_extension.cc b/src/google/protobuf/compiler/javanano/javanano_extension.cc new file mode 100644 index 00000000..754ed550 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_extension.cc @@ -0,0 +1,150 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: bduff@google.com (Brian Duff) + +#include <google/protobuf/compiler/javanano/javanano_extension.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/wire_format.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +const char* GetTypeConstantName(const FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return "TYPE_INT32" ; + case FieldDescriptor::TYPE_UINT32 : return "TYPE_UINT32" ; + case FieldDescriptor::TYPE_SINT32 : return "TYPE_SINT32" ; + case FieldDescriptor::TYPE_FIXED32 : return "TYPE_FIXED32" ; + case FieldDescriptor::TYPE_SFIXED32: return "TYPE_SFIXED32"; + case FieldDescriptor::TYPE_INT64 : return "TYPE_INT64" ; + case FieldDescriptor::TYPE_UINT64 : return "TYPE_UINT64" ; + case FieldDescriptor::TYPE_SINT64 : return "TYPE_SINT64" ; + case FieldDescriptor::TYPE_FIXED64 : return "TYPE_FIXED64" ; + case FieldDescriptor::TYPE_SFIXED64: return "TYPE_SFIXED64"; + case FieldDescriptor::TYPE_FLOAT : return "TYPE_FLOAT" ; + case FieldDescriptor::TYPE_DOUBLE : return "TYPE_DOUBLE" ; + case FieldDescriptor::TYPE_BOOL : return "TYPE_BOOL" ; + case FieldDescriptor::TYPE_STRING : return "TYPE_STRING" ; + case FieldDescriptor::TYPE_BYTES : return "TYPE_BYTES" ; + case FieldDescriptor::TYPE_ENUM : return "TYPE_ENUM" ; + case FieldDescriptor::TYPE_GROUP : return "TYPE_GROUP" ; + case FieldDescriptor::TYPE_MESSAGE : return "TYPE_MESSAGE" ; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace + +void SetVariables(const FieldDescriptor* descriptor, const Params params, + map<string, string>* variables) { + (*variables)["extends"] = ClassName(params, descriptor->containing_type()); + (*variables)["name"] = RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); + bool repeated = descriptor->is_repeated(); + (*variables)["repeated"] = repeated ? "Repeated" : ""; + (*variables)["type"] = GetTypeConstantName(descriptor->type()); + JavaType java_type = GetJavaType(descriptor->type()); + string tag = SimpleItoa(WireFormat::MakeTag(descriptor)); + if (java_type == JAVATYPE_MESSAGE) { + (*variables)["ext_type"] = "MessageTyped"; + string message_type = ClassName(params, descriptor->message_type()); + if (repeated) { + message_type += "[]"; + } + (*variables)["class"] = message_type; + // For message typed extensions, tags_params contains a single tag + // for both singular and repeated cases. + (*variables)["tag_params"] = tag; + } else { + (*variables)["ext_type"] = "PrimitiveTyped"; + if (!repeated) { + (*variables)["class"] = BoxedPrimitiveTypeName(java_type); + (*variables)["tag_params"] = tag; + } else { + (*variables)["class"] = PrimitiveTypeName(java_type) + "[]"; + if (!descriptor->is_packable()) { + // Non-packable: nonPackedTag == tag, packedTag == 0 + (*variables)["tag_params"] = tag + ", " + tag + ", 0"; + } else if (descriptor->options().packed()) { + // Packable and packed: tag == packedTag + string non_packed_tag = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), + WireFormat::WireTypeForFieldType(descriptor->type()))); + (*variables)["tag_params"] = tag + ", " + non_packed_tag + ", " + tag; + } else { + // Packable and not packed: tag == nonPackedTag + string packed_tag = SimpleItoa(WireFormatLite::MakeTag( + descriptor->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED)); + (*variables)["tag_params"] = tag + ", " + tag + ", " + packed_tag; + } + } + } +} + +ExtensionGenerator:: +ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params) + : params_(params), descriptor_(descriptor) { + SetVariables(descriptor, params, &variables_); +} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::Generate(io::Printer* printer) const { + printer->Print("\n"); + PrintFieldComment(printer, descriptor_); + printer->Print(variables_, + "public static final com.google.protobuf.nano.Extension<\n" + " $extends$,\n" + " $class$> $name$ =\n" + " com.google.protobuf.nano.Extension.create$repeated$$ext_type$(\n" + " com.google.protobuf.nano.Extension.$type$,\n" + " $class$.class,\n" + " $tag_params$);\n"); +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google + diff --git a/src/google/protobuf/compiler/javanano/javanano_extension.h b/src/google/protobuf/compiler/javanano/javanano_extension.h new file mode 100644 index 00000000..4843e296 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_extension.h @@ -0,0 +1,74 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: bduff@google.com (Brian Duff) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVANANO_EXTENSION_H_ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_EXTENSION_H_ + +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/descriptor.pb.h> + + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace javanano { + +class ExtensionGenerator { + public: + explicit ExtensionGenerator(const FieldDescriptor* descriptor, const Params& params); + ~ExtensionGenerator(); + + void Generate(io::Printer* printer) const; + + private: + const Params& params_; + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_EXTENSION_H_ diff --git a/src/google/protobuf/compiler/javanano/javanano_field.cc b/src/google/protobuf/compiler/javanano/javanano_field.cc new file mode 100644 index 00000000..e3e4cefe --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_field.cc @@ -0,0 +1,143 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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 <google/protobuf/compiler/javanano/javanano_field.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/compiler/javanano/javanano_primitive_field.h> +#include <google/protobuf/compiler/javanano/javanano_enum_field.h> +#include <google/protobuf/compiler/javanano/javanano_message_field.h> +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +FieldGenerator::~FieldGenerator() {} + +bool FieldGenerator::SavedDefaultNeeded() const { + // No saved default for this field by default. + // Subclasses whose instances may need saved defaults will override this + // and return the appropriate value. + return false; +} + +void FieldGenerator::GenerateInitSavedDefaultCode(io::Printer* printer) const { + // No saved default for this field by default. + // Subclasses whose instances may need saved defaults will override this + // and generate the appropriate init code to the printer. +} + +void FieldGenerator::GenerateMergingCodeFromPacked(io::Printer* printer) const { + // Reaching here indicates a bug. Cases are: + // - This FieldGenerator should support packing, but this method should be + // overridden. + // - This FieldGenerator doesn't support packing, and this method should + // never have been called. + GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() " + << "called on field generator that does not support packing."; +} + +// ============================================= + +FieldGeneratorMap::FieldGeneratorMap( + const Descriptor* descriptor, const Params ¶ms) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr<FieldGenerator>[descriptor->field_count()]) { + + int next_has_bit_index = 0; + bool saved_defaults_needed = false; + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + FieldGenerator* field_generator = MakeGenerator( + descriptor->field(i), params, &next_has_bit_index); + saved_defaults_needed = saved_defaults_needed + || field_generator->SavedDefaultNeeded(); + field_generators_[i].reset(field_generator); + } + total_bits_ = next_has_bit_index; + saved_defaults_needed_ = saved_defaults_needed; +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field, + const Params ¶ms, int* next_has_bit_index) { + JavaType java_type = GetJavaType(field); + if (field->is_repeated()) { + switch (java_type) { + case JAVATYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field, params); + case JAVATYPE_ENUM: + return new RepeatedEnumFieldGenerator(field, params); + default: + return new RepeatedPrimitiveFieldGenerator(field, params); + } + } else if (params.optional_field_accessors() && field->is_optional() + && java_type != JAVATYPE_MESSAGE) { + // We need a has-bit for each primitive/enum field because their default + // values could be same as explicitly set values. But we don't need it + // for a message field because they have no defaults and Nano uses 'null' + // for unset messages, which cannot be set explicitly. + switch (java_type) { + case JAVATYPE_ENUM: + return new AccessorEnumFieldGenerator( + field, params, (*next_has_bit_index)++); + default: + return new AccessorPrimitiveFieldGenerator( + field, params, (*next_has_bit_index)++); + } + } else { + switch (java_type) { + case JAVATYPE_MESSAGE: + return new MessageFieldGenerator(field, params); + case JAVATYPE_ENUM: + return new EnumFieldGenerator(field, params); + default: + return new PrimitiveFieldGenerator(field, params); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_field.h b/src/google/protobuf/compiler/javanano/javanano_field.h new file mode 100644 index 00000000..6170c2c0 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_field.h @@ -0,0 +1,119 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_FIELD_H__ + +#include <string> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace javanano { + +class FieldGenerator { + public: + FieldGenerator(const Params& params) : params_(params) {} + virtual ~FieldGenerator(); + + virtual bool SavedDefaultNeeded() const; + virtual void GenerateInitSavedDefaultCode(io::Printer* printer) const; + + // Generates code for Java fields and methods supporting this field. + // If this field needs a saved default (SavedDefaultNeeded() is true), + // then @lazy_init controls how the static field for that default value + // and its initialization code should be generated. If @lazy_init is + // true, the static field is not declared final and the initialization + // code is generated only when GenerateInitSavedDefaultCode is called; + // otherwise, the static field is declared final and initialized inline. + // GenerateInitSavedDefaultCode will not be called in the latter case. + virtual void GenerateMembers( + io::Printer* printer, bool lazy_init) const = 0; + + virtual void GenerateClearCode(io::Printer* printer) const = 0; + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + + // Generates code to merge from packed serialized form. The default + // implementation will fail; subclasses which can handle packed serialized + // forms will override this and print appropriate code to the printer. + virtual void GenerateMergingCodeFromPacked(io::Printer* printer) const; + + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + virtual void GenerateEqualsCode(io::Printer* printer) const = 0; + virtual void GenerateHashCodeCode(io::Printer* printer) const = 0; + + protected: + const Params& params_; + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor, const Params ¶ms); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + int total_bits() const { return total_bits_; } + bool saved_defaults_needed() const { return saved_defaults_needed_; } + + private: + const Descriptor* descriptor_; + scoped_array<scoped_ptr<FieldGenerator> > field_generators_; + int total_bits_; + bool saved_defaults_needed_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field, + const Params ¶ms, int* next_has_bit_index); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_FIELD_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_file.cc b/src/google/protobuf/compiler/javanano/javanano_file.cc new file mode 100644 index 00000000..3676ab9d --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_file.cc @@ -0,0 +1,263 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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 <iostream> + +#include <google/protobuf/compiler/javanano/javanano_file.h> +#include <google/protobuf/compiler/javanano/javanano_enum.h> +#include <google/protobuf/compiler/javanano/javanano_extension.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/compiler/javanano/javanano_message.h> +#include <google/protobuf/compiler/code_generator.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +namespace { + +// Recursively searches the given message to see if it contains any extensions. +bool UsesExtensions(const Message& message) { + const Reflection* reflection = message.GetReflection(); + + // We conservatively assume that unknown fields are extensions. + if (reflection->GetUnknownFields(message).field_count() > 0) return true; + + vector<const FieldDescriptor*> fields; + reflection->ListFields(message, &fields); + + for (int i = 0; i < fields.size(); i++) { + if (fields[i]->is_extension()) return true; + + if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (fields[i]->is_repeated()) { + int size = reflection->FieldSize(message, fields[i]); + for (int j = 0; j < size; j++) { + const Message& sub_message = + reflection->GetRepeatedMessage(message, fields[i], j); + if (UsesExtensions(sub_message)) return true; + } + } else { + const Message& sub_message = reflection->GetMessage(message, fields[i]); + if (UsesExtensions(sub_message)) return true; + } + } + } + + return false; +} + +} // namespace + +FileGenerator::FileGenerator(const FileDescriptor* file, const Params& params) + : file_(file), + params_(params), + java_package_(FileJavaPackage(params, file)), + classname_(FileClassName(params, file)) {} + +FileGenerator::~FileGenerator() {} + +bool FileGenerator::Validate(string* error) { + // Check for extensions + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + if (UsesExtensions(file_proto) && !params_.store_unknown_fields()) { + error->assign(file_->name()); + error->append( + ": Java NANO_RUNTIME only supports extensions when the " + "'store_unknown_fields' generator option is 'true'."); + return false; + } + + if (file_->service_count() != 0 && !params_.ignore_services()) { + error->assign(file_->name()); + error->append( + ": Java NANO_RUNTIME does not support services\""); + return false; + } + + if (!IsOuterClassNeeded(params_, file_)) { + return true; + } + + // Check whether legacy javanano generator would omit the outer class. + if (!params_.has_java_outer_classname(file_->name()) + && file_->message_type_count() == 1 + && file_->enum_type_count() == 0 && file_->extension_count() == 0) { + cout << "INFO: " << file_->name() << ":" << endl; + cout << "Javanano generator has changed to align with java generator. " + "An outer class will be created for this file and the single message " + "in the file will become a nested class. Use java_multiple_files to " + "skip generating the outer class, or set an explicit " + "java_outer_classname to suppress this message." << endl; + } + + // Check that no class name matches the file's class name. This is a common + // problem that leads to Java compile errors that can be hard to understand. + // It's especially bad when using the java_multiple_files, since we would + // end up overwriting the outer class with one of the inner ones. + bool found_conflict = false; + for (int i = 0; !found_conflict && i < file_->message_type_count(); i++) { + if (file_->message_type(i)->name() == classname_) { + found_conflict = true; + } + } + if (params_.java_enum_style()) { + for (int i = 0; !found_conflict && i < file_->enum_type_count(); i++) { + if (file_->enum_type(i)->name() == classname_) { + found_conflict = true; + } + } + } + if (found_conflict) { + error->assign(file_->name()); + error->append( + ": Cannot generate Java output because the file's outer class name, \""); + error->append(classname_); + error->append( + "\", matches the name of one of the types declared inside it. " + "Please either rename the type or use the java_outer_classname " + "option to specify a different outer class name for the .proto file."); + return false; + } + return true; +} + +void FileGenerator::Generate(io::Printer* printer) { + // We don't import anything because we refer to all classes by their + // fully-qualified names in the generated source. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); + if (!java_package_.empty()) { + printer->Print( + "\n" + "package $package$;\n", + "package", java_package_); + } + + // Note: constants (from enums, emitted in the loop below) may have the same names as constants + // in the nested classes. This causes Java warnings, but is not fatal, so we suppress those + // warnings here in the top-most class declaration. + printer->Print( + "\n" + "@SuppressWarnings(\"hiding\")\n" + "public interface $classname$ {\n", + "classname", classname_); + printer->Indent(); + + // ----------------------------------------------------------------- + + // Extensions. + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator(file_->extension(i), params_).Generate(printer); + } + + // Enums. + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator(file_->enum_type(i), params_).Generate(printer); + } + + // Messages. + if (!params_.java_multiple_files(file_->name())) { + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator(file_->message_type(i), params_).Generate(printer); + } + } + + // Static variables. + for (int i = 0; i < file_->message_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(file_->message_type(i), params_).GenerateStaticVariables(printer); + } + + printer->Outdent(); + printer->Print( + "}\n"); +} + +template<typename GeneratorClass, typename DescriptorClass> +static void GenerateSibling(const string& package_dir, + const string& java_package, + const DescriptorClass* descriptor, + GeneratorContext* output_directory, + vector<string>* file_list, + const Params& params) { + string filename = package_dir + descriptor->name() + ".java"; + file_list->push_back(filename); + + scoped_ptr<io::ZeroCopyOutputStream> output( + output_directory->Open(filename)); + io::Printer printer(output.get(), '$'); + + printer.Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"); + if (!java_package.empty()) { + printer.Print( + "\n" + "package $package$;\n", + "package", java_package); + } + + GeneratorClass(descriptor, params).Generate(&printer); +} + +void FileGenerator::GenerateSiblings(const string& package_dir, + GeneratorContext* output_directory, + vector<string>* file_list) { + if (params_.java_multiple_files(file_->name())) { + for (int i = 0; i < file_->message_type_count(); i++) { + GenerateSibling<MessageGenerator>(package_dir, java_package_, + file_->message_type(i), + output_directory, file_list, params_); + } + + if (params_.java_enum_style()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + GenerateSibling<EnumGenerator>(package_dir, java_package_, + file_->enum_type(i), + output_directory, file_list, params_); + } + } + } +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_file.h b/src/google/protobuf/compiler/javanano/javanano_file.h new file mode 100644 index 00000000..217eafe2 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_file.h @@ -0,0 +1,94 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_FILE_H__ + +#include <string> +#include <vector> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } + namespace compiler { + class GeneratorContext; // code_generator.h + } +} + +namespace protobuf { +namespace compiler { +namespace javanano { + +class FileGenerator { + public: + explicit FileGenerator(const FileDescriptor* file, const Params& params); + ~FileGenerator(); + + // Checks for problems that would otherwise lead to cryptic compile errors. + // Returns true if there are no problems, or writes an error description to + // the given string and returns false otherwise. + bool Validate(string* error); + + void Generate(io::Printer* printer); + + // If we aren't putting everything into one file, this will write all the + // files other than the outer file (i.e. one for each message, enum, and + // service type). + void GenerateSiblings(const string& package_dir, + GeneratorContext* output_directory, + vector<string>* file_list); + + const string& java_package() { return java_package_; } + const string& classname() { return classname_; } + + private: + const FileDescriptor* file_; + const Params& params_; + string java_package_; + string classname_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_FILE_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.cc b/src/google/protobuf/compiler/javanano/javanano_generator.cc new file mode 100644 index 00000000..b5fbcd5f --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_generator.cc @@ -0,0 +1,219 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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 <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/compiler/javanano/javanano_generator.h> +#include <google/protobuf/compiler/javanano/javanano_file.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/zero_copy_stream.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +namespace { + +string TrimString(const string& s) { + string::size_type start = s.find_first_not_of(" \n\r\t"); + if (start == string::npos) { + return ""; + } + string::size_type end = s.find_last_not_of(" \n\r\t") + 1; + return s.substr(start, end - start); +} + +} // namespace + +void UpdateParamsRecursively(Params& params, + const FileDescriptor* file) { + // Add any parameters for this file + if (file->options().has_java_outer_classname()) { + params.set_java_outer_classname( + file->name(), file->options().java_outer_classname()); + } + if (file->options().has_java_package()) { + params.set_java_package( + file->name(), file->options().java_package()); + } + if (file->options().has_java_multiple_files()) { + params.set_java_multiple_files( + file->name(), file->options().java_multiple_files()); + } + + // Loop through all dependent files recursively + // adding dep + for (int i = 0; i < file->dependency_count(); i++) { + UpdateParamsRecursively(params, file->dependency(i)); + } +} + +JavaNanoGenerator::JavaNanoGenerator() {} +JavaNanoGenerator::~JavaNanoGenerator() {} + +bool JavaNanoGenerator::Generate(const FileDescriptor* file, + const string& parameter, + GeneratorContext* output_directory, + string* error) const { + vector<pair<string, string> > options; + + ParseGeneratorParameter(parameter, &options); + + // ----------------------------------------------------------------- + // parse generator options + + // Name a file where we will write a list of generated file names, one + // per line. + string output_list_file; + Params params(file->name()); + + // Update per file params + UpdateParamsRecursively(params, file); + + // Replace any existing options with ones from command line + for (int i = 0; i < options.size(); i++) { + string option_name = TrimString(options[i].first); + string option_value = TrimString(options[i].second); + if (option_name == "output_list_file") { + output_list_file = option_value; + } else if (option_name == "java_package") { + vector<string> parts; + SplitStringUsing(option_value, "|", &parts); + if (parts.size() != 2) { + *error = "Bad java_package, expecting filename|PackageName found '" + + option_value + "'"; + return false; + } + params.set_java_package(parts[0], parts[1]); + } else if (option_name == "java_outer_classname") { + vector<string> parts; + SplitStringUsing(option_value, "|", &parts); + if (parts.size() != 2) { + *error = "Bad java_outer_classname, " + "expecting filename|ClassName found '" + + option_value + "'"; + return false; + } + params.set_java_outer_classname(parts[0], parts[1]); + } else if (option_name == "store_unknown_fields") { + params.set_store_unknown_fields(option_value == "true"); + } else if (option_name == "java_multiple_files") { + params.set_override_java_multiple_files(option_value == "true"); + } else if (option_name == "java_nano_generate_has") { + params.set_generate_has(option_value == "true"); + } else if (option_name == "enum_style") { + params.set_java_enum_style(option_value == "java"); + } else if (option_name == "optional_field_style") { + params.set_optional_field_accessors(option_value == "accessors"); + params.set_use_reference_types_for_primitives(option_value == "reftypes" + || option_value == "reftypes_compat_mode"); + params.set_reftypes_primitive_enums( + option_value == "reftypes_compat_mode"); + if (option_value == "reftypes_compat_mode") { + params.set_generate_clear(false); + } + } else if (option_name == "generate_equals") { + params.set_generate_equals(option_value == "true"); + } else if (option_name == "ignore_services") { + params.set_ignore_services(option_value == "true"); + } else if (option_name == "parcelable_messages") { + params.set_parcelable_messages(option_value == "true"); + } else { + *error = "Ignore unknown javanano generator option: " + option_name; + } + } + + // Check illegal parameter combinations + // Note: the enum-like optional_field_style generator param ensures + // that we can never have illegal combinations of field styles + // (e.g. reftypes and accessors can't be on at the same time). + if (params.generate_has() + && (params.optional_field_accessors() + || params.use_reference_types_for_primitives())) { + error->assign("java_nano_generate_has=true cannot be used in conjunction" + " with optional_field_style=accessors or optional_field_style=reftypes"); + return false; + } + + // ----------------------------------------------------------------- + + FileGenerator file_generator(file, params); + if (!file_generator.Validate(error)) { + return false; + } + + string package_dir = + StringReplace(file_generator.java_package(), ".", "/", true); + if (!package_dir.empty()) package_dir += "/"; + + vector<string> all_files; + + if (IsOuterClassNeeded(params, file)) { + string java_filename = package_dir; + java_filename += file_generator.classname(); + java_filename += ".java"; + all_files.push_back(java_filename); + + // Generate main java file. + scoped_ptr<io::ZeroCopyOutputStream> output( + output_directory->Open(java_filename)); + io::Printer printer(output.get(), '$'); + file_generator.Generate(&printer); + } + + // Generate sibling files. + file_generator.GenerateSiblings(package_dir, output_directory, &all_files); + + // Generate output list if requested. + if (!output_list_file.empty()) { + // Generate output list. This is just a simple text file placed in a + // deterministic location which lists the .java files being generated. + scoped_ptr<io::ZeroCopyOutputStream> srclist_raw_output( + output_directory->Open(output_list_file)); + io::Printer srclist_printer(srclist_raw_output.get(), '$'); + for (int i = 0; i < all_files.size(); i++) { + srclist_printer.Print("$filename$\n", "filename", all_files[i]); + } + } + + return true; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_generator.h b/src/google/protobuf/compiler/javanano/javanano_generator.h new file mode 100644 index 00000000..6f9f7f2a --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_generator.h @@ -0,0 +1,72 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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. +// +// Generates Java nano code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVANANO_NANO_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_NANO_GENERATOR_H__ + +#include <string> +#include <google/protobuf/compiler/code_generator.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +// CodeGenerator implementation which generates Java nano code. If you create your +// own protocol compiler binary and you want it to support Java output for the +// nano runtime, you can do so by registering an instance of this CodeGenerator with +// the CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT JavaNanoGenerator : public CodeGenerator { + public: + JavaNanoGenerator(); + ~JavaNanoGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + GeneratorContext* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaNanoGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_NANO_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.cc b/src/google/protobuf/compiler/javanano/javanano_helpers.cc new file mode 100644 index 00000000..2149418a --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.cc @@ -0,0 +1,566 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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 <limits> +#include <vector> + +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/hash.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +const char kThickSeparator[] = + "// ===================================================================\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\n"; + +class RenameKeywords { + private: + hash_set<string> java_keywords_set_; + + public: + RenameKeywords() { + static const char* kJavaKeywordsList[] = { + // Reserved Java Keywords + "abstract", "assert", "boolean", "break", "byte", "case", "catch", + "char", "class", "const", "continue", "default", "do", "double", "else", + "enum", "extends", "final", "finally", "float", "for", "goto", "if", + "implements", "import", "instanceof", "int", "interface", "long", + "native", "new", "package", "private", "protected", "public", "return", + "short", "static", "strictfp", "super", "switch", "synchronized", + "this", "throw", "throws", "transient", "try", "void", "volatile", "while", + + // Reserved Keywords for Literals + "false", "null", "true" + }; + + for (int i = 0; i < GOOGLE_ARRAYSIZE(kJavaKeywordsList); i++) { + java_keywords_set_.insert(kJavaKeywordsList[i]); + } + } + + // Used to rename the a field name if it's a java keyword. Specifically + // this is used to rename the ["name"] or ["capitalized_name"] field params. + // (http://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html) + string RenameJavaKeywordsImpl(const string& input) { + string result = input; + + if (java_keywords_set_.find(result) != java_keywords_set_.end()) { + result += "_"; + } + + return result; + } + +}; + +static RenameKeywords sRenameKeywords; + +namespace { + +const char* kDefaultPackage = ""; + +const string& FieldName(const FieldDescriptor* field) { + // Groups are hacky: The name of the field is just the lower-cased name + // of the group type. In Java, though, we would like to retain the original + // capitalization of the type name. + if (field->type() == FieldDescriptor::TYPE_GROUP) { + return field->message_type()->name(); + } else { + return field->name(); + } +} + +string UnderscoresToCamelCaseImpl(const string& input, bool cap_next_letter) { + string result; + // Note: I distrust ctype.h due to locales. + for (int i = 0; i < input.size(); i++) { + if ('a' <= input[i] && input[i] <= 'z') { + if (cap_next_letter) { + result += input[i] + ('A' - 'a'); + } else { + result += input[i]; + } + cap_next_letter = false; + } else if ('A' <= input[i] && input[i] <= 'Z') { + if (i == 0 && !cap_next_letter) { + // Force first letter to lower-case unless explicitly told to + // capitalize it. + result += input[i] + ('a' - 'A'); + } else { + // Capital letters after the first are left as-is. + result += input[i]; + } + cap_next_letter = false; + } else if ('0' <= input[i] && input[i] <= '9') { + result += input[i]; + cap_next_letter = true; + } else { + cap_next_letter = true; + } + } + return result; +} + +} // namespace + +string UnderscoresToCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCaseImpl(FieldName(field), false); +} + +string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field) { + return UnderscoresToCamelCaseImpl(FieldName(field), true); +} + +string UnderscoresToCamelCase(const MethodDescriptor* method) { + return UnderscoresToCamelCaseImpl(method->name(), false); +} + +string RenameJavaKeywords(const string& input) { + return sRenameKeywords.RenameJavaKeywordsImpl(input); +} + +string StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +string FileClassName(const Params& params, const FileDescriptor* file) { + if (params.has_java_outer_classname(file->name())) { + return params.java_outer_classname(file->name()); + } else { + // Use the filename itself with underscores removed + // and a CamelCase style name. + string basename; + string::size_type last_slash = file->name().find_last_of('/'); + if (last_slash == string::npos) { + basename = file->name(); + } else { + basename = file->name().substr(last_slash + 1); + } + return UnderscoresToCamelCaseImpl(StripProto(basename), true); + } +} + +string FileJavaPackage(const Params& params, const FileDescriptor* file) { + if (params.has_java_package(file->name())) { + return params.java_package(file->name()); + } else { + string result = kDefaultPackage; + if (!file->package().empty()) { + if (!result.empty()) result += '.'; + result += file->package(); + } + return result; + } +} + +bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file) { + // If java_multiple_files is false, the outer class is always needed. + if (!params.java_multiple_files(file->name())) { + return true; + } + + // File-scope extensions need the outer class as the scope. + if (file->extension_count() != 0) { + return true; + } + + // If container interfaces are not generated, file-scope enums need the + // outer class as the scope. + if (file->enum_type_count() != 0 && !params.java_enum_style()) { + return true; + } + + return false; +} + +string ToJavaName(const Params& params, const string& name, bool is_class, + const Descriptor* parent, const FileDescriptor* file) { + string result; + if (parent != NULL) { + result.append(ClassName(params, parent)); + } else if (is_class && params.java_multiple_files(file->name())) { + result.append(FileJavaPackage(params, file)); + } else { + result.append(ClassName(params, file)); + } + if (!result.empty()) result.append(1, '.'); + result.append(RenameJavaKeywords(name)); + return result; +} + +string ClassName(const Params& params, const FileDescriptor* descriptor) { + string result = FileJavaPackage(params, descriptor); + if (!result.empty()) result += '.'; + result += FileClassName(params, descriptor); + return result; +} + +string ClassName(const Params& params, const EnumDescriptor* descriptor) { + const Descriptor* parent = descriptor->containing_type(); + // When using Java enum style, an enum's class name contains the enum name. + // Use the standard ToJavaName translation. + if (params.java_enum_style()) { + return ToJavaName(params, descriptor->name(), true, parent, + descriptor->file()); + } + // Otherwise the enum members are accessed from the enclosing class. + if (parent != NULL) { + return ClassName(params, parent); + } else { + return ClassName(params, descriptor->file()); + } +} + +string FieldConstantName(const FieldDescriptor *field) { + string name = field->name() + "_FIELD_NUMBER"; + UpperString(&name); + return name; +} + +string FieldDefaultConstantName(const FieldDescriptor *field) { + return "_" + RenameJavaKeywords(UnderscoresToCamelCase(field)) + "Default"; +} + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // We don't want to print group bodies so we cut off after the first line + // (the second line for extensions). + string def = field->DebugString(); + string::size_type first_line_end = def.find_first_of('\n'); + printer->Print("// $def$\n", + "def", def.substr(0, first_line_end)); + if (field->is_extension()) { + string::size_type second_line_start = first_line_end + 1; + string::size_type second_line_length = + def.find('\n', second_line_start) - second_line_start; + printer->Print("// $def$\n", + "def", def.substr(second_line_start, second_line_length)); + } +} + +JavaType GetJavaType(FieldDescriptor::Type field_type) { + switch (field_type) { + case FieldDescriptor::TYPE_INT32: + case FieldDescriptor::TYPE_UINT32: + case FieldDescriptor::TYPE_SINT32: + case FieldDescriptor::TYPE_FIXED32: + case FieldDescriptor::TYPE_SFIXED32: + return JAVATYPE_INT; + + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_UINT64: + case FieldDescriptor::TYPE_SINT64: + case FieldDescriptor::TYPE_FIXED64: + case FieldDescriptor::TYPE_SFIXED64: + return JAVATYPE_LONG; + + case FieldDescriptor::TYPE_FLOAT: + return JAVATYPE_FLOAT; + + case FieldDescriptor::TYPE_DOUBLE: + return JAVATYPE_DOUBLE; + + case FieldDescriptor::TYPE_BOOL: + return JAVATYPE_BOOLEAN; + + case FieldDescriptor::TYPE_STRING: + return JAVATYPE_STRING; + + case FieldDescriptor::TYPE_BYTES: + return JAVATYPE_BYTES; + + case FieldDescriptor::TYPE_ENUM: + return JAVATYPE_ENUM; + + case FieldDescriptor::TYPE_GROUP: + case FieldDescriptor::TYPE_MESSAGE: + return JAVATYPE_MESSAGE; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return JAVATYPE_INT; +} + +string PrimitiveTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT : return "int"; + case JAVATYPE_LONG : return "long"; + case JAVATYPE_FLOAT : return "float"; + case JAVATYPE_DOUBLE : return "double"; + case JAVATYPE_BOOLEAN: return "boolean"; + case JAVATYPE_STRING : return "java.lang.String"; + case JAVATYPE_BYTES : return "byte[]"; + case JAVATYPE_ENUM : return "int"; + case JAVATYPE_MESSAGE: return ""; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +string BoxedPrimitiveTypeName(JavaType type) { + switch (type) { + case JAVATYPE_INT : return "java.lang.Integer"; + case JAVATYPE_LONG : return "java.lang.Long"; + case JAVATYPE_FLOAT : return "java.lang.Float"; + case JAVATYPE_DOUBLE : return "java.lang.Double"; + case JAVATYPE_BOOLEAN: return "java.lang.Boolean"; + case JAVATYPE_STRING : return "java.lang.String"; + case JAVATYPE_BYTES : return "byte[]"; + case JAVATYPE_ENUM : return "java.lang.Integer"; + case JAVATYPE_MESSAGE: return ""; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +string EmptyArrayName(const Params& params, const FieldDescriptor* field) { + switch (GetJavaType(field)) { + case JAVATYPE_INT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; + case JAVATYPE_LONG : return "com.google.protobuf.nano.WireFormatNano.EMPTY_LONG_ARRAY"; + case JAVATYPE_FLOAT : return "com.google.protobuf.nano.WireFormatNano.EMPTY_FLOAT_ARRAY"; + case JAVATYPE_DOUBLE : return "com.google.protobuf.nano.WireFormatNano.EMPTY_DOUBLE_ARRAY"; + case JAVATYPE_BOOLEAN: return "com.google.protobuf.nano.WireFormatNano.EMPTY_BOOLEAN_ARRAY"; + case JAVATYPE_STRING : return "com.google.protobuf.nano.WireFormatNano.EMPTY_STRING_ARRAY"; + case JAVATYPE_BYTES : return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES_ARRAY"; + case JAVATYPE_ENUM : return "com.google.protobuf.nano.WireFormatNano.EMPTY_INT_ARRAY"; + case JAVATYPE_MESSAGE: return ClassName(params, field->message_type()) + ".EMPTY_ARRAY"; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +string DefaultValue(const Params& params, const FieldDescriptor* field) { + if (field->label() == FieldDescriptor::LABEL_REPEATED) { + return EmptyArrayName(params, field); + } + + if (params.use_reference_types_for_primitives()) { + if (params.reftypes_primitive_enums() + && field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + return "Integer.MIN_VALUE"; + } + return "null"; + } + + // Switch on cpp_type since we need to know which default_value_* method + // of FieldDescriptor to call. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + // Need to print as a signed int since Java has no unsigned. + return SimpleItoa(static_cast<int32>(field->default_value_uint32())); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field->default_value_int64()) + "L"; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(static_cast<int64>(field->default_value_uint64())) + + "L"; + case FieldDescriptor::CPPTYPE_DOUBLE: { + double value = field->default_value_double(); + if (value == numeric_limits<double>::infinity()) { + return "Double.POSITIVE_INFINITY"; + } else if (value == -numeric_limits<double>::infinity()) { + return "Double.NEGATIVE_INFINITY"; + } else if (value != value) { + return "Double.NaN"; + } else { + return SimpleDtoa(value) + "D"; + } + } + case FieldDescriptor::CPPTYPE_FLOAT: { + float value = field->default_value_float(); + if (value == numeric_limits<float>::infinity()) { + return "Float.POSITIVE_INFINITY"; + } else if (value == -numeric_limits<float>::infinity()) { + return "Float.NEGATIVE_INFINITY"; + } else if (value != value) { + return "Float.NaN"; + } else { + return SimpleFtoa(value) + "F"; + } + } + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_STRING: + if (!field->default_value_string().empty()) { + // Point it to the static final in the generated code. + return FieldDefaultConstantName(field); + } else { + if (field->type() == FieldDescriptor::TYPE_BYTES) { + return "com.google.protobuf.nano.WireFormatNano.EMPTY_BYTES"; + } else { + return "\"\""; + } + } + + case FieldDescriptor::CPPTYPE_ENUM: + return ClassName(params, field->enum_type()) + "." + + RenameJavaKeywords(field->default_value_enum()->name()); + + case FieldDescriptor::CPPTYPE_MESSAGE: + return "null"; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + + +static const char* kBitMasks[] = { + "0x00000001", + "0x00000002", + "0x00000004", + "0x00000008", + "0x00000010", + "0x00000020", + "0x00000040", + "0x00000080", + + "0x00000100", + "0x00000200", + "0x00000400", + "0x00000800", + "0x00001000", + "0x00002000", + "0x00004000", + "0x00008000", + + "0x00010000", + "0x00020000", + "0x00040000", + "0x00080000", + "0x00100000", + "0x00200000", + "0x00400000", + "0x00800000", + + "0x01000000", + "0x02000000", + "0x04000000", + "0x08000000", + "0x10000000", + "0x20000000", + "0x40000000", + "0x80000000", +}; + +string GetBitFieldName(int index) { + string var_name = "bitField"; + var_name += SimpleItoa(index); + var_name += "_"; + return var_name; +} + +string GetBitFieldNameForBit(int bit_index) { + return GetBitFieldName(bit_index / 32); +} + +string GenerateGetBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = "((" + var_name + " & " + mask + ") != 0)"; + return result; +} + +string GenerateSetBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = var_name + " |= " + mask; + return result; +} + +string GenerateClearBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = var_name + " = (" + var_name + " & ~" + mask + ")"; + return result; +} + +string GenerateDifferentBit(int bit_index) { + string var_name = GetBitFieldNameForBit(bit_index); + int bit_in_var_index = bit_index % 32; + + string mask = kBitMasks[bit_in_var_index]; + string result = "((" + var_name + " & " + mask + + ") != (other." + var_name + " & " + mask + "))"; + return result; +} + +void SetBitOperationVariables(const string name, + int bitIndex, map<string, string>* variables) { + (*variables)["get_" + name] = GenerateGetBit(bitIndex); + (*variables)["set_" + name] = GenerateSetBit(bitIndex); + (*variables)["clear_" + name] = GenerateClearBit(bitIndex); + (*variables)["different_" + name] = GenerateDifferentBit(bitIndex); +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_helpers.h b/src/google/protobuf/compiler/javanano/javanano_helpers.h new file mode 100644 index 00000000..29310743 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_helpers.h @@ -0,0 +1,189 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_HELPERS_H__ + +#include <string> +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/descriptor.h> +#include <google/protobuf/io/printer.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +// Commonly-used separator comments. Thick is a line of '=', thin is a line +// of '-'. +extern const char kThickSeparator[]; +extern const char kThinSeparator[]; + +// Converts the field's name to camel-case, e.g. "foo_bar_baz" becomes +// "fooBarBaz" or "FooBarBaz", respectively. +string UnderscoresToCamelCase(const FieldDescriptor* field); +string UnderscoresToCapitalizedCamelCase(const FieldDescriptor* field); + +// Appends an "_" to the end of a field where the name is a reserved java +// keyword. For example int32 public = 1 will generate int public_. +string RenameJavaKeywords(const string& input); + +// Similar, but for method names. (Typically, this merely has the effect +// of lower-casing the first letter of the name.) +string UnderscoresToCamelCase(const MethodDescriptor* method); + +// Strips ".proto" or ".protodevel" from the end of a filename. +string StripProto(const string& filename); + +// Gets the unqualified class name for the file. Each .proto file becomes a +// single Java class, with all its contents nested in that class. +string FileClassName(const Params& params, const FileDescriptor* file); + +// Returns the file's Java package name. +string FileJavaPackage(const Params& params, const FileDescriptor* file); + +// Returns whether the Java outer class is needed, i.e. whether the option +// java_multiple_files is false, or the proto file contains any file-scope +// enums/extensions. +bool IsOuterClassNeeded(const Params& params, const FileDescriptor* file); + +// Converts the given simple name of a proto entity to its fully-qualified name +// in the Java namespace, given that it is in the given file enclosed in the +// given parent message (or NULL for file-scope entities). Whether the file's +// outer class name should be included in the return value depends on factors +// inferrable from the given arguments, including is_class which indicates +// whether the entity translates to a Java class. +string ToJavaName(const Params& params, const string& name, bool is_class, + const Descriptor* parent, const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +inline string ClassName(const Params& params, const Descriptor* descriptor) { + return ToJavaName(params, descriptor->name(), true, + descriptor->containing_type(), descriptor->file()); +} +string ClassName(const Params& params, const EnumDescriptor* descriptor); +inline string ClassName(const Params& params, + const ServiceDescriptor* descriptor) { + return ToJavaName(params, descriptor->name(), true, NULL, descriptor->file()); +} +inline string ExtensionIdentifierName(const Params& params, + const FieldDescriptor* descriptor) { + return ToJavaName(params, descriptor->name(), false, + descriptor->extension_scope(), descriptor->file()); +} +string ClassName(const Params& params, const FileDescriptor* descriptor); + +// Get the unqualified name that should be used for a field's field +// number constant. +string FieldConstantName(const FieldDescriptor *field); + +string FieldDefaultConstantName(const FieldDescriptor *field); + +// Print the field's proto-syntax definition as a comment. +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field); + +enum JavaType { + JAVATYPE_INT, + JAVATYPE_LONG, + JAVATYPE_FLOAT, + JAVATYPE_DOUBLE, + JAVATYPE_BOOLEAN, + JAVATYPE_STRING, + JAVATYPE_BYTES, + JAVATYPE_ENUM, + JAVATYPE_MESSAGE +}; + +JavaType GetJavaType(FieldDescriptor::Type field_type); + +inline JavaType GetJavaType(const FieldDescriptor* field) { + return GetJavaType(field->type()); +} + +string PrimitiveTypeName(JavaType type); + +// Get the fully-qualified class name for a boxed primitive type, e.g. +// "java.lang.Integer" for JAVATYPE_INT. Returns NULL for enum and message +// types. +string BoxedPrimitiveTypeName(JavaType type); + +string EmptyArrayName(const Params& params, const FieldDescriptor* field); + +string DefaultValue(const Params& params, const FieldDescriptor* field); + + +// Methods for shared bitfields. + +// Gets the name of the shared bitfield for the given field index. +string GetBitFieldName(int index); + +// Gets the name of the shared bitfield for the given bit index. +// Effectively, GetBitFieldName(bit_index / 32) +string GetBitFieldNameForBit(int bit_index); + +// Generates the java code for the expression that returns whether the bit at +// the given bit index is set. +// Example: "((bitField1_ & 0x04000000) != 0)" +string GenerateGetBit(int bit_index); + +// Generates the java code for the expression that sets the bit at the given +// bit index. +// Example: "bitField1_ |= 0x04000000" +string GenerateSetBit(int bit_index); + +// Generates the java code for the expression that clears the bit at the given +// bit index. +// Example: "bitField1_ = (bitField1_ & ~0x04000000)" +string GenerateClearBit(int bit_index); + +// Generates the java code for the expression that returns whether the bit at +// the given bit index contains different values in the current object and +// another object accessible via the variable 'other'. +// Example: "((bitField1_ & 0x04000000) != (other.bitField1_ & 0x04000000))" +string GenerateDifferentBit(int bit_index); + +// Sets the 'get_*', 'set_*', 'clear_*' and 'different_*' variables, where * is +// the given name of the bit, to the appropriate Java expressions for the given +// bit index. +void SetBitOperationVariables(const string name, + int bitIndex, map<string, string>* variables); + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_HELPERS_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_message.cc b/src/google/protobuf/compiler/javanano/javanano_message.cc new file mode 100644 index 00000000..7c52ca31 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_message.cc @@ -0,0 +1,555 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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 <algorithm> +#include <google/protobuf/stubs/hash.h> +#include <google/protobuf/compiler/javanano/javanano_message.h> +#include <google/protobuf/compiler/javanano/javanano_enum.h> +#include <google/protobuf/compiler/javanano/javanano_extension.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/descriptor.pb.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +// Sort the fields of the given Descriptor by number into a new[]'d array +// and return it. +const FieldDescriptor** SortFieldsByNumber(const Descriptor* descriptor) { + const FieldDescriptor** fields = + new const FieldDescriptor*[descriptor->field_count()]; + for (int i = 0; i < descriptor->field_count(); i++) { + fields[i] = descriptor->field(i); + } + sort(fields, fields + descriptor->field_count(), + FieldOrderingByNumber()); + return fields; +} + +} // namespace + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor, const Params& params) + : params_(params), + descriptor_(descriptor), + field_generators_(descriptor, params) { +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { + // Generate static members for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(descriptor_->nested_type(i), params_) + .GenerateStaticVariables(printer); + } +} + +void MessageGenerator::GenerateStaticVariableInitializers( + io::Printer* printer) { + // Generate static member initializers for all nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(descriptor_->nested_type(i), params_) + .GenerateStaticVariableInitializers(printer); + } +} + +void MessageGenerator::Generate(io::Printer* printer) { + if (!params_.store_unknown_fields() && + (descriptor_->extension_count() != 0 || descriptor_->extension_range_count() != 0)) { + GOOGLE_LOG(FATAL) << "Extensions are only supported in NANO_RUNTIME if the " + "'store_unknown_fields' generator option is 'true'\n"; + } + + const string& file_name = descriptor_->file()->name(); + bool is_own_file = + params_.java_multiple_files(file_name) + && descriptor_->containing_type() == NULL; + + if (is_own_file) { + // Note: constants (from enums and fields requiring stored defaults, emitted in the loop below) + // may have the same names as constants in the nested classes. This causes Java warnings, but + // is not fatal, so we suppress those warnings here in the top-most class declaration. + printer->Print( + "\n" + "@SuppressWarnings(\"hiding\")\n" + "public final class $classname$ extends\n", + "classname", descriptor_->name()); + } else { + printer->Print( + "\n" + "public static final class $classname$ extends\n", + "classname", descriptor_->name()); + } + if (params_.store_unknown_fields() && params_.parcelable_messages()) { + printer->Print( + " com.google.protobuf.nano.android.ParcelableExtendableMessageNano<$classname$> {\n", + "classname", descriptor_->name()); + } else if (params_.store_unknown_fields()) { + printer->Print( + " com.google.protobuf.nano.ExtendableMessageNano<$classname$> {\n", + "classname", descriptor_->name()); + } else if (params_.parcelable_messages()) { + printer->Print( + " com.google.protobuf.nano.android.ParcelableMessageNano {\n"); + } else { + printer->Print( + " com.google.protobuf.nano.MessageNano {\n"); + } + printer->Indent(); + + // Nested types and extensions + for (int i = 0; i < descriptor_->extension_count(); i++) { + ExtensionGenerator(descriptor_->extension(i), params_).Generate(printer); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i), params_).Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + MessageGenerator(descriptor_->nested_type(i), params_).Generate(printer); + } + + // Lazy initialization of otherwise static final fields can help prevent the + // class initializer from being generated. We want to prevent it because it + // stops ProGuard from inlining any methods in this class into call sites and + // therefore reducing the method count. However, extensions are best kept as + // public static final fields with initializers, so with their existence we + // won't bother with lazy initialization. + bool lazy_init = descriptor_->extension_count() == 0; + + // Empty array + if (lazy_init) { + printer->Print( + "\n" + "private static volatile $classname$[] _emptyArray;\n" + "public static $classname$[] emptyArray() {\n" + " // Lazily initializes the empty array\n" + " if (_emptyArray == null) {\n" + " synchronized (\n" + " com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {\n" + " if (_emptyArray == null) {\n" + " _emptyArray = new $classname$[0];\n" + " }\n" + " }\n" + " }\n" + " return _emptyArray;\n" + "}\n", + "classname", descriptor_->name()); + } else { + printer->Print( + "\n" + "private static final $classname$[] EMPTY_ARRAY = {};\n" + "public static $classname$[] emptyArray() {\n" + " return EMPTY_ARRAY;\n" + "}\n", + "classname", descriptor_->name()); + } + + // Integers for bit fields + int totalInts = (field_generators_.total_bits() + 31) / 32; + if (totalInts > 0) { + printer->Print("\n"); + for (int i = 0; i < totalInts; i++) { + printer->Print("private int $bit_field_name$;\n", + "bit_field_name", GetBitFieldName(i)); + } + } + + // Fields and maybe their default values + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)).GenerateMembers( + printer, lazy_init); + } + + // Constructor, with lazy init code if needed + if (lazy_init && field_generators_.saved_defaults_needed()) { + printer->Print( + "\n" + "private static volatile boolean _classInitialized;\n" + "\n" + "public $classname$() {\n" + " // Lazily initializes the field defaults\n" + " if (!_classInitialized) {\n" + " synchronized (\n" + " com.google.protobuf.nano.InternalNano.LAZY_INIT_LOCK) {\n" + " if (!_classInitialized) {\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Indent(); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitSavedDefaultCode(printer); + } + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " _classInitialized = true;\n" + " }\n" + " }\n" + " }\n"); + if (params_.generate_clear()) { + printer->Print(" clear();\n"); + } + printer->Print("}\n"); + } else { + if (params_.generate_clear()) { + printer->Print( + "\n" + "public $classname$() {\n" + " clear();\n" + "}\n", + "classname", descriptor_->name()); + } + } + + // Other methods in this class + + GenerateClear(printer); + + if (params_.generate_equals()) { + GenerateEquals(printer); + GenerateHashCode(printer); + } + + GenerateMessageSerializationMethods(printer); + GenerateMergeFromMethods(printer); + GenerateParseFromMethods(printer); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +void MessageGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + // Rely on the parent implementations of writeTo() and getSerializedSize() + // if there are no fields to serialize in this message. + if (descriptor_->field_count() == 0) { + return; + } + + scoped_array<const FieldDescriptor*> sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "\n" + "@Override\n" + "public void writeTo(com.google.protobuf.nano.CodedOutputByteBufferNano output)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + + // Output the fields in sorted order + for (int i = 0; i < descriptor_->field_count(); i++) { + GenerateSerializeOneField(printer, sorted_fields[i]); + } + + // The parent implementation will write any unknown fields if necessary. + printer->Print( + "super.writeTo(output);\n"); + + printer->Outdent(); + printer->Print("}\n"); + + // The parent implementation will get the serialized size for unknown + // fields if necessary. + printer->Print( + "\n" + "@Override\n" + "protected int computeSerializedSize() {\n" + " int size = super.computeSerializedSize();\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + printer->Outdent(); + printer->Print( + " return size;\n" + "}\n"); +} + +void MessageGenerator::GenerateMergeFromMethods(io::Printer* printer) { + scoped_array<const FieldDescriptor*> sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "\n" + "@Override\n" + "public $classname$ mergeFrom(\n" + " com.google.protobuf.nano.CodedInputByteBufferNano input)\n" + " throws java.io.IOException {\n", + "classname", descriptor_->name()); + + printer->Indent(); + + printer->Print( + "while (true) {\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\n" + "switch (tag) {\n"); + printer->Indent(); + + printer->Print( + "case 0:\n" // zero signals EOF / limit reached + " return this;\n" + "default: {\n"); + + printer->Indent(); + if (params_.store_unknown_fields()) { + printer->Print( + "if (!storeUnknownField(input, tag)) {\n" + " return this;\n" + "}\n"); + } else { + printer->Print( + "if (!com.google.protobuf.nano.WireFormatNano.parseUnknownField(input, tag)) {\n" + " return this;\n" // it's an endgroup tag + "}\n"); + } + printer->Print("break;\n"); + printer->Outdent(); + printer->Print("}\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormatLite::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateMergingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + + if (field->is_packable()) { + // To make packed = true wire compatible, we generate parsing code from a + // packed version of this field regardless of field->options().packed(). + uint32 packed_tag = WireFormatLite::MakeTag(field->number(), + WireFormatLite::WIRETYPE_LENGTH_DELIMITED); + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(packed_tag)); + printer->Indent(); + + field_generators_.get(field).GenerateMergingCodeFromPacked(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + } + + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + " }\n" // while (true) + "}\n"); +} + +void MessageGenerator:: +GenerateParseFromMethods(io::Printer* printer) { + // Note: These are separate from GenerateMessageSerializationMethods() + // because they need to be generated even for messages that are optimized + // for code size. + printer->Print( + "\n" + "public static $classname$ parseFrom(byte[] data)\n" + " throws com.google.protobuf.nano.InvalidProtocolBufferNanoException {\n" + " return com.google.protobuf.nano.MessageNano.mergeFrom(new $classname$(), data);\n" + "}\n" + "\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.nano.CodedInputByteBufferNano input)\n" + " throws java.io.IOException {\n" + " return new $classname$().mergeFrom(input);\n" + "}\n", + "classname", descriptor_->name()); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void MessageGenerator::GenerateClear(io::Printer* printer) { + if (!params_.generate_clear()) { + return; + } + printer->Print( + "\n" + "public $classname$ clear() {\n", + "classname", descriptor_->name()); + printer->Indent(); + + // Clear bit fields. + int totalInts = (field_generators_.total_bits() + 31) / 32; + for (int i = 0; i < totalInts; i++) { + printer->Print("$bit_field_name$ = 0;\n", + "bit_field_name", GetBitFieldName(i)); + } + + // Call clear for all of the fields. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateClearCode(printer); + } + + // Clear unknown fields. + if (params_.store_unknown_fields()) { + printer->Print("unknownFieldData = null;\n"); + } + + printer->Outdent(); + printer->Print( + " cachedSize = -1;\n" + " return this;\n" + "}\n"); +} + +void MessageGenerator::GenerateEquals(io::Printer* printer) { + // Don't override if there are no fields. We could generate an + // equals method that compares types, but often empty messages + // are used as namespaces. + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public boolean equals(Object o) {\n"); + printer->Indent(); + printer->Print( + "if (o == this) {\n" + " return true;\n" + "}\n" + "if (!(o instanceof $classname$)) {\n" + " return false;\n" + "}\n" + "$classname$ other = ($classname$) o;\n", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateEqualsCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "return unknownFieldDataEquals(other);\n"); + } else { + printer->Print( + "return true;\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator::GenerateHashCode(io::Printer* printer) { + if (descriptor_->field_count() == 0 && !params_.store_unknown_fields()) { + return; + } + + printer->Print( + "\n" + "@Override\n" + "public int hashCode() {\n"); + printer->Indent(); + + printer->Print("int result = 17;\n"); + printer->Print("result = 31 * result + getClass().getName().hashCode();\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + field_generators_.get(field).GenerateHashCodeCode(printer); + } + + if (params_.store_unknown_fields()) { + printer->Print( + "result = 31 * result + unknownFieldDataHashCode();\n"); + } + + printer->Print("return result;\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +// =================================================================== + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_message.h b/src/google/protobuf/compiler/javanano/javanano_message.h new file mode 100644 index 00000000..6f25a3a0 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_message.h @@ -0,0 +1,95 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_MESSAGE_H__ + +#include <string> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/compiler/javanano/javanano_field.h> +#include <google/protobuf/compiler/javanano/javanano_params.h> +#include <google/protobuf/stubs/common.h> + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace javanano { + +class MessageGenerator { + public: + explicit MessageGenerator(const Descriptor* descriptor, const Params& params); + ~MessageGenerator(); + + // All static variables have to be declared at the top-level of the file + // so that we can control initialization order, which is important for + // DescriptorProto bootstrapping to work. + void GenerateStaticVariables(io::Printer* printer); + + // Output code which initializes the static variables generated by + // GenerateStaticVariables(). + void GenerateStaticVariableInitializers(io::Printer* printer); + + // Generate the class itself. + void Generate(io::Printer* printer); + + private: + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateMergeFromMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + + void GenerateClear(io::Printer* printer); + void GenerateEquals(io::Printer* printer); + void GenerateHashCode(io::Printer* printer); + + const Params& params_; + const Descriptor* descriptor_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.cc b/src/google/protobuf/compiler/javanano/javanano_message_field.cc new file mode 100644 index 00000000..a46081d0 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.cc @@ -0,0 +1,259 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <map> +#include <string> + +#include <google/protobuf/compiler/javanano/javanano_message_field.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const Params& params, + const FieldDescriptor* descriptor, map<string, string>* variables) { + (*variables)["name"] = + RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); + (*variables)["capitalized_name"] = + RenameJavaKeywords(UnderscoresToCapitalizedCamelCase(descriptor)); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = ClassName(params, descriptor->message_type()); + (*variables)["group_or_message"] = + (descriptor->type() == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message"; + (*variables)["message_name"] = descriptor->containing_type()->name(); + //(*variables)["message_type"] = descriptor->message_type()->name(); + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetMessageVariables(params, descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { + printer->Print(variables_, + "public $type$ $name$;\n"); +} + +void MessageFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = null;\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " this.$name$ = new $type$();\n" + "}\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup(this.$name$, $number$);\n"); + } else { + printer->Print(variables_, + "input.readMessage(this.$name$);\n"); + } +} + +void MessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null) {\n" + " output.write$group_or_message$($number$, this.$name$);\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null) {\n" + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$group_or_message$Size($number$, this.$name$);\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ == null) { \n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else {\n" + " if (!this.$name$.equals(other.$name$)) {\n" + " return false;\n" + " }\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result +\n" + " (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetMessageVariables(params, descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /* unused lazy_init */) const { + printer->Print(variables_, + "public $type$[] $name$;\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = $type$.emptyArray();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // First, figure out the length of the array, then parse. + printer->Print(variables_, + "int arrayLength = com.google.protobuf.nano.WireFormatNano\n" + " .getRepeatedFieldArrayLength(input, $tag$);\n" + "int i = this.$name$ == null ? 0 : this.$name$.length;\n" + "$type$[] newArray =\n" + " new $type$[i + arrayLength];\n" + "if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + "}\n" + "for (; i < newArray.length - 1; i++) {\n" + " newArray[i] = new $type$();\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + " input.readGroup(newArray[i], $number$);\n"); + } else { + printer->Print(variables_, + " input.readMessage(newArray[i]);\n"); + } + + printer->Print(variables_, + " input.readTag();\n" + "}\n" + "// Last one without readTag.\n" + "newArray[i] = new $type$();\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup(newArray[i], $number$);\n"); + } else { + printer->Print(variables_, + "input.readMessage(newArray[i]);\n"); + } + + printer->Print(variables_, + "this.$name$ = newArray;\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n" + " for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " output.write$group_or_message$($number$, element);\n" + " }\n" + " }\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n" + " for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$group_or_message$Size($number$, element);\n" + " }\n" + " }\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_message_field.h b/src/google/protobuf/compiler/javanano/javanano_message_field.h new file mode 100644 index 00000000..5d35fd24 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_message_field.h @@ -0,0 +1,96 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_MESSAGE_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/javanano/javanano_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator( + const FieldDescriptor* descriptor, const Params& params); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor, + const Params& params); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/javanano/javanano_params.h b/src/google/protobuf/compiler/javanano/javanano_params.h new file mode 100644 index 00000000..4691f360 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_params.h @@ -0,0 +1,240 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2010 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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: wink@google.com (Wink Saville) + +#ifndef PROTOBUF_COMPILER_JAVANANO_JAVANANO_PARAMS_H_ +#define PROTOBUF_COMPILER_JAVANANO_JAVANANO_PARAMS_H_ + +#include <map> +#include <set> +#include <google/protobuf/stubs/strutil.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +enum eMultipleFiles { JAVANANO_MUL_UNSET, JAVANANO_MUL_FALSE, JAVANANO_MUL_TRUE }; + +// Parameters for used by the generators +class Params { + public: + typedef map<string, string> NameMap; + typedef set<string> NameSet; + private: + string empty_; + string base_name_; + eMultipleFiles override_java_multiple_files_; + bool store_unknown_fields_; + NameMap java_packages_; + NameMap java_outer_classnames_; + NameSet java_multiple_files_; + bool generate_has_; + bool java_enum_style_; + bool optional_field_accessors_; + bool use_reference_types_for_primitives_; + bool generate_equals_; + bool ignore_services_; + bool parcelable_messages_; + bool reftypes_primitive_enums_; + bool generate_clear_; + + public: + Params(const string & base_name) : + empty_(""), + base_name_(base_name), + override_java_multiple_files_(JAVANANO_MUL_UNSET), + store_unknown_fields_(false), + generate_has_(false), + java_enum_style_(false), + optional_field_accessors_(false), + use_reference_types_for_primitives_(false), + generate_equals_(false), + ignore_services_(false), + parcelable_messages_(false), + reftypes_primitive_enums_(false), + generate_clear_(true) { + } + + const string& base_name() const { + return base_name_; + } + + bool has_java_package(const string& file_name) const { + return java_packages_.find(file_name) + != java_packages_.end(); + } + void set_java_package(const string& file_name, + const string& java_package) { + java_packages_[file_name] = java_package; + } + const string& java_package(const string& file_name) const { + NameMap::const_iterator itr; + + itr = java_packages_.find(file_name); + if (itr == java_packages_.end()) { + return empty_; + } else { + return itr->second; + } + } + const NameMap& java_packages() { + return java_packages_; + } + + bool has_java_outer_classname(const string& file_name) const { + return java_outer_classnames_.find(file_name) + != java_outer_classnames_.end(); + } + void set_java_outer_classname(const string& file_name, + const string& java_outer_classname) { + java_outer_classnames_[file_name] = java_outer_classname; + } + const string& java_outer_classname(const string& file_name) const { + NameMap::const_iterator itr; + + itr = java_outer_classnames_.find(file_name); + if (itr == java_outer_classnames_.end()) { + return empty_; + } else { + return itr->second; + } + } + const NameMap& java_outer_classnames() { + return java_outer_classnames_; + } + + void set_override_java_multiple_files(bool java_multiple_files) { + if (java_multiple_files) { + override_java_multiple_files_ = JAVANANO_MUL_TRUE; + } else { + override_java_multiple_files_ = JAVANANO_MUL_FALSE; + } + } + void clear_override_java_multiple_files() { + override_java_multiple_files_ = JAVANANO_MUL_UNSET; + } + + void set_java_multiple_files(const string& file_name, bool value) { + if (value) { + java_multiple_files_.insert(file_name); + } else { + java_multiple_files_.erase(file_name); + } + } + bool java_multiple_files(const string& file_name) const { + switch (override_java_multiple_files_) { + case JAVANANO_MUL_FALSE: + return false; + case JAVANANO_MUL_TRUE: + return true; + default: + return java_multiple_files_.find(file_name) + != java_multiple_files_.end(); + } + } + + void set_store_unknown_fields(bool value) { + store_unknown_fields_ = value; + } + bool store_unknown_fields() const { + return store_unknown_fields_; + } + + void set_generate_has(bool value) { + generate_has_ = value; + } + bool generate_has() const { + return generate_has_; + } + + void set_java_enum_style(bool value) { + java_enum_style_ = value; + } + bool java_enum_style() const { + return java_enum_style_; + } + + void set_optional_field_accessors(bool value) { + optional_field_accessors_ = value; + } + bool optional_field_accessors() const { + return optional_field_accessors_; + } + + void set_use_reference_types_for_primitives(bool value) { + use_reference_types_for_primitives_ = value; + } + bool use_reference_types_for_primitives() const { + return use_reference_types_for_primitives_; + } + + void set_generate_equals(bool value) { + generate_equals_ = value; + } + bool generate_equals() const { + return generate_equals_; + } + + void set_ignore_services(bool value) { + ignore_services_ = value; + } + bool ignore_services() const { + return ignore_services_; + } + + void set_parcelable_messages(bool value) { + parcelable_messages_ = value; + } + bool parcelable_messages() const { + return parcelable_messages_; + } + + void set_reftypes_primitive_enums(bool value) { + reftypes_primitive_enums_ = value; + } + bool reftypes_primitive_enums() const { + return reftypes_primitive_enums_; + } + + void set_generate_clear(bool value) { + generate_clear_ = value; + } + bool generate_clear() const { + return generate_clear_; + } +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google +#endif // PROTOBUF_COMPILER_JAVANANO_JAVANANO_PARAMS_H_ diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc new file mode 100644 index 00000000..a3bc3a84 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.cc @@ -0,0 +1,910 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include <map> +#include <math.h> +#include <string> + +#include <google/protobuf/compiler/javanano/javanano_primitive_field.h> +#include <google/protobuf/stubs/common.h> +#include <google/protobuf/compiler/javanano/javanano_helpers.h> +#include <google/protobuf/io/printer.h> +#include <google/protobuf/wire_format.h> +#include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +using internal::WireFormat; +using internal::WireFormatLite; + +namespace { + +bool IsReferenceType(JavaType type) { + switch (type) { + case JAVATYPE_INT : return false; + case JAVATYPE_LONG : return false; + case JAVATYPE_FLOAT : return false; + case JAVATYPE_DOUBLE : return false; + case JAVATYPE_BOOLEAN: return false; + case JAVATYPE_STRING : return true; + case JAVATYPE_BYTES : return true; + case JAVATYPE_ENUM : return false; + case JAVATYPE_MESSAGE: return true; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + +bool IsArrayType(JavaType type) { + switch (type) { + case JAVATYPE_INT : return false; + case JAVATYPE_LONG : return false; + case JAVATYPE_FLOAT : return false; + case JAVATYPE_DOUBLE : return false; + case JAVATYPE_BOOLEAN: return false; + case JAVATYPE_STRING : return false; + case JAVATYPE_BYTES : return true; + case JAVATYPE_ENUM : return false; + case JAVATYPE_MESSAGE: return false; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + +const char* GetCapitalizedType(const FieldDescriptor* field) { + switch (field->type()) { + case FieldDescriptor::TYPE_INT32 : return "Int32" ; + case FieldDescriptor::TYPE_UINT32 : return "UInt32" ; + case FieldDescriptor::TYPE_SINT32 : return "SInt32" ; + case FieldDescriptor::TYPE_FIXED32 : return "Fixed32" ; + case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; + case FieldDescriptor::TYPE_INT64 : return "Int64" ; + case FieldDescriptor::TYPE_UINT64 : return "UInt64" ; + case FieldDescriptor::TYPE_SINT64 : return "SInt64" ; + case FieldDescriptor::TYPE_FIXED64 : return "Fixed64" ; + case FieldDescriptor::TYPE_SFIXED64: return "SFixed64"; + case FieldDescriptor::TYPE_FLOAT : return "Float" ; + case FieldDescriptor::TYPE_DOUBLE : return "Double" ; + case FieldDescriptor::TYPE_BOOL : return "Bool" ; + case FieldDescriptor::TYPE_STRING : return "String" ; + case FieldDescriptor::TYPE_BYTES : return "Bytes" ; + case FieldDescriptor::TYPE_ENUM : return "Enum" ; + case FieldDescriptor::TYPE_GROUP : return "Group" ; + case FieldDescriptor::TYPE_MESSAGE : return "Message" ; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +// For encodings with fixed sizes, returns that size in bytes. Otherwise +// returns -1. +int FixedSize(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return -1; + case FieldDescriptor::TYPE_INT64 : return -1; + case FieldDescriptor::TYPE_UINT32 : return -1; + case FieldDescriptor::TYPE_UINT64 : return -1; + case FieldDescriptor::TYPE_SINT32 : return -1; + case FieldDescriptor::TYPE_SINT64 : return -1; + case FieldDescriptor::TYPE_FIXED32 : return WireFormatLite::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return WireFormatLite::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return WireFormatLite::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return WireFormatLite::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return WireFormatLite::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return WireFormatLite::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return WireFormatLite::kBoolSize; + case FieldDescriptor::TYPE_ENUM : return -1; + + case FieldDescriptor::TYPE_STRING : return -1; + case FieldDescriptor::TYPE_BYTES : return -1; + case FieldDescriptor::TYPE_GROUP : return -1; + case FieldDescriptor::TYPE_MESSAGE : return -1; + + // No default because we want the compiler to complain if any new + // types are added. + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; +} + +// Return true if the type is a that has variable length +// for instance String's. +bool IsVariableLenType(JavaType type) { + switch (type) { + case JAVATYPE_INT : return false; + case JAVATYPE_LONG : return false; + case JAVATYPE_FLOAT : return false; + case JAVATYPE_DOUBLE : return false; + case JAVATYPE_BOOLEAN: return false; + case JAVATYPE_STRING : return true; + case JAVATYPE_BYTES : return true; + case JAVATYPE_ENUM : return false; + case JAVATYPE_MESSAGE: return true; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return false; +} + +bool AllAscii(const string& text) { + for (int i = 0; i < text.size(); i++) { + if ((text[i] & 0x80) != 0) { + return false; + } + } + return true; +} + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, const Params params, + map<string, string>* variables) { + (*variables)["name"] = + RenameJavaKeywords(UnderscoresToCamelCase(descriptor)); + (*variables)["capitalized_name"] = + RenameJavaKeywords(UnderscoresToCapitalizedCamelCase(descriptor)); + (*variables)["number"] = SimpleItoa(descriptor->number()); + if (params.use_reference_types_for_primitives() + && !descriptor->is_repeated()) { + (*variables)["type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + } else { + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + } + // Deals with defaults. For C++-string types (string and bytes), + // we might need to have the generated code do the unicode decoding + // (see comments in InternalNano.java for gory details.). We would + // like to do this once into a static field and re-use that from + // then on. + if (descriptor->cpp_type() == FieldDescriptor::CPPTYPE_STRING && + !descriptor->default_value_string().empty() && + !params.use_reference_types_for_primitives()) { + if (descriptor->type() == FieldDescriptor::TYPE_BYTES) { + (*variables)["default"] = DefaultValue(params, descriptor); + (*variables)["default_constant"] = FieldDefaultConstantName(descriptor); + (*variables)["default_constant_value"] = strings::Substitute( + "com.google.protobuf.nano.InternalNano.bytesDefaultValue(\"$0\")", + CEscape(descriptor->default_value_string())); + (*variables)["default_copy_if_needed"] = + (*variables)["default"] + ".clone()"; + } else if (AllAscii(descriptor->default_value_string())) { + // All chars are ASCII. In this case directly referencing a + // CEscape()'d string literal works fine. + (*variables)["default"] = + "\"" + CEscape(descriptor->default_value_string()) + "\""; + (*variables)["default_copy_if_needed"] = (*variables)["default"]; + } else { + // Strings where some chars are non-ASCII. We need to save the + // default value. + (*variables)["default"] = DefaultValue(params, descriptor); + (*variables)["default_constant"] = FieldDefaultConstantName(descriptor); + (*variables)["default_constant_value"] = strings::Substitute( + "com.google.protobuf.nano.InternalNano.stringDefaultValue(\"$0\")", + CEscape(descriptor->default_value_string())); + (*variables)["default_copy_if_needed"] = (*variables)["default"]; + } + } else { + // Non-string, non-bytes field. Defaults are literals. + (*variables)["default"] = DefaultValue(params, descriptor); + (*variables)["default_copy_if_needed"] = (*variables)["default"]; + } + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); + (*variables)["tag"] = SimpleItoa(WireFormat::MakeTag(descriptor)); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); + (*variables)["non_packed_tag"] = SimpleItoa( + internal::WireFormatLite::MakeTag(descriptor->number(), + internal::WireFormat::WireTypeForFieldType(descriptor->type()))); + int fixed_size = FixedSize(descriptor->type()); + if (fixed_size != -1) { + (*variables)["fixed_size"] = SimpleItoa(fixed_size); + } + (*variables)["message_name"] = descriptor->containing_type()->name(); + (*variables)["empty_array_name"] = EmptyArrayName(params, descriptor); +} +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, params, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +bool PrimitiveFieldGenerator::SavedDefaultNeeded() const { + return variables_.find("default_constant") != variables_.end(); +} + +void PrimitiveFieldGenerator::GenerateInitSavedDefaultCode(io::Printer* printer) const { + if (variables_.find("default_constant") != variables_.end()) { + printer->Print(variables_, + "$default_constant$ = $default_constant_value$;\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer, bool lazy_init) const { + if (variables_.find("default_constant") != variables_.end()) { + // Those primitive types that need a saved default. + if (lazy_init) { + printer->Print(variables_, + "private static $type$ $default_constant$;\n"); + } else { + printer->Print(variables_, + "private static final $type$ $default_constant$ =\n" + " $default_constant_value$;\n"); + } + } + + printer->Print(variables_, + "public $type$ $name$;\n"); + + if (params_.generate_has()) { + printer->Print(variables_, + "public boolean has$capitalized_name$;\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = $default_copy_if_needed$;\n"); + + if (params_.generate_has()) { + printer->Print(variables_, + "has$capitalized_name$ = false;\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "this.$name$ = input.read$capitalized_type$();\n"); + + if (params_.generate_has()) { + printer->Print(variables_, + "has$capitalized_name$ = true;\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateSerializationConditional(io::Printer* printer) const { + if (params_.use_reference_types_for_primitives()) { + // For reference type mode, serialize based on equality + // to null. + printer->Print(variables_, + "if (this.$name$ != null) {\n"); + return; + } + if (params_.generate_has()) { + printer->Print(variables_, + "if (has$capitalized_name$ || "); + } else { + printer->Print(variables_, + "if ("); + } + JavaType java_type = GetJavaType(descriptor_); + if (IsArrayType(java_type)) { + printer->Print(variables_, + "!java.util.Arrays.equals(this.$name$, $default$)) {\n"); + } else if (IsReferenceType(java_type)) { + printer->Print(variables_, + "!this.$name$.equals($default$)) {\n"); + } else if (java_type == JAVATYPE_FLOAT) { + printer->Print(variables_, + "java.lang.Float.floatToIntBits(this.$name$)\n" + " != java.lang.Float.floatToIntBits($default$)) {\n"); + } else if (java_type == JAVATYPE_DOUBLE) { + printer->Print(variables_, + "java.lang.Double.doubleToLongBits(this.$name$)\n" + " != java.lang.Double.doubleToLongBits($default$)) {\n"); + } else { + printer->Print(variables_, + "this.$name$ != $default$) {\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + if (descriptor_->is_required() && !params_.generate_has()) { + // Always serialize a required field if we don't have the 'has' signal. + printer->Print(variables_, + "output.write$capitalized_type$($number$, this.$name$);\n"); + } else { + GenerateSerializationConditional(printer); + printer->Print(variables_, + " output.write$capitalized_type$($number$, this.$name$);\n" + "}\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + if (descriptor_->is_required() && !params_.generate_has()) { + printer->Print(variables_, + "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$Size($number$, this.$name$);\n"); + } else { + GenerateSerializationConditional(printer); + printer->Print(variables_, + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$Size($number$, this.$name$);\n" + "}\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + // We define equality as serialized form equality. If generate_has(), + // then if the field value equals the default value in both messages, + // but one's 'has' field is set and the other's is not, the serialized + // forms are different and we should return false. + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "if (!java.util.Arrays.equals(this.$name$, other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (java.util.Arrays.equals(this.$name$, $default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "if (this.$name$ == null) {\n" + " if (other.$name$ != null) {\n" + " return false;\n" + " }\n" + "} else if (!this.$name$.equals(other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$.equals($default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } else if (java_type == JAVATYPE_FLOAT) { + printer->Print(variables_, + "{\n" + " int bits = java.lang.Float.floatToIntBits(this.$name$);\n" + " if (bits != java.lang.Float.floatToIntBits(other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (bits == java.lang.Float.floatToIntBits($default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + " }\n" + "}\n"); + } else if (java_type == JAVATYPE_DOUBLE) { + printer->Print(variables_, + "{\n" + " long bits = java.lang.Double.doubleToLongBits(this.$name$);\n" + " if (bits != java.lang.Double.doubleToLongBits(other.$name$)"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (bits == java.lang.Double.doubleToLongBits($default$)\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + " }\n" + "}\n"); + } else { + printer->Print(variables_, + "if (this.$name$ != other.$name$"); + if (params_.generate_has()) { + printer->Print(variables_, + "\n" + " || (this.$name$ == $default$\n" + " && this.has$capitalized_name$ != other.has$capitalized_name$)"); + } + printer->Print(") {\n" + " return false;\n" + "}\n"); + } +} + +void PrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + JavaType java_type = GetJavaType(descriptor_); + if (java_type == JAVATYPE_BYTES) { + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode(this.$name$);\n"); + } else if (java_type == JAVATYPE_STRING + || params_.use_reference_types_for_primitives()) { + printer->Print(variables_, + "result = 31 * result\n" + " + (this.$name$ == null ? 0 : this.$name$.hashCode());\n"); + } else { + switch (java_type) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + printer->Print(variables_, + "result = 31 * result + this.$name$;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result\n" + " + (int) (this.$name$ ^ (this.$name$ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result\n" + " + java.lang.Float.floatToIntBits(this.$name$);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits(this.$name$);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + (this.$name$ ? 1231 : 1237);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } + } +} + +// =================================================================== + +AccessorPrimitiveFieldGenerator:: +AccessorPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Params& params, int has_bit_index) + : FieldGenerator(params), descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, params, &variables_); + SetBitOperationVariables("has", has_bit_index, &variables_); +} + +AccessorPrimitiveFieldGenerator::~AccessorPrimitiveFieldGenerator() {} + +bool AccessorPrimitiveFieldGenerator::SavedDefaultNeeded() const { + return variables_.find("default_constant") != variables_.end(); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateInitSavedDefaultCode(io::Printer* printer) const { + if (variables_.find("default_constant") != variables_.end()) { + printer->Print(variables_, + "$default_constant$ = $default_constant_value$;\n"); + } +} + +void AccessorPrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer, bool lazy_init) const { + if (variables_.find("default_constant") != variables_.end()) { + // Those primitive types that need a saved default. + if (lazy_init) { + printer->Print(variables_, + "private static $type$ $default_constant$;\n"); + } else { + printer->Print(variables_, + "private static final $type$ $default_constant$ =\n" + " $default_constant_value$;\n"); + } + } + printer->Print(variables_, + "private $type$ $name$_;\n" + "public $type$ get$capitalized_name$() {\n" + " return $name$_;\n" + "}\n" + "public $message_name$ set$capitalized_name$($type$ value) {\n"); + if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + " if (value == null) {\n" + " throw new java.lang.NullPointerException();\n" + " }\n"); + } + printer->Print(variables_, + " $name$_ = value;\n" + " $set_has$;\n" + " return this;\n" + "}\n" + "public boolean has$capitalized_name$() {\n" + " return $get_has$;\n" + "}\n" + "public $message_name$ clear$capitalized_name$() {\n" + " $name$_ = $default_copy_if_needed$;\n" + " $clear_has$;\n" + " return this;\n" + "}\n"); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = $default_copy_if_needed$;\n"); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$_ = input.read$capitalized_type$();\n" + "$set_has$;\n"); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has$) {\n" + " output.write$capitalized_type$($number$, $name$_);\n" + "}\n"); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($get_has$) {\n" + " size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$Size($number$, $name$_);\n" + "}\n"); +} + +void AccessorPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + // For all Java primitive types below, the equality checks match the + // results of BoxedType.valueOf(primitiveValue).equals(otherValue). + case JAVATYPE_FLOAT: + printer->Print(variables_, + "if ($different_has$\n" + " || java.lang.Float.floatToIntBits($name$_)\n" + " != java.lang.Float.floatToIntBits(other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "if ($different_has$\n" + " || java.lang.Double.doubleToLongBits($name$_)\n" + " != java.lang.Double.doubleToLongBits(other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_INT: + case JAVATYPE_LONG: + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "if ($different_has$\n" + " || $name$_ != other.$name$_) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !$name$_.equals(other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "if ($different_has$\n" + " || !java.util.Arrays.equals($name$_, other.$name$_)) {\n" + " return false;\n" + "}\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } +} + +void AccessorPrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + switch (GetJavaType(descriptor_)) { + // For all Java primitive types below, the hash codes match the + // results of BoxedType.valueOf(primitiveValue).hashCode(). + case JAVATYPE_INT: + printer->Print(variables_, + "result = 31 * result + $name$_;\n"); + break; + case JAVATYPE_LONG: + printer->Print(variables_, + "result = 31 * result + (int) ($name$_ ^ ($name$_ >>> 32));\n"); + break; + case JAVATYPE_FLOAT: + printer->Print(variables_, + "result = 31 * result +\n" + " java.lang.Float.floatToIntBits($name$_);\n"); + break; + case JAVATYPE_DOUBLE: + printer->Print(variables_, + "{\n" + " long v = java.lang.Double.doubleToLongBits($name$_);\n" + " result = 31 * result + (int) (v ^ (v >>> 32));\n" + "}\n"); + break; + case JAVATYPE_BOOLEAN: + printer->Print(variables_, + "result = 31 * result + ($name$_ ? 1231 : 1237);\n"); + break; + case JAVATYPE_STRING: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + $name$_.hashCode();\n"); + break; + case JAVATYPE_BYTES: + // Accessor style would guarantee $name$_ non-null + printer->Print(variables_, + "result = 31 * result + java.util.Arrays.hashCode($name$_);\n"); + break; + default: + GOOGLE_LOG(ERROR) << "unknown java type for primitive field"; + break; + } +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, const Params& params) + : FieldGenerator(params), descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, params, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer, bool /*unused init_defaults*/) const { + printer->Print(variables_, + "public $type$[] $name$;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateClearCode(io::Printer* printer) const { + printer->Print(variables_, + "$name$ = $default$;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + // First, figure out the length of the array, then parse. + printer->Print(variables_, + "int arrayLength = com.google.protobuf.nano.WireFormatNano\n" + " .getRepeatedFieldArrayLength(input, $non_packed_tag$);\n" + "int i = this.$name$ == null ? 0 : this.$name$.length;\n"); + + if (GetJavaType(descriptor_) == JAVATYPE_BYTES) { + printer->Print(variables_, + "byte[][] newArray = new byte[i + arrayLength][];\n"); + } else { + printer->Print(variables_, + "$type$[] newArray = new $type$[i + arrayLength];\n"); + } + printer->Print(variables_, + "if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + "}\n" + "for (; i < newArray.length - 1; i++) {\n" + " newArray[i] = input.read$capitalized_type$();\n" + " input.readTag();\n" + "}\n" + "// Last one without readTag.\n" + "newArray[i] = input.read$capitalized_type$();\n" + "this.$name$ = newArray;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCodeFromPacked(io::Printer* printer) const { + printer->Print( + "int length = input.readRawVarint32();\n" + "int limit = input.pushLimit(length);\n"); + + // If we know the elements will all be of the same size, the arrayLength + // can be calculated much more easily. However, FixedSize() returns 1 for + // repeated bool fields, which are guaranteed to have the fixed size of + // 1 byte per value only if we control the output. On the wire they can + // legally appear as variable-size integers, so we need to use the slow + // way for repeated bool fields. + if (descriptor_->type() == FieldDescriptor::TYPE_BOOL + || FixedSize(descriptor_->type()) == -1) { + printer->Print(variables_, + "// First pass to compute array length.\n" + "int arrayLength = 0;\n" + "int startPos = input.getPosition();\n" + "while (input.getBytesUntilLimit() > 0) {\n" + " input.read$capitalized_type$();\n" + " arrayLength++;\n" + "}\n" + "input.rewindToPosition(startPos);\n"); + } else { + printer->Print(variables_, + "int arrayLength = length / $fixed_size$;\n"); + } + + printer->Print(variables_, + "int i = this.$name$ == null ? 0 : this.$name$.length;\n" + "$type$[] newArray = new $type$[i + arrayLength];\n" + "if (i != 0) {\n" + " java.lang.System.arraycopy(this.$name$, 0, newArray, 0, i);\n" + "}\n" + "for (; i < newArray.length; i++) {\n" + " newArray[i] = input.read$capitalized_type$();\n" + "}\n" + "this.$name$ = newArray;\n" + "input.popLimit(limit);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateRepeatedDataSizeCode(io::Printer* printer) const { + // Creates a variable dataSize and puts the serialized size in there. + // If the element type is a Java reference type, also generates + // dataCount which stores the number of non-null elements in the field. + if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + "int dataCount = 0;\n" + "int dataSize = 0;\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " dataCount++;\n" + " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$SizeNoTag(element);\n" + " }\n" + "}\n"); + } else if (FixedSize(descriptor_->type()) == -1) { + printer->Print(variables_, + "int dataSize = 0;\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " dataSize += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .compute$capitalized_type$SizeNoTag(element);\n" + "}\n"); + } else { + printer->Print(variables_, + "int dataSize = $fixed_size$ * this.$name$.length;\n"); + } +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n"); + printer->Indent(); + + if (descriptor_->is_packable() && descriptor_->options().packed()) { + GenerateRepeatedDataSizeCode(printer); + printer->Print(variables_, + "output.writeRawVarint32($tag$);\n" + "output.writeRawVarint32(dataSize);\n" + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.write$capitalized_type$NoTag(this.$name$[i]);\n" + "}\n"); + } else if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + "for (int i = 0; i < this.$name$.length; i++) {\n" + " $type$ element = this.$name$[i];\n" + " if (element != null) {\n" + " output.write$capitalized_type$($number$, element);\n" + " }\n" + "}\n"); + } else { + printer->Print(variables_, + "for (int i = 0; i < this.$name$.length; i++) {\n" + " output.write$capitalized_type$($number$, this.$name$[i]);\n" + "}\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (this.$name$ != null && this.$name$.length > 0) {\n"); + printer->Indent(); + + GenerateRepeatedDataSizeCode(printer); + + printer->Print( + "size += dataSize;\n"); + if (descriptor_->is_packable() && descriptor_->options().packed()) { + printer->Print(variables_, + "size += $tag_size$;\n" + "size += com.google.protobuf.nano.CodedOutputByteBufferNano\n" + " .computeRawVarint32Size(dataSize);\n"); + } else if (IsReferenceType(GetJavaType(descriptor_))) { + printer->Print(variables_, + "size += $tag_size$ * dataCount;\n"); + } else { + printer->Print(variables_, + "size += $tag_size$ * this.$name$.length;\n"); + } + + printer->Outdent(); + + printer->Print( + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateEqualsCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!com.google.protobuf.nano.InternalNano.equals(\n" + " this.$name$, other.$name$)) {\n" + " return false;\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateHashCodeCode(io::Printer* printer) const { + printer->Print(variables_, + "result = 31 * result\n" + " + com.google.protobuf.nano.InternalNano.hashCode(this.$name$);\n"); +} + +} // namespace javanano +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/javanano/javanano_primitive_field.h b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h new file mode 100644 index 00000000..c04a19b7 --- /dev/null +++ b/src/google/protobuf/compiler/javanano/javanano_primitive_field.h @@ -0,0 +1,126 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://code.google.com/p/protobuf/ +// +// 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_JAVANANO_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVANANO_PRIMITIVE_FIELD_H__ + +#include <map> +#include <string> +#include <google/protobuf/compiler/javanano/javanano_field.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace javanano { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator( + const FieldDescriptor* descriptor, const Params ¶ms); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + bool SavedDefaultNeeded() const; + void GenerateInitSavedDefaultCode(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + void GenerateSerializationConditional(io::Printer* printer) const; + + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class AccessorPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit AccessorPrimitiveFieldGenerator(const FieldDescriptor* descriptor, + const Params ¶ms, int has_bit_index); + ~AccessorPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + bool SavedDefaultNeeded() const; + void GenerateInitSavedDefaultCode(io::Printer* printer) const; + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(AccessorPrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor, const Params& params); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer, bool lazy_init) const; + void GenerateClearCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateMergingCodeFromPacked(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + void GenerateEqualsCode(io::Printer* printer) const; + void GenerateHashCodeCode(io::Printer* printer) const; + + private: + void GenerateRepeatedDataSizeCode(io::Printer* printer) const; + + const FieldDescriptor* descriptor_; + map<string, string> variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace javanano +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVANANO_PRIMITIVE_FIELD_H__ |