commit f92c545f493dce6e5a2e378e8268c1e7b0e55a4d Author: Jon Skeet Date: Thu Aug 14 20:33:26 2008 +0100 Initial commit of C# code developed before installing Git. committer: Jon Skeet diff --git a/CHANGES.txt b/CHANGES.txt new file mode 100644 index 00000000..853e639a --- /dev/null +++ b/CHANGES.txt @@ -0,0 +1,3 @@ +2008-07-07 version 2.0.0: + + * First public release. diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 00000000..a22cda45 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,49 @@ +This file contains a list of people who have made large contributions +to the public version of Protocol Buffers. + +Original Protocol Buffers design and implementation: + Sanjay Ghemawat + Jeff Dean + Daniel Dulitz + Craig Silverstein + Paul Haahr + Corey Anderson + (and many others) + +Proto2 C++ and Java primary author: + Kenton Varda + +Proto2 Python primary authors: + Will Robinson + Petar Petrov + +Large code contributions: + Joseph Schorr + Wenbo Zhu + +Large quantity of code reviews: + Scott Bruce + Frank Yellin + Neal Norwitz + Jeffrey Yasskin + Ambrose Feinstein + +Documentation: + Lisa Carey + +Maven packaging: + Gregory Kick + +Non-Google patch contributors: + Kevin Ko + * Small patch to handle trailing slashes in --proto_path flag. + Johan Euphrosine + * Small patch to fix Pyhton CallMethod(). + Ulrich Kunitz + * Small optimizations to Python serialization. + Leandro Lucarella + * VI syntax highlighting tweaks. + * Fix compiler to not make output executable. + Dilip Joseph + * Heuristic detection of sub-messages when printing unknown fields in + text format. diff --git a/COPYING.txt b/COPYING.txt new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/COPYING.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/INSTALL.txt b/INSTALL.txt new file mode 100644 index 00000000..ce3b0949 --- /dev/null +++ b/INSTALL.txt @@ -0,0 +1,237 @@ +This file contains detailed but generic information on building and +installing the C++ part of this project. For shorter instructions, +as well as instructions for compiling and installing the Java or +Python parts, see README. + +====================================================================== + +Copyright 1994, 1995, 1996, 1999, 2000, 2001, 2002 Free Software +Foundation, Inc. + + This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + + +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the `--target=TYPE' option to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +will cause the specified gcc to be used as the C compiler (unless it is +overridden in the site shell script). + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..4e2f6515 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,117 @@ +## Process this file with automake to produce Makefile.in + +ACLOCAL_AMFLAGS = -I m4 + +SUBDIRS = src + +EXTRA_DIST = \ + autogen.sh \ + generate_descriptor_proto.sh \ + README.txt \ + INSTALL.txt \ + COPYING.txt \ + CONTRIBUTORS.txt \ + CHANGES.txt \ + editors/README.txt \ + editors/proto.vim \ + vsprojects/config.h \ + vsprojects/extract_includes.bat \ + vsprojects/libprotobuf.vcproj \ + vsprojects/libprotoc.vcproj \ + vsprojects/protobuf.sln \ + vsprojects/protoc.vcproj \ + vsprojects/readme.txt \ + vsprojects/tests.vcproj \ + vsprojects/convert2008to2005.sh \ + examples/README.txt \ + examples/Makefile \ + examples/addressbook.proto \ + examples/add_person.cc \ + examples/list_people.cc \ + examples/AddPerson.java \ + examples/ListPeople.java \ + examples/add_person.py \ + examples/list_people.py \ + java/src/main/java/com/google/protobuf/AbstractMessage.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/CodedOutputStream.java \ + java/src/main/java/com/google/protobuf/Descriptors.java \ + java/src/main/java/com/google/protobuf/DynamicMessage.java \ + java/src/main/java/com/google/protobuf/ExtensionRegistry.java \ + java/src/main/java/com/google/protobuf/FieldSet.java \ + java/src/main/java/com/google/protobuf/GeneratedMessage.java \ + java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java \ + java/src/main/java/com/google/protobuf/Message.java \ + java/src/main/java/com/google/protobuf/RpcCallback.java \ + java/src/main/java/com/google/protobuf/RpcChannel.java \ + java/src/main/java/com/google/protobuf/RpcController.java \ + java/src/main/java/com/google/protobuf/RpcUtil.java \ + java/src/main/java/com/google/protobuf/Service.java \ + java/src/main/java/com/google/protobuf/TextFormat.java \ + java/src/main/java/com/google/protobuf/UninitializedMessageException.java \ + java/src/main/java/com/google/protobuf/UnknownFieldSet.java \ + java/src/main/java/com/google/protobuf/WireFormat.java \ + java/src/test/java/com/google/protobuf/AbstractMessageTest.java \ + java/src/test/java/com/google/protobuf/CodedInputStreamTest.java \ + java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java \ + java/src/test/java/com/google/protobuf/DescriptorsTest.java \ + java/src/test/java/com/google/protobuf/DynamicMessageTest.java \ + java/src/test/java/com/google/protobuf/GeneratedMessageTest.java \ + java/src/test/java/com/google/protobuf/MessageTest.java \ + java/src/test/java/com/google/protobuf/ServiceTest.java \ + java/src/test/java/com/google/protobuf/TestUtil.java \ + java/src/test/java/com/google/protobuf/TextFormatTest.java \ + java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \ + java/src/test/java/com/google/protobuf/WireFormatTest.java \ + java/src/test/java/com/google/protobuf/multiple_files_test.proto \ + java/pom.xml \ + java/README.txt \ + python/google/protobuf/internal/generator_test.py \ + python/google/protobuf/internal/decoder.py \ + python/google/protobuf/internal/decoder_test.py \ + python/google/protobuf/internal/descriptor_test.py \ + python/google/protobuf/internal/encoder.py \ + python/google/protobuf/internal/encoder_test.py \ + python/google/protobuf/internal/input_stream.py \ + python/google/protobuf/internal/input_stream_test.py \ + python/google/protobuf/internal/message_listener.py \ + python/google/protobuf/internal/more_extensions.proto \ + python/google/protobuf/internal/more_messages.proto \ + python/google/protobuf/internal/output_stream.py \ + python/google/protobuf/internal/output_stream_test.py \ + python/google/protobuf/internal/reflection_test.py \ + python/google/protobuf/internal/service_reflection_test.py \ + python/google/protobuf/internal/test_util.py \ + python/google/protobuf/internal/text_format_test.py \ + python/google/protobuf/internal/wire_format.py \ + python/google/protobuf/internal/wire_format_test.py \ + python/google/protobuf/internal/__init__.py \ + python/google/protobuf/descriptor.py \ + python/google/protobuf/message.py \ + python/google/protobuf/reflection.py \ + python/google/protobuf/service.py \ + python/google/protobuf/service_reflection.py \ + python/google/protobuf/text_format.py \ + python/google/protobuf/__init__.py \ + python/google/__init__.py \ + python/ez_setup.py \ + python/setup.py \ + python/mox.py \ + python/stubout.py \ + python/README.txt + +# Deletes all the files generated by autogen.sh. +MAINTAINERCLEANFILES = \ + aclocal.m4 \ + config.guess \ + config.sub \ + configure \ + depcomp \ + install-sh \ + ltmain.sh \ + Makefile.in \ + missing \ + mkinstalldirs \ + config.h.in \ + stamp.h.in diff --git a/README.txt b/README.txt new file mode 100644 index 00000000..71780367 --- /dev/null +++ b/README.txt @@ -0,0 +1,77 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. +http://code.google.com/apis/protocolbuffers/ + +BETA WARNING +============ + +This package is a beta. This means that API may change in an +incompatible way in the future. It's unlikely that any big changes +will be made, but we can make no promises. Expect a non-beta release +sometime in August 2008. + +C++ Installation - Unix +======================= + +To build and install the C++ Protocol Buffer runtime and the Protocol +Buffer compiler (protoc) execute the following: + + $ ./configure + $ make + $ make check + $ make install + +If "make check" fails, you can still install, but it is likely that +some features of this library will not work correctly on your system. +Proceed at your own risk. + +"make install" may require superuser privileges. + +For advanced usage information on configure and make, see INSTALL.txt. + +** Hint on insall location ** + + By default, the package will be installed to /usr/local. However, + on many platforms, /usr/local/lib is not part of LD_LIBRARY_PATH. + You can add it, but it may be easier to just install to /usr + instead. To do this, invoke configure as follows: + + ./configure --prefix=/usr + + If you already built the package with a different prefix, make sure + to run "make clean" before building again. + +** Note for Solaris users ** + + Solaris 10 x86 has a bug that will make linking fail, complaining + about libstdc++.la being invalid. We have included a work-around + in this package. To use the work-around, run configure as follows: + + ./configure LDFLAGS=-L$PWD/src/solaris + + See src/solaris/libstdc++.la for more info on this bug. + +C++ Installation - Windows +========================== + +If you are using Micosoft Visual C++, see vsprojects/readme.txt. + +If you are using Cygwin or MinGW, follow the Unix installation +instructions, above. + +Java and Python Installation +============================ + +The Java and Python runtime libraries for Protocol Buffers are located +in the java and python directories. See the README file in each +directory for more information on how to compile and install them. +Note that both of them require you to first install the Protocol +Buffer compiler (protoc), which is part of the C++ package. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 00000000..34a4d2e3 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Run this script to generate the configure script and other files that will +# be included in the distribution. These files are not checked in because they +# are automatically generated. + +# Check that we're being run from the right directory. +if test ! -e src/google/protobuf/stubs/common.h; then + cat >&2 << __EOF__ +Could not find source code. Make sure you are running this script from the +root of the distribution tree. +__EOF__ + exit 1 +fi + +set -ex + +rm -rf autom4te.cache + +aclocal-1.9 --force -I m4 +libtoolize -c -f +autoheader -f -W all +automake-1.9 --foreign -a -c -f -W all +autoconf -f -W all,no-obsolete + +rm -rf autom4te.cache config.h.in~ +exit 0 diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..6dca1633 --- /dev/null +++ b/configure.ac @@ -0,0 +1,34 @@ +## Process this file with autoconf to produce configure. +## In general, the safest way to proceed is to run ./autogen.sh + +AC_PREREQ(2.59) + +# Note: If you change the version, you must also update it in: +# * java/pom.xml +# * python/setup.py +# * src/google/protobuf/stubs/common.h +AC_INIT(protobuf, 2.0.1-SNAPSHOT, protobuf@googlegroups.com) + +AC_CONFIG_SRCDIR(src/google/protobuf/message.cc) +AM_CONFIG_HEADER(config.h) +AM_INIT_AUTOMAKE + +# Checks for programs. +AC_PROG_CC +AC_PROG_CXX +AC_PROG_LIBTOOL +AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([fcntl.h inttypes.h limits.h stdlib.h unistd.h]) + +# Checks for library functions. +AC_FUNC_MEMCMP +AC_FUNC_STRTOD +AC_CHECK_FUNCS([ftruncate memset mkdir strchr strerror strtol]) + +ACX_PTHREAD +AC_CXX_STL_HASH + +AC_OUTPUT( Makefile src/Makefile ) diff --git a/editors/README.txt b/editors/README.txt new file mode 100644 index 00000000..3e9fc79e --- /dev/null +++ b/editors/README.txt @@ -0,0 +1,5 @@ +This directory contains syntax highlighting and configuration files for editors +to properly display Protocol Buffer files. + +See each file's header comment for directions on how to use it with the +appropriate editor. diff --git a/editors/proto.vim b/editors/proto.vim new file mode 100644 index 00000000..19c0d4c5 --- /dev/null +++ b/editors/proto.vim @@ -0,0 +1,91 @@ +" Protocol Buffers - Google's data interchange format +" Copyright 2008 Google Inc. +" +" 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. + +" This is the Vim syntax file for Google Protocol Buffers. +" +" Usage: +" +" 1. cp proto.vim ~/.vim/syntax/ +" 2. Add the following to ~/.vimrc: +" +" augroup filetype +" au! BufRead,BufNewFile *.proto setfiletype proto +" augroup end +" +" Or just create a new file called ~/.vim/ftdetect/proto.vim with the +" previous lines on it. + +if version < 600 + syntax clear +elseif exists("b:current_syntax") + finish +endif + +syn case match + +syn keyword pbTodo contained TODO FIXME XXX +syn cluster pbCommentGrp contains=pbTodo + +syn keyword pbSyntax syntax import option +syn keyword pbStructure package message group +syn keyword pbRepeat optional required repeated +syn keyword pbDefault default +syn keyword pbExtend extend extensions to max +syn keyword pbRPC service rpc returns + +syn keyword pbType int32 int64 uint32 uint64 sint32 sint64 +syn keyword pbType fixed32 fixed64 sfixed32 sfixed64 +syn keyword pbType float double bool string bytes +syn keyword pbTypedef enum +syn keyword pbBool true false + +syn match pbInt /-\?\<\d\+\>/ +syn match pbInt /\<0[xX]\x+\>/ +syn match pbFloat /\<-\?\d*\(\.\d*\)\?/ +" TODO: .proto also supports C-style block comments; +" see /usr/share/vim/vim70/syntax/c.vim for how it's done. +syn region pbComment start="//" skip="\\$" end="$" keepend contains=@pbCommentGrp +syn region pbString start=/"/ skip=/\\"/ end=/"/ +syn region pbString start=/'/ skip=/\\'/ end=/'/ + +if version >= 508 || !exists("did_proto_syn_inits") + if version < 508 + let did_proto_syn_inits = 1 + command -nargs=+ HiLink hi link + else + command -nargs=+ HiLink hi def link + endif + + HiLink pbTodo Todo + + HiLink pbSyntax Include + HiLink pbStructure Structure + HiLink pbRepeat Repeat + HiLink pbDefault Keyword + HiLink pbExtend Keyword + HiLink pbRPC Keyword + HiLink pbType Type + HiLink pbTypedef Typedef + HiLink pbBool Boolean + + HiLink pbInt Number + HiLink pbFloat Float + HiLink pbComment Comment + HiLink pbString String + + delcommand HiLink +endif + +let b:current_syntax = "proto" diff --git a/examples/AddPerson.java b/examples/AddPerson.java new file mode 100644 index 00000000..4917684b --- /dev/null +++ b/examples/AddPerson.java @@ -0,0 +1,89 @@ +// See README.txt for information and build instructions. + +import com.example.tutorial.AddressBookProtos.AddressBook; +import com.example.tutorial.AddressBookProtos.Person; +import java.io.BufferedReader; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.PrintStream; + +class AddPerson { + // This function fills in a Person message based on user input. + static Person PromptForAddress(BufferedReader stdin, + PrintStream stdout) throws IOException { + Person.Builder person = Person.newBuilder(); + + stdout.print("Enter person ID: "); + person.setId(Integer.valueOf(stdin.readLine())); + + stdout.print("Enter name: "); + person.setName(stdin.readLine()); + + stdout.print("Enter email address (blank for none): "); + String email = stdin.readLine(); + if (email.length() > 0) { + person.setEmail(email); + } + + while (true) { + stdout.print("Enter a phone number (or leave blank to finish): "); + String number = stdin.readLine(); + if (number.length() == 0) { + break; + } + + Person.PhoneNumber.Builder phoneNumber = + Person.PhoneNumber.newBuilder().setNumber(number); + + stdout.print("Is this a mobile, home, or work phone? "); + String type = stdin.readLine(); + if (type.equals("mobile")) { + phoneNumber.setType(Person.PhoneType.MOBILE); + } else if (type.equals("home")) { + phoneNumber.setType(Person.PhoneType.HOME); + } else if (type.equals("work")) { + phoneNumber.setType(Person.PhoneType.WORK); + } else { + stdout.println("Unknown phone type. Using default."); + } + + person.addPhone(phoneNumber); + } + + return person.build(); + } + + // Main function: Reads the entire address book from a file, + // adds one person based on user input, then writes it back out to the same + // file. + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: AddPerson ADDRESS_BOOK_FILE"); + System.exit(-1); + } + + AddressBook.Builder addressBook = AddressBook.newBuilder(); + + // Read the existing address book. + try { + FileInputStream input = new FileInputStream(args[0]); + addressBook.mergeFrom(input); + input.close(); + } catch (FileNotFoundException e) { + System.out.println(args[0] + ": File not found. Creating a new file."); + } + + // Add an address. + addressBook.addPerson( + PromptForAddress(new BufferedReader(new InputStreamReader(System.in)), + System.out)); + + // Write the new address book back to disk. + FileOutputStream output = new FileOutputStream(args[0]); + addressBook.build().writeTo(output); + output.close(); + } +} diff --git a/examples/ListPeople.java b/examples/ListPeople.java new file mode 100644 index 00000000..b2f153af --- /dev/null +++ b/examples/ListPeople.java @@ -0,0 +1,50 @@ +// See README.txt for information and build instructions. + +import com.example.tutorial.AddressBookProtos.AddressBook; +import com.example.tutorial.AddressBookProtos.Person; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintStream; + +class ListPeople { + // Iterates though all people in the AddressBook and prints info about them. + static void Print(AddressBook addressBook) { + for (Person person: addressBook.getPersonList()) { + System.out.println("Person ID: " + person.getId()); + System.out.println(" Name: " + person.getName()); + if (person.hasEmail()) { + System.out.println(" E-mail address: " + person.getEmail()); + } + + for (Person.PhoneNumber phoneNumber : person.getPhoneList()) { + switch (phoneNumber.getType()) { + case MOBILE: + System.out.print(" Mobile phone #: "); + break; + case HOME: + System.out.print(" Home phone #: "); + break; + case WORK: + System.out.print(" Work phone #: "); + break; + } + System.out.println(phoneNumber.getNumber()); + } + } + } + + // Main function: Reads the entire address book from a file and prints all + // the information inside. + public static void main(String[] args) throws Exception { + if (args.length != 1) { + System.err.println("Usage: ListPeople ADDRESS_BOOK_FILE"); + System.exit(-1); + } + + // Read the existing address book. + AddressBook addressBook = + AddressBook.parseFrom(new FileInputStream(args[0])); + + Print(addressBook); + } +} diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 00000000..999ee94a --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,56 @@ +# See README.txt. + +.PHONY: all cpp java python clean + +all: cpp java python + +cpp: add_person_cpp list_people_cpp +java: add_person_java list_people_java +python: add_person_python list_people_python + +clean: + rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python + rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/*.class + rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/AddressBookProtos.java + rm -f *.pyc + rmdir com/example/tutorial 2>/dev/null || true + rmdir com/example 2>/dev/null || true + rmdir com 2>/dev/null || true + +protoc_middleman: addressbook.proto + protoc --cpp_out=. --java_out=. --python_out=. addressbook.proto + @touch protoc_middleman + +add_person_cpp: add_person.cc protoc_middleman + c++ add_person.cc addressbook.pb.cc -lprotobuf -o add_person_cpp + +list_people_cpp: list_people.cc protoc_middleman + c++ list_people.cc addressbook.pb.cc -lprotobuf -o list_people_cpp + +javac_middleman: AddPerson.java ListPeople.java protoc_middleman + javac AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java + @touch javac_middleman + +add_person_java: javac_middleman + @echo "Writing shortcut script add_person_java..." + @echo '#! /bin/sh' > add_person_java + @echo 'java -classpath .:$$CLASSPATH AddPerson "$$@"' >> add_person_java + @chmod +x add_person_java + +list_people_java: javac_middleman + @echo "Writing shortcut script list_people_java..." + @echo '#! /bin/sh' > list_people_java + @echo 'java -classpath .:$$CLASSPATH ListPeople "$$@"' >> list_people_java + @chmod +x list_people_java + +add_person_python: add_person.py protoc_middleman + @echo "Writing shortcut script add_person_python..." + @echo '#! /bin/sh' > add_person_python + @echo './add_person.py "$$@"' >> add_person_python + @chmod +x add_person_python + +list_people_python: list_people.py protoc_middleman + @echo "Writing shortcut script list_people_python..." + @echo '#! /bin/sh' > list_people_python + @echo './list_people.py "$$@"' >> list_people_python + @chmod +x list_people_python diff --git a/examples/README.txt b/examples/README.txt new file mode 100644 index 00000000..b0d1a0c2 --- /dev/null +++ b/examples/README.txt @@ -0,0 +1,25 @@ +This directory contains example code that uses Protocol Buffers to manage an +address book. Two programs are provided, each with three different +implementations, one written in each of C++, Java, and Python. The add_person +example adds a new person to an address book, prompting the user to input +the person's information. The list_people example lists people already in the +address book. The examples use the exact same format in all three languages, +so you can, for example, use add_person_java to create an address book and then +use list_people_python to read it. + +You must install the protobuf package before you can build these. + +To build all the examples (on a unix-like system), simply run "make". This +creates the following executable files in the current directory: + add_person_cpp list_people_cpp + add_person_java list_people_java + add_person_python list_people_python + +If you only want to compile examples in one language, use "make cpp", +"make java", or "make python". + +All of these programs simply take an address book file as their parameter. +The add_person programs will create the file if it doesn't already exist. + +These examples are part of the Protocol Buffers tutorial, located at: + http://code.google.com/apis/protocolbuffers/docs/tutorials.html diff --git a/examples/add_person.cc b/examples/add_person.cc new file mode 100644 index 00000000..2aca216b --- /dev/null +++ b/examples/add_person.cc @@ -0,0 +1,92 @@ +// See README.txt for information and build instructions. + +#include +#include +#include +#include "addressbook.pb.h" +using namespace std; + +// This function fills in a Person message based on user input. +void PromptForAddress(tutorial::Person* person) { + cout << "Enter person ID number: "; + int id; + cin >> id; + person->set_id(id); + cin.ignore(256, '\n'); + + cout << "Enter name: "; + getline(cin, *person->mutable_name()); + + cout << "Enter email address (blank for none): "; + string email; + getline(cin, email); + if (!email.empty()) { + person->set_email(email); + } + + while (true) { + cout << "Enter a phone number (or leave blank to finish): "; + string number; + getline(cin, number); + if (number.empty()) { + break; + } + + tutorial::Person::PhoneNumber* phone_number = person->add_phone(); + phone_number->set_number(number); + + cout << "Is this a mobile, home, or work phone? "; + string type; + getline(cin, type); + if (type == "mobile") { + phone_number->set_type(tutorial::Person::MOBILE); + } else if (type == "home") { + phone_number->set_type(tutorial::Person::HOME); + } else if (type == "work") { + phone_number->set_type(tutorial::Person::WORK); + } else { + cout << "Unknown phone type. Using default." << endl; + } + } +} + +// Main function: Reads the entire address book from a file, +// adds one person based on user input, then writes it back out to the same +// file. +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; + return -1; + } + + tutorial::AddressBook address_book; + + { + // Read the existing address book. + fstream input(argv[1], ios::in | ios::binary); + if (!input) { + cout << argv[1] << ": File not found. Creating a new file." << endl; + } else if (!address_book.ParseFromIstream(&input)) { + cerr << "Failed to parse address book." << endl; + return -1; + } + } + + // Add an address. + PromptForAddress(address_book.add_person()); + + { + // Write the new address book back to disk. + fstream output(argv[1], ios::out | ios::trunc | ios::binary); + if (!address_book.SerializeToOstream(&output)) { + cerr << "Failed to write address book." << endl; + return -1; + } + } + + return 0; +} diff --git a/examples/add_person.py b/examples/add_person.py new file mode 100755 index 00000000..78e56966 --- /dev/null +++ b/examples/add_person.py @@ -0,0 +1,58 @@ +#! /usr/bin/python + +# See README.txt for information and build instructions. + +import addressbook_pb2 +import sys + +# This function fills in a Person message based on user input. +def PromptForAddress(person): + person.id = int(raw_input("Enter person ID number: ")) + person.name = raw_input("Enter name: ") + + email = raw_input("Enter email address (blank for none): ") + if email != "": + person.email = email + + while True: + number = raw_input("Enter a phone number (or leave blank to finish): ") + if number == "": + break + + phone_number = person.phone.add() + phone_number.number = number + + type = raw_input("Is this a mobile, home, or work phone? ") + if type == "mobile": + phone_number.type = addressbook_pb2.Person.MOBILE + elif type == "home": + phone_number.type = addressbook_pb2.Person.HOME + elif type == "work": + phone_number.type = addressbook_pb2.Person.WORK + else: + print "Unknown phone type; leaving as default value." + +# Main procedure: Reads the entire address book from a file, +# adds one person based on user input, then writes it back out to the same +# file. +if len(sys.argv) != 2: + print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" + sys.exit(-1) + +address_book = addressbook_pb2.AddressBook() + +# Read the existing address book. +try: + f = open(sys.argv[1], "rb") + address_book.ParseFromString(f.read()) + f.close() +except IOError: + print sys.argv[1] + ": File not found. Creating a new file." + +# Add an address. +PromptForAddress(address_book.person.add()) + +# Write the new address book back to disk. +f = open(sys.argv[1], "wb") +f.write(address_book.SerializeToString()) +f.close() diff --git a/examples/addressbook.proto b/examples/addressbook.proto new file mode 100644 index 00000000..b14829e9 --- /dev/null +++ b/examples/addressbook.proto @@ -0,0 +1,30 @@ +// See README.txt for information and build instructions. + +package tutorial; + +option java_package = "com.example.tutorial"; +option java_outer_classname = "AddressBookProtos"; + +message Person { + required string name = 1; + required int32 id = 2; // Unique ID number for this person. + optional string email = 3; + + enum PhoneType { + MOBILE = 0; + HOME = 1; + WORK = 2; + } + + message PhoneNumber { + required string number = 1; + optional PhoneType type = 2 [default = HOME]; + } + + repeated PhoneNumber phone = 4; +} + +// Our address book file is just one of these. +message AddressBook { + repeated Person person = 1; +} diff --git a/examples/list_people.cc b/examples/list_people.cc new file mode 100644 index 00000000..bd9a583f --- /dev/null +++ b/examples/list_people.cc @@ -0,0 +1,65 @@ +// See README.txt for information and build instructions. + +#include +#include +#include +#include "addressbook.pb.h" +using namespace std; + +// Iterates though all people in the AddressBook and prints info about them. +void ListPeople(const tutorial::AddressBook& address_book) { + for (int i = 0; i < address_book.person_size(); i++) { + const tutorial::Person& person = address_book.person(i); + + cout << "Person ID: " << person.id() << endl; + cout << " Name: " << person.name() << endl; + if (person.has_email()) { + cout << " E-mail address: " << person.email() << endl; + } + + for (int j = 0; j < person.phone_size(); j++) { + const tutorial::Person::PhoneNumber& phone_number = person.phone(j); + + switch (phone_number.type()) { + case tutorial::Person::MOBILE: + cout << " Mobile phone #: "; + break; + case tutorial::Person::HOME: + cout << " Home phone #: "; + break; + case tutorial::Person::WORK: + cout << " Work phone #: "; + break; + } + cout << phone_number.number() << endl; + } + } +} + +// Main function: Reads the entire address book from a file and prints all +// the information inside. +int main(int argc, char* argv[]) { + // Verify that the version of the library that we linked against is + // compatible with the version of the headers we compiled against. + GOOGLE_PROTOBUF_VERIFY_VERSION; + + if (argc != 2) { + cerr << "Usage: " << argv[0] << " ADDRESS_BOOK_FILE" << endl; + return -1; + } + + tutorial::AddressBook address_book; + + { + // Read the existing address book. + fstream input(argv[1], ios::in | ios::binary); + if (!address_book.ParseFromIstream(&input)) { + cerr << "Failed to parse address book." << endl; + return -1; + } + } + + ListPeople(address_book); + + return 0; +} diff --git a/examples/list_people.py b/examples/list_people.py new file mode 100755 index 00000000..76f4bf1e --- /dev/null +++ b/examples/list_people.py @@ -0,0 +1,38 @@ +#! /usr/bin/python + +# See README.txt for information and build instructions. + +import addressbook_pb2 +import sys + +# Iterates though all people in the AddressBook and prints info about them. +def ListPeople(address_book): + for person in address_book.person: + print "Person ID:", person.id + print " Name:", person.name + if person.HasField('email'): + print " E-mail address:", person.email + + for phone_number in person.phone: + if phone_number.type == addressbook_pb2.Person.MOBILE: + print " Mobile phone #: ", + elif phone_number.type == addressbook_pb2.Person.HOME: + print " Home phone #: ", + elif phone_number.type == addressbook_pb2.Person.WORK: + print " Work phone #: ", + print phone_number.number + +# Main procedure: Reads the entire address book from a file and prints all +# the information inside. +if len(sys.argv) != 2: + print "Usage:", sys.argv[0], "ADDRESS_BOOK_FILE" + sys.exit(-1) + +address_book = addressbook_pb2.AddressBook() + +# Read the existing address book. +f = open(sys.argv[1], "rb") +address_book.ParseFromString(f.read()) +f.close() + +ListPeople(address_book) diff --git a/generate_descriptor_proto.sh b/generate_descriptor_proto.sh new file mode 100755 index 00000000..5587db0c --- /dev/null +++ b/generate_descriptor_proto.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Run this script to regenerate descriptor.pb.{h,cc} after the protocol +# compiler changes. Since these files are compiled into the protocol compiler +# itself, they cannot be generated automatically by a make rule. "make check" +# will fail if these files do not match what the protocol compiler would +# generate. + +if test ! -e src/google/protobuf/stubs/common.h; then + cat >&2 << __EOF__ +Could not find source code. Make sure you are running this script from the +root of the distribution tree. +__EOF__ + exit 1 +fi + +if test ! -e src/Makefile; then + cat >&2 << __EOF__ +Could not find src/Makefile. You must run ./configure (and perhaps +./autogen.sh) first. +__EOF__ + exit 1 +fi + +pushd src +make protoc && ./protoc --cpp_out=dllexport_decl=LIBPROTOBUF_EXPORT:. google/protobuf/descriptor.proto +popd diff --git a/java/README.txt b/java/README.txt new file mode 100644 index 00000000..050bbfe7 --- /dev/null +++ b/java/README.txt @@ -0,0 +1,74 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. + +This directory contains the Java Protocol Buffers runtime library. + +Installation - With Maven +========================= + +The Protocol Buffers build is managed using Maven. If you would +rather build without Maven, see the next section. + +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. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/java/pom.xml b/java/pom.xml new file mode 100644 index 00000000..1bb26b2e --- /dev/null +++ b/java/pom.xml @@ -0,0 +1,125 @@ + + + 4.0.0 + + + com.google.protobuf + protobuf-java + 2.0.1-SNAPSHOT + jar + Protocol Buffer Java API + + Protocol Buffers are a way of encoding structured data in an efficient yet + extensible format. + + 2008 + http://code.google.com/p/protobuf + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + http://code.google.com/p/protobuf/source/browse + + scm:svn:http://protobuf.googlecode.com/svn/trunk/ + + + + + junit + junit + 4.4 + test + + + org.easymock + easymock + 2.2 + test + + + org.easymock + easymockclassextension + 2.2.1 + test + + + + + + maven-compiler-plugin + + 1.5 + 1.5 + + + + maven-surefire-plugin + + + **/*Test.java + + + + + maven-antrun-plugin + + + generate-sources + generate-sources + + + + + + + + + + target/generated-sources + + + run + + + + generate-test-sources + generate-test-sources + + + + + + + + + + + + + + + target/generated-test-sources + + + run + + + + + + + diff --git a/java/src/main/java/com/google/protobuf/AbstractMessage.java b/java/src/main/java/com/google/protobuf/AbstractMessage.java new file mode 100644 index 00000000..f4145190 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/AbstractMessage.java @@ -0,0 +1,343 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Map; + +/** + * A partial implementation of the {@link Message} interface which implements + * as many methods of that interface as possible in terms of other methods. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class AbstractMessage implements Message { + @SuppressWarnings("unchecked") + public boolean isInitialized() { + // Check that all required fields are present. + for (FieldDescriptor field : getDescriptorForType().getFields()) { + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + } + + // Check that embedded messages are initialized. + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + for (Message element : (List) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (!((Message) entry.getValue()).isInitialized()) { + return false; + } + } + } + } + + return true; + } + + public final String toString() { + return TextFormat.printToString(this); + } + + public void writeTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List) entry.getValue()) { + output.writeField(field.getType(), field.getNumber(), element); + } + } else { + output.writeField(field.getType(), field.getNumber(), entry.getValue()); + } + } + + UnknownFieldSet unknownFields = getUnknownFields(); + if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + unknownFields.writeAsMessageSetTo(output); + } else { + unknownFields.writeTo(output); + } + } + + public ByteString toByteString() { + try { + ByteString.CodedBuilder out = + ByteString.newCodedBuilder(getSerializedSize()); + writeTo(out.getCodedOutput()); + return out.build(); + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public byte[] toByteArray() { + try { + byte[] result = new byte[getSerializedSize()]; + CodedOutputStream output = CodedOutputStream.newInstance(result); + writeTo(output); + output.checkNoSpaceLeft(); + return result; + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + public void writeTo(OutputStream output) throws IOException { + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + private int memoizedSize = -1; + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + for (Map.Entry entry : getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List) entry.getValue()) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } + } else { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), entry.getValue()); + } + } + + UnknownFieldSet unknownFields = getUnknownFields(); + if (getDescriptorForType().getOptions().getMessageSetWireFormat()) { + size += unknownFields.getSerializedSizeAsMessageSet(); + } else { + size += unknownFields.getSerializedSize(); + } + + memoizedSize = size; + return size; + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof Message)) { + return false; + } + Message otherMessage = (Message) other; + if (getDescriptorForType() != otherMessage.getDescriptorForType()) { + return false; + } + return getAllFields().equals(otherMessage.getAllFields()); + } + + @Override + public int hashCode() { + int hash = 41; + hash = (19 * hash) + getDescriptorForType().hashCode(); + hash = (53 * hash) + getAllFields().hashCode(); + return hash; + } + + // ================================================================= + + /** + * A partial implementation of the {@link Message.Builder} interface which + * implements as many methods of that interface as possible in terms of + * other methods. + */ + @SuppressWarnings("unchecked") + public static abstract class Builder + implements Message.Builder { + // The compiler produces an error if this is not declared explicitly. + public abstract BuilderType clone(); + + public BuilderType clear() { + for (Map.Entry entry : + getAllFields().entrySet()) { + clearField(entry.getKey()); + } + return (BuilderType) this; + } + + public BuilderType mergeFrom(Message other) { + if (other.getDescriptorForType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "mergeFrom(Message) can only merge messages of the same type."); + } + + // Note: We don't attempt to verify that other's fields have valid + // types. Doing so would be a losing battle. We'd have to verify + // all sub-messages as well, and we'd have to make copies of all of + // them to insure that they don't change after verification (since + // the Message interface itself cannot enforce immutability of + // implementations). + // TODO(kenton): Provide a function somewhere called makeDeepCopy() + // which allows people to make secure deep copies of messages. + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + for (Object element : (List)entry.getValue()) { + addRepeatedField(field, element); + } + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)getField(field); + if (existingValue == existingValue.getDefaultInstanceForType()) { + setField(field, entry.getValue()); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)entry.getValue()) + .build()); + } + } else { + setField(field, entry.getValue()); + } + } + + return (BuilderType) this; + } + + public BuilderType mergeFrom(CodedInputStream input) throws IOException { + return mergeFrom(input, ExtensionRegistry.getEmptyRegistry()); + } + + public BuilderType mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + UnknownFieldSet.Builder unknownFields = + UnknownFieldSet.newBuilder(getUnknownFields()); + FieldSet.mergeFrom(input, unknownFields, extensionRegistry, this); + setUnknownFields(unknownFields.build()); + return (BuilderType) this; + } + + public BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + setUnknownFields( + UnknownFieldSet.newBuilder(getUnknownFields()) + .mergeFrom(unknownFields) + .build()); + return (BuilderType) this; + } + + public BuilderType mergeFrom(ByteString data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(byte[] data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom( + byte[] data, ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input, extensionRegistry); + input.checkLastTagWas(0); + return (BuilderType) this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + public BuilderType mergeFrom(InputStream input) throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + + public BuilderType mergeFrom(InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput, extensionRegistry); + codedInput.checkLastTagWas(0); + return (BuilderType) this; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ByteString.java b/java/src/main/java/com/google/protobuf/ByteString.java new file mode 100644 index 00000000..4da03eca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ByteString.java @@ -0,0 +1,318 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterOutputStream; +import java.io.UnsupportedEncodingException; + +/** + * Immutable array of bytes. + * + * @author crazybob@google.com Bob Lee + * @author kenton@google.com Kenton Varda + */ +public final class ByteString { + private final byte[] bytes; + + private ByteString(byte[] bytes) { + this.bytes = bytes; + } + + /** + * Gets the byte at the given index. + * + * @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size + */ + public byte byteAt(int index) { + return bytes[index]; + } + + /** + * Gets the number of bytes. + */ + public int size() { + return this.bytes.length; + } + + /** + * Returns {@code true} if the size is {@code 0}, {@code false} otherwise. + */ + public boolean isEmpty() { + return this.bytes.length == 0; + } + + // ================================================================= + // byte[] -> ByteString + + /** + * Empty ByteString. + */ + public static final ByteString EMPTY = new ByteString(new byte[0]); + + /** + * Copies the given bytes into a {@code ByteString}. + */ + public static ByteString copyFrom(byte[] bytes, int offset, int size) { + byte[] copy = new byte[size]; + System.arraycopy(bytes, offset, copy, 0, size); + return new ByteString(copy); + } + + /** + * Copies the given bytes into a {@code ByteString}. + */ + public static ByteString copyFrom(byte[] bytes) { + return copyFrom(bytes, 0, bytes.length); + } + + /** + * Encodes {@code text} into a sequence of bytes using the named charset + * and returns the result as a {@code ByteString}. + */ + public static ByteString copyFrom(String text, String charsetName) + throws UnsupportedEncodingException { + return new ByteString(text.getBytes(charsetName)); + } + + /** + * Encodes {@code text} into a sequence of UTF-8 bytes and returns the + * result as a {@code ByteString}. + */ + public static ByteString copyFromUtf8(String text) { + try { + return new ByteString(text.getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported?", e); + } + } + + // ================================================================= + // ByteString -> byte[] + + /** + * Copies bytes into a buffer at the given offset. + * + * @param target buffer to copy into + * @param offset in the target buffer + */ + public void copyTo(byte[] target, int offset) { + System.arraycopy(bytes, 0, target, offset, bytes.length); + } + + /** + * Copies bytes into a buffer. + * + * @param target buffer to copy into + * @param sourceOffset offset within these bytes + * @param targetOffset offset within the target buffer + * @param size number of bytes to copy + */ + public void copyTo(byte[] target, int sourceOffset, int targetOffset, + int size) { + System.arraycopy(bytes, sourceOffset, target, targetOffset, size); + } + + /** + * Copies bytes to a {@code byte[]}. + */ + public byte[] toByteArray() { + int size = this.bytes.length; + byte[] copy = new byte[size]; + System.arraycopy(this.bytes, 0, copy, 0, size); + return copy; + } + + /** + * Constructs a new {@code String} by decoding the bytes using the + * specified charset. + */ + public String toString(String charsetName) + throws UnsupportedEncodingException { + return new String(this.bytes, charsetName); + } + + /** + * Constructs a new {@code String} by decoding the bytes as UTF-8. + */ + public String toStringUtf8() { + try { + return new String(this.bytes, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported?", e); + } + } + + // ================================================================= + // equals() and hashCode() + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + + if (!(o instanceof ByteString)) { + return false; + } + + ByteString other = (ByteString) o; + int size = this.bytes.length; + if (size != other.bytes.length) { + return false; + } + + byte[] bytes = this.bytes; + byte[] otherBytes = other.bytes; + for (int i = 0; i < size; i++) { + if (bytes[i] != otherBytes[i]) { + return false; + } + } + + return true; + } + + volatile int hash = 0; + + @Override + public int hashCode() { + int h = this.hash; + + if (h == 0) { + byte[] bytes = this.bytes; + int size = this.bytes.length; + + h = size; + for (int i = 0; i < size; i++) { + h = h * 31 + bytes[i]; + } + if (h == 0) { + h = 1; + } + + this.hash = h; + } + + return h; + } + + // ================================================================= + // Input stream + + /** + * Creates an {@code InputStream} which can be used to read the bytes. + */ + public InputStream newInput() { + return new ByteArrayInputStream(bytes); + } + + /** + * Creates a {@link CodedInputStream} which can be used to read the bytes. + * Using this is more efficient than creating a {@link CodedInputStream} + * wrapping the result of {@link #newInput()}. + */ + public CodedInputStream newCodedInput() { + // We trust CodedInputStream not to modify the bytes, or to give anyone + // else access to them. + return CodedInputStream.newInstance(bytes); + } + + // ================================================================= + // Output stream + + /** + * Creates a new {@link Output} with the given initial capacity. + */ + public static Output newOutput(int initialCapacity) { + return new Output(new ByteArrayOutputStream(initialCapacity)); + } + + /** + * Creates a new {@link Output}. + */ + public static Output newOutput() { + return newOutput(32); + } + + /** + * Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to + * create the {@code ByteString} instance. + */ + public static final class Output extends FilterOutputStream { + private final ByteArrayOutputStream bout; + + /** + * Constructs a new output with the given initial capacity. + */ + private Output(ByteArrayOutputStream bout) { + super(bout); + this.bout = bout; + } + + /** + * Creates a {@code ByteString} instance from this {@code Output}. + */ + public ByteString toByteString() { + byte[] byteArray = bout.toByteArray(); + return new ByteString(byteArray); + } + } + + /** + * Constructs a new ByteString builder, which allows you to efficiently + * construct a {@code ByteString} by writing to a {@link CodedOutputSteam}. + * Using this is much more efficient than calling {@code newOutput()} and + * wrapping that in a {@code CodedOutputStream}. + * + *

This is package-private because it's a somewhat confusing interface. + * Users can call {@link Message#toByteString()} instead of calling this + * directly. + * + * @param size The target byte size of the {@code ByteString}. You must + * write exactly this many bytes before building the result. + */ + static CodedBuilder newCodedBuilder(int size) { + return new CodedBuilder(size); + } + + /** See {@link ByteString#newCodedBuilder(int)}. */ + static final class CodedBuilder { + private final CodedOutputStream output; + private final byte[] buffer; + + private CodedBuilder(int size) { + buffer = new byte[size]; + output = CodedOutputStream.newInstance(buffer); + } + + public ByteString build() { + output.checkNoSpaceLeft(); + + // We can be confident that the CodedOutputStream will not modify the + // underlying bytes anymore because it already wrote all of them. So, + // no need to make a copy. + return new ByteString(buffer); + } + + public CodedOutputStream getCodedOutput() { + return output; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/CodedInputStream.java b/java/src/main/java/com/google/protobuf/CodedInputStream.java new file mode 100644 index 00000000..b32c349c --- /dev/null +++ b/java/src/main/java/com/google/protobuf/CodedInputStream.java @@ -0,0 +1,766 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * 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 CodedInputStream { + /** + * Create a new CodedInputStream wrapping the given InputStream. + */ + public static CodedInputStream newInstance(InputStream input) { + return new CodedInputStream(input); + } + + /** + * Create a new CodedInputStream wrapping the given byte array. + */ + public static CodedInputStream newInstance(byte[] buf) { + return new CodedInputStream(buf); + } + + // ----------------------------------------------------------------- + + /** + * 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 (bufferPos == bufferSize && !refillBuffer(false)) { + lastTag = 0; + return 0; + } + + lastTag = readRawVarint32(); + if (lastTag == 0) { + // If we actually read zero, that's not a valid tag. + throw InvalidProtocolBufferException.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 InvalidProtocolBufferException {@code value} does not match the + * last tag. + */ + public void checkLastTagWas(int value) throws InvalidProtocolBufferException { + if (lastTag != value) { + throw InvalidProtocolBufferException.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(int tag) throws IOException { + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + readInt32(); + return true; + case WireFormat.WIRETYPE_FIXED64: + readRawLittleEndian64(); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + skipRawBytes(readRawVarint32()); + return true; + case WireFormat.WIRETYPE_START_GROUP: + skipMessage(); + checkLastTagWas( + WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), + WireFormat.WIRETYPE_END_GROUP)); + return true; + case WireFormat.WIRETYPE_END_GROUP: + return false; + case WireFormat.WIRETYPE_FIXED32: + readRawLittleEndian32(); + return true; + default: + throw InvalidProtocolBufferException.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) { + 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 { + 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. + 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(int fieldNumber, Message.Builder builder, + ExtensionRegistry extensionRegistry) + throws IOException { + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + ++recursionDepth; + builder.mergeFrom(this, extensionRegistry); + checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + --recursionDepth; + } + + /** + * Reads a {@code group} field value from the stream and merges it into the + * given {@link UnknownFieldSet}. + */ + public void readUnknownGroup(int fieldNumber, UnknownFieldSet.Builder builder) + throws IOException { + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + ++recursionDepth; + builder.mergeFrom(this); + checkLastTagWas( + WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP)); + --recursionDepth; + } + + /** Read an embedded message field value from the stream. */ + public void readMessage(Message.Builder builder, + ExtensionRegistry extensionRegistry) + throws IOException { + int length = readRawVarint32(); + if (recursionDepth >= recursionLimit) { + throw InvalidProtocolBufferException.recursionLimitExceeded(); + } + int oldLimit = pushLimit(length); + ++recursionDepth; + builder.mergeFrom(this, extensionRegistry); + checkLastTagWas(0); + --recursionDepth; + popLimit(oldLimit); + } + + /** Read a {@code bytes} field value from the stream. */ + public ByteString readBytes() throws IOException { + 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. + ByteString result = ByteString.copyFrom(buffer, bufferPos, size); + bufferPos += size; + return result; + } else { + // Slow path: Build a byte array first then copy it. + return ByteString.copyFrom(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 field of any primitive type. Enums, groups, and embedded + * messages are not handled by this method. + * + * @param type Declared type of the field. + * @return An object representing the field's value, of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public Object readPrimitiveField( + Descriptors.FieldDescriptor.Type type) throws IOException { + switch (type) { + case DOUBLE : return readDouble (); + case FLOAT : return readFloat (); + case INT64 : return readInt64 (); + case UINT64 : return readUInt64 (); + case INT32 : return readInt32 (); + case FIXED64 : return readFixed64 (); + case FIXED32 : return readFixed32 (); + case BOOL : return readBool (); + case STRING : return readString (); + case BYTES : return readBytes (); + case UINT32 : return readUInt32 (); + case SFIXED32: return readSFixed32(); + case SFIXED64: return readSFixed64(); + case SINT32 : return readSInt32 (); + case SINT64 : return readSInt64 (); + + case GROUP: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle nested groups."); + case MESSAGE: + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle embedded messages."); + case ENUM: + // We don't hanlde enums because we don't know what to do if the + // value is not recognized. + throw new IllegalArgumentException( + "readPrimitiveField() cannot handle enums."); + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // ================================================================= + + /** + * 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 InvalidProtocolBufferException.malformedVarint(); + } + } + } + } + return result; + } + + /** Read a raw Varint from the stream. */ + public long readRawVarint64() throws IOException { + int shift = 0; + long result = 0; + while (shift < 64) { + byte b = readRawByte(); + result |= (long)(b & 0x7F) << shift; + if ((b & 0x80) == 0) return result; + shift += 7; + } + throw InvalidProtocolBufferException.malformedVarint(); + } + + /** Read a 32-bit little-endian integer from the stream. */ + public int readRawLittleEndian32() throws IOException { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + byte b4 = readRawByte(); + return (((int)b1 & 0xff) ) | + (((int)b2 & 0xff) << 8) | + (((int)b3 & 0xff) << 16) | + (((int)b4 & 0xff) << 24); + } + + /** Read a 64-bit little-endian integer from the stream. */ + public long readRawLittleEndian64() throws IOException { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + byte b4 = readRawByte(); + byte b5 = readRawByte(); + byte b6 = readRawByte(); + byte b7 = readRawByte(); + 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(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(long n) { + return (n >>> 1) ^ -(n & 1); + } + + // ----------------------------------------------------------------- + + private byte[] buffer; + private int bufferSize; + private int bufferSizeAfterLimit = 0; + private int bufferPos = 0; + private InputStream input; + private int lastTag = 0; + + /** + * The total number of bytes read before the current buffer. The total + * bytes read up to the current position can be computed as + * {@code totalBytesRetired + bufferPos}. + */ + private int totalBytesRetired = 0; + + /** The absolute position of the end of the current message. */ + private int currentLimit = Integer.MAX_VALUE; + + /** See setRecursionLimit() */ + private int recursionDepth = 0; + 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 static final int BUFFER_SIZE = 4096; + + private CodedInputStream(byte[] buffer) { + this.buffer = buffer; + this.bufferSize = buffer.length; + this.input = null; + } + + private CodedInputStream(InputStream input) { + this.buffer = new byte[BUFFER_SIZE]; + this.bufferSize = 0; + this.input = input; + } + + /** + * 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(int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Recursion limit cannot be negative: " + limit); + } + 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 (nor with + * {@link ByteString#newCodedInput}). + * + * @return the old limit. + */ + public int setSizeLimit(int limit) { + if (limit < 0) { + throw new IllegalArgumentException( + "Size limit cannot be negative: " + limit); + } + int oldLimit = sizeLimit; + sizeLimit = limit; + return oldLimit; + } + + /** + * 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 InvalidProtocolBufferException { + if (byteLimit < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + byteLimit += totalBytesRetired + bufferPos; + int oldLimit = currentLimit; + if (byteLimit > oldLimit) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + currentLimit = byteLimit; + + recomputeBufferSizeAfterLimit(); + + return oldLimit; + } + + private void recomputeBufferSizeAfterLimit() { + bufferSize += bufferSizeAfterLimit; + int bufferEnd = totalBytesRetired + 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(int oldLimit) { + currentLimit = oldLimit; + recomputeBufferSizeAfterLimit(); + } + + /** + * Called with {@code this.buffer} is empty to read more bytes from the + * input. If {@code mustSucceed} is true, refillBuffer() gurantees that + * either there will be at least one byte in the buffer when it returns + * or it will throw an exception. If {@code mustSucceed} is false, + * refillBuffer() returns false if no more bytes were available. + */ + private boolean refillBuffer(boolean mustSucceed) throws IOException { + if (bufferPos < bufferSize) { + throw new IllegalStateException( + "refillBuffer() called when buffer wasn't empty."); + } + + if (totalBytesRetired + bufferSize == currentLimit) { + // Oops, we hit a limit. + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; + } + } + + totalBytesRetired += bufferSize; + + bufferPos = 0; + bufferSize = (input == null) ? -1 : input.read(buffer); + if (bufferSize == -1) { + bufferSize = 0; + if (mustSucceed) { + throw InvalidProtocolBufferException.truncatedMessage(); + } else { + return false; + } + } else { + recomputeBufferSizeAfterLimit(); + int totalBytesRead = + totalBytesRetired + bufferSize + bufferSizeAfterLimit; + if (totalBytesRead > sizeLimit || totalBytesRead < 0) { + throw InvalidProtocolBufferException.sizeLimitExceeded(); + } + return true; + } + } + + /** + * Read one byte from the input. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public byte readRawByte() throws IOException { + if (bufferPos == bufferSize) { + refillBuffer(true); + } + return buffer[bufferPos++]; + } + + /** + * Read a fixed size of bytes from the input. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public byte[] readRawBytes(int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + + if (totalBytesRetired + bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); + // Then fail. + throw InvalidProtocolBufferException.truncatedMessage(); + } + + if (size <= bufferSize - bufferPos) { + // We have all the bytes we need already. + byte[] bytes = new byte[size]; + System.arraycopy(buffer, bufferPos, bytes, 0, size); + bufferPos += size; + return bytes; + } else if (size < BUFFER_SIZE) { + // Reading more bytes than are in the buffer, but not an excessive number + // of bytes. We can safely allocate the resulting array ahead of time. + + // First copy what we have. + byte[] bytes = new byte[size]; + int pos = bufferSize - bufferPos; + System.arraycopy(buffer, bufferPos, bytes, 0, pos); + bufferPos = bufferSize; + + // We want to use refillBuffer() and then copy from the buffer into our + // byte array rather than reading directly into our byte array because + // the input may be unbuffered. + refillBuffer(true); + + while (size - pos > bufferSize) { + System.arraycopy(buffer, 0, bytes, pos, bufferSize); + pos += bufferSize; + bufferPos = bufferSize; + refillBuffer(true); + } + + System.arraycopy(buffer, 0, bytes, pos, size - pos); + bufferPos = size - pos; + + return bytes; + } else { + // The size is very large. For security reasons, we can't allocate the + // entire byte array yet. The size comes directly from the input, so a + // maliciously-crafted message could provide a bogus very large size in + // order to trick the app into allocating a lot of memory. We avoid this + // by allocating and reading only a small chunk at a time, so that the + // malicious message must actually *be* extremely large to cause + // problems. Meanwhile, we limit the allowed size of a message elsewhere. + + // Remember the buffer markers since we'll have to copy the bytes out of + // it later. + int originalBufferPos = bufferPos; + int originalBufferSize = bufferSize; + + // Mark the current buffer consumed. + totalBytesRetired += bufferSize; + bufferPos = 0; + bufferSize = 0; + + // Read all the rest of the bytes we need. + int sizeLeft = size - (originalBufferSize - originalBufferPos); + List chunks = new ArrayList(); + + while (sizeLeft > 0) { + byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)]; + int pos = 0; + while (pos < chunk.length) { + int n = (input == null) ? -1 : + input.read(chunk, pos, chunk.length - pos); + if (n == -1) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + totalBytesRetired += n; + pos += n; + } + sizeLeft -= chunk.length; + chunks.add(chunk); + } + + // OK, got everything. Now concatenate it all into one buffer. + byte[] bytes = new byte[size]; + + // Start by copying the leftover bytes from this.buffer. + int pos = originalBufferSize - originalBufferPos; + System.arraycopy(buffer, originalBufferPos, bytes, 0, pos); + + // And now all the chunks. + for (byte[] chunk : chunks) { + System.arraycopy(chunk, 0, bytes, pos, chunk.length); + pos += chunk.length; + } + + // Done. + return bytes; + } + } + + /** + * Reads and discards {@code size} bytes. + * + * @throws InvalidProtocolBufferException The end of the stream or the current + * limit was reached. + */ + public void skipRawBytes(int size) throws IOException { + if (size < 0) { + throw InvalidProtocolBufferException.negativeSize(); + } + + if (totalBytesRetired + bufferPos + size > currentLimit) { + // Read to the end of the stream anyway. + skipRawBytes(currentLimit - totalBytesRetired - bufferPos); + // Then fail. + throw InvalidProtocolBufferException.truncatedMessage(); + } + + if (size < bufferSize - bufferPos) { + // We have all the bytes we need already. + bufferPos += size; + } else { + // Skipping more bytes than are in the buffer. First skip what we have. + int pos = bufferSize - bufferPos; + totalBytesRetired += pos; + bufferPos = 0; + bufferSize = 0; + + // Then skip directly from the InputStream for the rest. + while (pos < size) { + int n = (input == null) ? -1 : (int) input.skip(size - pos); + if (n <= 0) { + throw InvalidProtocolBufferException.truncatedMessage(); + } + pos += n; + totalBytesRetired += n; + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/CodedOutputStream.java b/java/src/main/java/com/google/protobuf/CodedOutputStream.java new file mode 100644 index 00000000..18498999 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/CodedOutputStream.java @@ -0,0 +1,775 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import java.io.OutputStream; +import java.io.IOException; + +/** + * Encodes and writes protocol message fields. + * + *

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

This class is totally unsynchronized. + * + * @author kneton@google.com Kenton Varda + */ +public final class CodedOutputStream { + private final byte[] buffer; + private final int limit; + private int position; + + private final OutputStream output; + + /** + * The buffer size used in {@link #newInstance(java.io.OutputStream)}. + */ + public static final int DEFAULT_BUFFER_SIZE = 4096; + + private CodedOutputStream(byte[] buffer, int offset, int length) { + this.output = null; + this.buffer = buffer; + this.position = offset; + this.limit = offset + length; + } + + private CodedOutputStream(OutputStream output, byte[] buffer) { + this.output = output; + this.buffer = buffer; + this.position = 0; + this.limit = buffer.length; + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given + * {@code OutputStream}. + */ + public static CodedOutputStream newInstance(OutputStream output) { + return newInstance(output, DEFAULT_BUFFER_SIZE); + } + + /** + * Create a new {@code CodedOutputStream} wrapping the given + * {@code OutputStream} with a given buffer size. + */ + public static CodedOutputStream newInstance(OutputStream output, + int bufferSize) { + return new CodedOutputStream(output, new byte[bufferSize]); + } + + /** + * 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}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance(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}. See also + * {@link ByteString#newCodedBuilder}. + */ + public static CodedOutputStream newInstance(byte[] flatArray, int offset, + int length) { + return new CodedOutputStream(flatArray, offset, length); + } + + // ----------------------------------------------------------------- + + /** Write a {@code double} field, including tag, to the stream. */ + public void writeDouble(int fieldNumber, double value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public void writeFloat(int fieldNumber, float value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(Float.floatToRawIntBits(value)); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + public void writeUInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public void writeInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int32} field, including tag, to the stream. */ + public void writeInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + public void writeFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + public void writeFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write a {@code bool} field, including tag, to the stream. */ + public void writeBool(int fieldNumber, boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawByte(value ? 1 : 0); + } + + /** Write a {@code string} field, including tag, to the stream. */ + public void writeString(int fieldNumber, String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + // 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. + byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code group} field, including tag, to the stream. */ + public void writeGroup(int fieldNumber, Message value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + value.writeTo(this); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** Write a group represented by an {@link UnknownFieldSet}. */ + public void writeUnknownGroup(int fieldNumber, UnknownFieldSet value) + throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_START_GROUP); + value.writeTo(this); + writeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP); + } + + /** Write an embedded message field, including tag, to the stream. */ + public void writeMessage(int fieldNumber, Message value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeRawVarint32(value.getSerializedSize()); + value.writeTo(this); + } + + /** Write a {@code bytes} field, including tag, to the stream. */ + public void writeBytes(int fieldNumber, ByteString value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + byte[] bytes = value.toByteArray(); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code uint32} field, including tag, to the stream. */ + public void writeUInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(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(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(value); + } + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public void writeSFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public void writeSFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write an {@code sint32} field, including tag, to the stream. */ + public void writeSInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write an {@code sint64} field, including tag, to the stream. */ + public void writeSInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(encodeZigZag64(value)); + } + + /** + * Write a MessageSet extension field to the stream. For historical reasons, + * the wire format differs from normal fields. + */ + public void writeMessageSetExtension(int fieldNumber, Message value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeMessage(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.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(int fieldNumber, ByteString value) + throws IOException { + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_START_GROUP); + writeUInt32(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber); + writeBytes(WireFormat.MESSAGE_SET_MESSAGE, value); + writeTag(WireFormat.MESSAGE_SET_ITEM, WireFormat.WIRETYPE_END_GROUP); + } + + /** + * Write a field of arbitrary type, including tag, to the stream. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public void writeField(Descriptors.FieldDescriptor.Type type, + int number, Object value) throws IOException { + switch (type) { + case DOUBLE : writeDouble (number, (Double )value); break; + case FLOAT : writeFloat (number, (Float )value); break; + case INT64 : writeInt64 (number, (Long )value); break; + case UINT64 : writeUInt64 (number, (Long )value); break; + case INT32 : writeInt32 (number, (Integer )value); break; + case FIXED64 : writeFixed64 (number, (Long )value); break; + case FIXED32 : writeFixed32 (number, (Integer )value); break; + case BOOL : writeBool (number, (Boolean )value); break; + case STRING : writeString (number, (String )value); break; + case GROUP : writeGroup (number, (Message )value); break; + case MESSAGE : writeMessage (number, (Message )value); break; + case BYTES : writeBytes (number, (ByteString)value); break; + case UINT32 : writeUInt32 (number, (Integer )value); break; + case SFIXED32: writeSFixed32(number, (Integer )value); break; + case SFIXED64: writeSFixed64(number, (Long )value); break; + case SINT32 : writeSInt32 (number, (Integer )value); break; + case SINT64 : writeSInt64 (number, (Long )value); break; + + case ENUM: + writeEnum(number, ((Descriptors.EnumValueDescriptor)value).getNumber()); + break; + } + } + + // ================================================================= + + /** + * Compute the number of bytes that would be needed to encode a + * {@code double} field, including tag. + */ + public static int computeDoubleSize(int fieldNumber, double value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code float} field, including tag. + */ + public static int computeFloatSize(int fieldNumber, float value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint64} field, including tag. + */ + public static int computeUInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int64} field, including tag. + */ + public static int computeInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code int32} field, including tag. + */ + public static int computeInt32Size(int fieldNumber, int value) { + if (value >= 0) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } else { + // Must sign-extend. + return computeTagSize(fieldNumber) + 10; + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed64} field, including tag. + */ + public static int computeFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code fixed32} field, including tag. + */ + public static int computeFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bool} field, including tag. + */ + public static int computeBoolSize(int fieldNumber, boolean value) { + return computeTagSize(fieldNumber) + 1; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code string} field, including tag. + */ + public static int computeStringSize(int fieldNumber, String value) { + try { + byte[] bytes = value.getBytes("UTF-8"); + return computeTagSize(fieldNumber) + + computeRawVarint32Size(bytes.length) + + bytes.length; + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field, including tag. + */ + public static int computeGroupSize(int fieldNumber, Message value) { + return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code group} field represented by an {@code UnknownFieldSet}, including + * tag. + */ + public static int computeUnknownGroupSize(int fieldNumber, + UnknownFieldSet value) { + return computeTagSize(fieldNumber) * 2 + value.getSerializedSize(); + } + + /** + * Compute the number of bytes that would be needed to encode an + * embedded message field, including tag. + */ + public static int computeMessageSize(int fieldNumber, Message value) { + int size = value.getSerializedSize(); + return computeTagSize(fieldNumber) + computeRawVarint32Size(size) + size; + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code bytes} field, including tag. + */ + public static int computeBytesSize(int fieldNumber, ByteString value) { + return computeTagSize(fieldNumber) + + computeRawVarint32Size(value.size()) + + value.size(); + } + + /** + * Compute the number of bytes that would be needed to encode a + * {@code uint32} field, including tag. + */ + public static int computeUInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(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(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed32} field, including tag. + */ + public static int computeSFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sfixed64} field, including tag. + */ + public static int computeSFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint32} field, including tag. + */ + public static int computeSInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + + computeRawVarint32Size(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode an + * {@code sint64} field, including tag. + */ + public static int computeSInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + + computeRawVarint64Size(encodeZigZag64(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( + int fieldNumber, Message value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeMessageSize(WireFormat.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( + int fieldNumber, ByteString value) { + return computeTagSize(WireFormat.MESSAGE_SET_ITEM) * 2 + + computeUInt32Size(WireFormat.MESSAGE_SET_TYPE_ID, fieldNumber) + + computeBytesSize(WireFormat.MESSAGE_SET_MESSAGE, value); + } + + /** + * Compute the number of bytes that would be needed to encode a + * field of arbitrary type, including tag, to the stream. + * + * @param type The field's type. + * @param number The field's number. + * @param value Object representing the field's value. Must be of the exact + * type which would be returned by + * {@link Message#getField(Descriptors.FieldDescriptor)} for + * this field. + */ + public static int computeFieldSize( + Descriptors.FieldDescriptor.Type type, + int number, Object value) { + switch (type) { + case DOUBLE : return computeDoubleSize (number, (Double )value); + case FLOAT : return computeFloatSize (number, (Float )value); + case INT64 : return computeInt64Size (number, (Long )value); + case UINT64 : return computeUInt64Size (number, (Long )value); + case INT32 : return computeInt32Size (number, (Integer )value); + case FIXED64 : return computeFixed64Size (number, (Long )value); + case FIXED32 : return computeFixed32Size (number, (Integer )value); + case BOOL : return computeBoolSize (number, (Boolean )value); + case STRING : return computeStringSize (number, (String )value); + case GROUP : return computeGroupSize (number, (Message )value); + case MESSAGE : return computeMessageSize (number, (Message )value); + case BYTES : return computeBytesSize (number, (ByteString)value); + case UINT32 : return computeUInt32Size (number, (Integer )value); + case SFIXED32: return computeSFixed32Size(number, (Integer )value); + case SFIXED64: return computeSFixed64Size(number, (Long )value); + case SINT32 : return computeSInt32Size (number, (Integer )value); + case SINT64 : return computeSInt64Size (number, (Long )value); + + case ENUM: + return computeEnumSize(number, + ((Descriptors.EnumValueDescriptor)value).getNumber()); + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // ================================================================= + + /** + * Internal helper that writes the current buffer to the output. The + * buffer position is reset to its initial value when this returns. + */ + private void refreshBuffer() throws IOException { + if (output == null) { + // We're writing to a single buffer. + throw new OutOfSpaceException(); + } + + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + output.write(buffer, 0, position); + position = 0; + } + + /** + * Flushes the stream and forces any buffered bytes to be written. This + * does not flush the underlying OutputStream. + */ + public void flush() throws IOException { + if (output != null) { + refreshBuffer(); + } + } + + /** + * If writing to a flat array, return the space left in the array. + * Otherwise, throws {@code UnsupportedOperationException}. + */ + public int spaceLeft() { + if (output == null) { + return limit - position; + } else { + throw new UnsupportedOperationException( + "spaceLeft() can only be called on CodedOutputStreams that are " + + "writing to a flat array."); + } + } + + /** + * 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 { + OutOfSpaceException() { + super("CodedOutputStream was writing to a flat byte array and ran " + + "out of space."); + } + } + + /** Write a single byte. */ + public void writeRawByte(byte value) throws IOException { + if (position == limit) { + refreshBuffer(); + } + + buffer[position++] = value; + } + + /** Write a single byte, represented by an integer value. */ + public void writeRawByte(int value) throws IOException { + writeRawByte((byte) value); + } + + /** Write an array of bytes. */ + public void writeRawBytes(byte[] value) throws IOException { + writeRawBytes(value, 0, value.length); + } + + /** Write part of an array of bytes. */ + public void writeRawBytes(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 { + // Write extends past current buffer. Fill the rest of this buffer and + // flush. + int bytesWritten = limit - position; + System.arraycopy(value, offset, buffer, position, bytesWritten); + offset += bytesWritten; + length -= bytesWritten; + position = limit; + refreshBuffer(); + + // Now deal with the rest. + // Since we have an output stream, this is our buffer + // and buffer offset == 0 + if (length <= limit) { + // Fits in new buffer. + System.arraycopy(value, offset, buffer, 0, length); + position = length; + } else { + // Write is very big. Let's do it all at once. + output.write(value, offset, length); + } + } + } + + /** Encode and write a tag. */ + public void writeTag(int fieldNumber, int wireType) throws IOException { + writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); + } + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(int fieldNumber) { + return computeRawVarint32Size(WireFormat.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(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(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(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(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(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(long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } +} diff --git a/java/src/main/java/com/google/protobuf/Descriptors.java b/java/src/main/java/com/google/protobuf/Descriptors.java new file mode 100644 index 00000000..9ad8e521 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Descriptors.java @@ -0,0 +1,1635 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.DescriptorProtos.*; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Contains a collection of classes which describe protocol message types. + * + * Every message type has a {@link Descriptors.Descriptor}, which lists all + * its fields and other information about a type. You can get a message + * type's descriptor by calling {@code MessageType.getDescriptor()}, or + * (given a message object of the type) {@code message.getDescriptorForType()}. + * + * Descriptors are built from DescriptorProtos, as defined in + * {@code net/proto2/proto/descriptor.proto}. + * + * @author kenton@google.com Kenton Varda + */ +public final class Descriptors { + /** + * Describes a {@code .proto} file, including everything defined within. + */ + public static final class FileDescriptor { + /** Convert the descriptor to its protocol message representation. */ + public FileDescriptorProto toProto() { return proto; } + + /** Get the file name. */ + public String getName() { return proto.getName(); } + + /** + * Get the proto package name. This is the package name given by the + * {@code package} statement in the {@code .proto} file, which differs + * from the Java package. + */ + public String getPackage() { return proto.getPackage(); } + + /** Get the {@code FileOptions}, defined in {@code descriptor.proto}. */ + public FileOptions getOptions() { return proto.getOptions(); } + + /** Get a list of top-level message types declared in this file. */ + public List getMessageTypes() { + return Collections.unmodifiableList(Arrays.asList(messageTypes)); + } + + /** Get a list of top-level enum types declared in this file. */ + public List getEnumTypes() { + return Collections.unmodifiableList(Arrays.asList(enumTypes)); + } + + /** Get a list of top-level services declared in this file. */ + public List getServices() { + return Collections.unmodifiableList(Arrays.asList(services)); + } + + /** Get a list of top-level extensions declared in this file. */ + public List getExtensions() { + return Collections.unmodifiableList(Arrays.asList(extensions)); + } + + /** Get a list of this file's dependencies (imports). */ + public List getDependencies() { + return Collections.unmodifiableList(Arrays.asList(dependencies)); + } + + /** + * Find a message type in the file by name. Does not find nested types. + * + * @param name The unqualified type name to look for. + * @return The message type's descriptor, or {@code null} if not found. + */ + public Descriptor findMessageTypeByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof Descriptor && + result.getFile() == this) { + return (Descriptor)result; + } else { + return null; + } + } + + /** + * Find an enum type in the file by name. Does not find nested types. + * + * @param name The unqualified type name to look for. + * @return The enum type's descriptor, or {@code null} if not found. + */ + public EnumDescriptor findEnumTypeByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof EnumDescriptor && + result.getFile() == this) { + return (EnumDescriptor)result; + } else { + return null; + } + } + + /** + * Find a service type in the file by name. + * + * @param name The unqualified type name to look for. + * @return The service type's descriptor, or {@code null} if not found. + */ + public ServiceDescriptor findServiceByName(String name) { + // Don't allow looking up nested types. This will make optimization + // easier later. + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof ServiceDescriptor && + result.getFile() == this) { + return (ServiceDescriptor)result; + } else { + return null; + } + } + + /** + * Find an extension in the file by name. Does not find extensions nested + * inside message types. + * + * @param name The unqualified extension name to look for. + * @return The extension's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findExtensionByName(String name) { + if (name.indexOf('.') != -1) return null; + if (getPackage().length() > 0) { + name = getPackage() + "." + name; + } + GenericDescriptor result = pool.findSymbol(name); + if (result != null && result instanceof FieldDescriptor && + result.getFile() == this) { + return (FieldDescriptor)result; + } else { + return null; + } + } + + /** + * Construct a {@code FileDescriptor}. + * + * @param proto The protocol message form of the FileDescriptor. + * @param dependencies {@code FileDescriptor}s corresponding to all of + * the file's dependencies, in the exact order listed + * in {@code proto}. + * @throws DescriptorValidationException {@code proto} is not a valid + * descriptor. This can occur for a number of reasons, e.g. + * because a field has an undefined type or because two messages + * were defined with the same name. + */ + public static FileDescriptor buildFrom(FileDescriptorProto proto, + FileDescriptor[] dependencies) + throws DescriptorValidationException { + // Building decsriptors involves two steps: translating and linking. + // In the translation step (implemented by FileDescriptor's + // constructor), we build an object tree mirroring the + // FileDescriptorProto's tree and put all of the descriptors into the + // DescriptorPool's lookup tables. In the linking step, we look up all + // type references in the DescriptorPool, so that, for example, a + // FieldDescriptor for an embedded message contains a pointer directly + // to the Descriptor for that message's type. We also detect undefined + // types in the linking step. + DescriptorPool pool = new DescriptorPool(dependencies); + FileDescriptor result = new FileDescriptor(proto, dependencies, pool); + + if (dependencies.length != proto.getDependencyCount()) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + for (int i = 0; i < proto.getDependencyCount(); i++) { + if (!dependencies[i].getName().equals(proto.getDependency(i))) { + throw new DescriptorValidationException(result, + "Dependencies passed to FileDescriptor.buildFrom() don't match " + + "those listed in the FileDescriptorProto."); + } + } + + result.crossLink(); + return result; + } + + /** + * This method is to be called by generated code only. It is equivalent + * to {@code buildFrom} except that the {@code FileDescriptorProto} is + * encoded in protocol buffer wire format. + */ + public static FileDescriptor internalBuildGeneratedFileFrom( + String descriptorData, FileDescriptor[] dependencies) + throws DescriptorValidationException, + InvalidProtocolBufferException { + // Hack: We can't embed a raw byte array inside generated Java code + // (at least, not efficiently), but we can embed Strings. So, the + // protocol compiler embeds the FileDescriptorProto as a giant + // string literal which is passed to this function to construct the + // file's FileDescriptor. The string literal contains only 8-bit + // characters, each one representing a byte of the FileDescriptorProto's + // serialized form. So, if we convert it to bytes in ISO-8859-1, we + // should get the original bytes that we want. + try { + FileDescriptorProto proto = + FileDescriptorProto.parseFrom(descriptorData.getBytes("ISO-8859-1")); + return buildFrom(proto, dependencies); + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException( + "Standard encoding ISO-8859-1 not supported by JVM.", e); + } + } + + private final FileDescriptorProto proto; + private final Descriptor[] messageTypes; + private final EnumDescriptor[] enumTypes; + private final ServiceDescriptor[] services; + private final FieldDescriptor[] extensions; + private final FileDescriptor[] dependencies; + private final DescriptorPool pool; + + private FileDescriptor(FileDescriptorProto proto, + FileDescriptor[] dependencies, + DescriptorPool pool) + throws DescriptorValidationException { + this.pool = pool; + this.proto = proto; + this.dependencies = dependencies.clone(); + + pool.addPackage(getPackage(), this); + + messageTypes = new Descriptor[proto.getMessageTypeCount()]; + for (int i = 0; i < proto.getMessageTypeCount(); i++) { + messageTypes[i] = + new Descriptor(proto.getMessageType(i), this, null, i); + } + + enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; + for (int i = 0; i < proto.getEnumTypeCount(); i++) { + enumTypes[i] = new EnumDescriptor(proto.getEnumType(i), this, null, i); + } + + services = new ServiceDescriptor[proto.getServiceCount()]; + for (int i = 0; i < proto.getServiceCount(); i++) { + services[i] = new ServiceDescriptor(proto.getService(i), this, i); + } + + extensions = new FieldDescriptor[proto.getExtensionCount()]; + for (int i = 0; i < proto.getExtensionCount(); i++) { + extensions[i] = new FieldDescriptor( + proto.getExtension(i), this, null, i, true); + } + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < messageTypes.length; i++) { + messageTypes[i].crossLink(); + } + + for (int i = 0; i < services.length; i++) { + services[i].crossLink(); + } + + for (int i = 0; i < extensions.length; i++) { + extensions[i].crossLink(); + } + } + } + + // ================================================================= + + /** Describes a message type. */ + public static final class Descriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. In other words, + * given a {@link FileDescriptor} {@code file}, the following is true: + *

+     *   for all i in [0, file.getMessageTypeCount()):
+     *     file.getMessageType(i).getIndex() == i
+     * 
+ * Similarly, for a {@link Descriptor} {@code messageType}: + *
+     *   for all i in [0, messageType.getNestedTypeCount()):
+     *     messageType.getNestedType(i).getIndex() == i
+     * 
+ */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public DescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name, within the proto language's + * namespace. This differs from the Java name. For example, given this + * {@code .proto}: + *
+     *   package foo.bar;
+     *   option java_package = "com.example.protos"
+     *   message Baz {}
+     * 
+ * {@code Baz}'s full name is "foo.bar.Baz". + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** If this is a nested type, get the outer descriptor, otherwise null. */ + public Descriptor getContainingType() { return containingType; } + + /** Get the {@code MessageOptions}, defined in {@code descriptor.proto}. */ + public MessageOptions getOptions() { return proto.getOptions(); } + + /** Get a list of this message type's fields. */ + public List getFields() { + return Collections.unmodifiableList(Arrays.asList(fields)); + } + + /** Get a list of this message type's extensions. */ + public List getExtensions() { + return Collections.unmodifiableList(Arrays.asList(extensions)); + } + + /** Get a list of message types nested within this one. */ + public List getNestedTypes() { + return Collections.unmodifiableList(Arrays.asList(nestedTypes)); + } + + /** Get a list of enum types nested within this one. */ + public List getEnumTypes() { + return Collections.unmodifiableList(Arrays.asList(enumTypes)); + } + + /** Determines if the given field number is an extension. */ + public boolean isExtensionNumber(int number) { + for (DescriptorProto.ExtensionRange range : proto.getExtensionRangeList()) { + if (range.getStart() <= number && number < range.getEnd()) { + return true; + } + } + return false; + } + + /** + * Finds a field by name. + * @param name The unqualified name of the field (e.g. "foo"). + * @return The field's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findFieldByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof FieldDescriptor) { + return (FieldDescriptor)result; + } else { + return null; + } + } + + /** + * Finds a field by field number. + * @param number The field number within this message type. + * @return The field's descriptor, or {@code null} if not found. + */ + public FieldDescriptor findFieldByNumber(int number) { + return file.pool.fieldsByNumber.get( + new DescriptorPool.DescriptorIntPair(this, number)); + } + + /** + * Finds a nested message type by name. + * @param name The unqualified name of the nested type (e.g. "Foo"). + * @return The types's descriptor, or {@code null} if not found. + */ + public Descriptor findNestedTypeByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof Descriptor) { + return (Descriptor)result; + } else { + return null; + } + } + + /** + * Finds a nested enum type by name. + * @param name The unqualified name of the nested type (e.g. "Foo"). + * @return The types's descriptor, or {@code null} if not found. + */ + public EnumDescriptor findEnumTypeByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof EnumDescriptor) { + return (EnumDescriptor)result; + } else { + return null; + } + } + + private final int index; + private final DescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor containingType; + private final Descriptor[] nestedTypes; + private final EnumDescriptor[] enumTypes; + private final FieldDescriptor[] fields; + private final FieldDescriptor[] extensions; + + private Descriptor(DescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + this.containingType = parent; + + this.nestedTypes = new Descriptor[proto.getNestedTypeCount()]; + for (int i = 0; i < proto.getNestedTypeCount(); i++) { + this.nestedTypes[i] = new Descriptor( + proto.getNestedType(i), file, this, i); + } + + this.enumTypes = new EnumDescriptor[proto.getEnumTypeCount()]; + for (int i = 0; i < proto.getEnumTypeCount(); i++) { + this.enumTypes[i] = new EnumDescriptor( + proto.getEnumType(i), file, this, i); + } + + this.fields = new FieldDescriptor[proto.getFieldCount()]; + for (int i = 0; i < proto.getFieldCount(); i++) { + this.fields[i] = new FieldDescriptor( + proto.getField(i), file, this, i, false); + } + + this.extensions = new FieldDescriptor[proto.getExtensionCount()]; + for (int i = 0; i < proto.getExtensionCount(); i++) { + this.extensions[i] = new FieldDescriptor( + proto.getExtension(i), file, this, i, true); + } + + file.pool.addSymbol(this); + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < nestedTypes.length; i++) { + nestedTypes[i].crossLink(); + } + + for (int i = 0; i < fields.length; i++) { + fields[i].crossLink(); + } + + for (int i = 0; i < extensions.length; i++) { + extensions[i].crossLink(); + } + } + } + + // ================================================================= + + /** Describes a field of a message type. */ + public static final class FieldDescriptor + implements GenericDescriptor, Comparable { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public FieldDescriptorProto toProto() { return proto; } + + /** Get the field's unqualified name. */ + public String getName() { return proto.getName(); } + + /** Get the field's number. */ + public int getNumber() { return proto.getNumber(); } + + /** + * Get the field's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** + * Get the field's java type. This is just for convenience. Every + * {@code FieldDescriptorProto.Type} maps to exactly one Java type. + */ + public JavaType getJavaType() { return type.getJavaType(); } + + /** Get the {@code FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the field's declared type. */ + public Type getType() { return type; } + + /** Is this field declared required? */ + public boolean isRequired() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REQUIRED; + } + + /** Is this field declared optional? */ + public boolean isOptional() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_OPTIONAL; + } + + /** Is this field declared repeated? */ + public boolean isRepeated() { + return proto.getLabel() == FieldDescriptorProto.Label.LABEL_REPEATED; + } + + /** Returns true if the field had an explicitly-defined default value. */ + public boolean hasDefaultValue() { return proto.hasDefaultValue(); } + + /** + * Returns the field's default value. Valid for all types except for + * messages and groups. For all other types, the object returned is of + * the same class that would returned by Message.getField(this). + */ + public Object getDefaultValue() { + if (getJavaType() == JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "FieldDescriptor.getDefaultValue() called on an embedded message " + + "field."); + } + return defaultValue; + } + + /** Get the {@code FieldOptions}, defined in {@code descriptor.proto}. */ + public FieldOptions getOptions() { return proto.getOptions(); } + + /** Is this field an extension? */ + public boolean isExtension() { return proto.hasExtendee(); } + + /** + * Get the field's containing type. For extensions, this is the type being + * extended, not the location where the extension was defined. See + * {@link #getExtensionScope()}. + */ + public Descriptor getContainingType() { return containingType; } + + /** + * For extensions defined nested within message types, gets the outer + * type. Not valid for non-extension fields. For example, consider + * this {@code .proto} file: + *
+     *   message Foo {
+     *     extensions 1000 to max;
+     *   }
+     *   extend Foo {
+     *     optional int32 baz = 1234;
+     *   }
+     *   message Bar {
+     *     extend Foo {
+     *       optional int32 qux = 4321;
+     *     }
+     *   }
+     * 
+ * Both {@code baz}'s and {@code qux}'s containing type is {@code Foo}. + * However, {@code baz}'s extension scope is {@code null} while + * {@code qux}'s extension scope is {@code Bar}. + */ + public Descriptor getExtensionScope() { + if (!isExtension()) { + throw new UnsupportedOperationException( + "This field is not an extension."); + } + return extensionScope; + } + + /** For embedded message and group fields, gets the field's type. */ + public Descriptor getMessageType() { + if (getJavaType() != JavaType.MESSAGE) { + throw new UnsupportedOperationException( + "This field is not of message type."); + } + return messageType; + } + + /** For enum fields, gets the field's type. */ + public EnumDescriptor getEnumType() { + if (getJavaType() != JavaType.ENUM) { + throw new UnsupportedOperationException( + "This field is not of enum type."); + } + return enumType; + } + + /** + * Compare with another {@code FieldDescriptor}. This orders fields in + * "canonical" order, which simply means ascending order by field number. + * {@code other} must be a field of the same type -- i.e. + * {@code getContainingType()} must return the same {@code Descriptor} for + * both fields. + * + * @return negative, zero, or positive if {@code this} is less than, + * equal to, or greater than {@code other}, respectively. + */ + public int compareTo(FieldDescriptor other) { + if (other.containingType != containingType) { + throw new IllegalArgumentException( + "FieldDescriptors can only be compared to other FieldDescriptors " + + "for fields of the same message type."); + } + return getNumber() - other.getNumber(); + } + + private final int index; + + private final FieldDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor extensionScope; + + // Possibly initialized during cross-linking. + private Type type; + private Descriptor containingType; + private Descriptor messageType; + private EnumDescriptor enumType; + private Object defaultValue; + + public static enum Type { + DOUBLE (FieldDescriptorProto.Type.TYPE_DOUBLE , JavaType.DOUBLE ), + FLOAT (FieldDescriptorProto.Type.TYPE_FLOAT , JavaType.FLOAT ), + INT64 (FieldDescriptorProto.Type.TYPE_INT64 , JavaType.LONG ), + UINT64 (FieldDescriptorProto.Type.TYPE_UINT64 , JavaType.LONG ), + INT32 (FieldDescriptorProto.Type.TYPE_INT32 , JavaType.INT ), + FIXED64 (FieldDescriptorProto.Type.TYPE_FIXED64 , JavaType.LONG ), + FIXED32 (FieldDescriptorProto.Type.TYPE_FIXED32 , JavaType.INT ), + BOOL (FieldDescriptorProto.Type.TYPE_BOOL , JavaType.BOOLEAN ), + STRING (FieldDescriptorProto.Type.TYPE_STRING , JavaType.STRING ), + GROUP (FieldDescriptorProto.Type.TYPE_GROUP , JavaType.MESSAGE ), + MESSAGE (FieldDescriptorProto.Type.TYPE_MESSAGE , JavaType.MESSAGE ), + BYTES (FieldDescriptorProto.Type.TYPE_BYTES , JavaType.BYTE_STRING), + UINT32 (FieldDescriptorProto.Type.TYPE_UINT32 , JavaType.INT ), + ENUM (FieldDescriptorProto.Type.TYPE_ENUM , JavaType.ENUM ), + SFIXED32(FieldDescriptorProto.Type.TYPE_SFIXED32, JavaType.INT ), + SFIXED64(FieldDescriptorProto.Type.TYPE_SFIXED64, JavaType.LONG ), + SINT32 (FieldDescriptorProto.Type.TYPE_SINT32 , JavaType.INT ), + SINT64 (FieldDescriptorProto.Type.TYPE_SINT64 , JavaType.LONG ); + + private Type(FieldDescriptorProto.Type proto, JavaType javaType) { + this.proto = proto; + this.javaType = javaType; + + if (this.ordinal() != proto.getNumber() - 1) { + throw new RuntimeException( + "descriptor.proto changed but Desrciptors.java wasn't updated."); + } + } + + private FieldDescriptorProto.Type proto; + private JavaType javaType; + + public FieldDescriptorProto.Type toProto() { return proto; } + public JavaType getJavaType() { return javaType; } + + public static Type valueOf(FieldDescriptorProto.Type type) { + return values()[type.getNumber() - 1]; + } + } + + static { + // Refuse to init if someone added a new declared type. + if (Type.values().length != FieldDescriptorProto.Type.values().length) { + throw new RuntimeException( + "descriptor.proto has a new declared type but Desrciptors.java " + + "wasn't updated."); + } + } + + public static enum JavaType { + INT(0), + LONG(0L), + FLOAT(0F), + DOUBLE(0D), + BOOLEAN(false), + STRING(""), + BYTE_STRING(ByteString.EMPTY), + ENUM(null), + MESSAGE(null); + + private JavaType(Object defaultDefault) { + this.defaultDefault = defaultDefault; + } + + /** + * The default default value for fields of this type, if it's a primitive + * type. This is meant for use inside this file only, hence is private. + */ + private Object defaultDefault; + } + + private FieldDescriptor(FieldDescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index, + boolean isExtension) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + + if (proto.hasType()) { + this.type = Type.valueOf(proto.getType()); + } + + if (getNumber() <= 0) { + throw new DescriptorValidationException(this, + "Field numbers must be positive integers."); + } + + if (isExtension) { + if (!proto.hasExtendee()) { + throw new DescriptorValidationException(this, + "FieldDescriptorProto.extendee not set for extension field."); + } + this.containingType = null; // Will be filled in when cross-linking + if (parent != null) { + this.extensionScope = parent; + } else { + this.extensionScope = null; + } + } else { + if (proto.hasExtendee()) { + throw new DescriptorValidationException(this, + "FieldDescriptorProto.extendee set for non-extension field."); + } + this.containingType = parent; + this.extensionScope = null; + } + + file.pool.addSymbol(this); + } + + /** Look up and cross-link all field types, etc. */ + private void crossLink() throws DescriptorValidationException { + if (proto.hasExtendee()) { + GenericDescriptor extendee = + file.pool.lookupSymbol(proto.getExtendee(), this); + if (!(extendee instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getExtendee() + "\" is not a message type."); + } + this.containingType = (Descriptor)extendee; + + if (!getContainingType().isExtensionNumber(getNumber())) { + throw new DescriptorValidationException(this, + "\"" + getContainingType().getFullName() + "\" does not declare " + + getNumber() + " as an extension number."); + } + } + + if (proto.hasTypeName()) { + GenericDescriptor typeDescriptor = + file.pool.lookupSymbol(proto.getTypeName(), this); + + if (!proto.hasType()) { + // Choose field type based on symbol. + if (typeDescriptor instanceof Descriptor) { + this.type = Type.MESSAGE; + } else if (typeDescriptor instanceof EnumDescriptor) { + this.type = Type.ENUM; + } else { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not a type."); + } + } + + if (getJavaType() == JavaType.MESSAGE) { + if (!(typeDescriptor instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not a message type."); + } + this.messageType = (Descriptor)typeDescriptor; + + if (proto.hasDefaultValue()) { + throw new DescriptorValidationException(this, + "Messages can't have default values."); + } + } else if (getJavaType() == JavaType.ENUM) { + if (!(typeDescriptor instanceof EnumDescriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getTypeName() + "\" is not an enum type."); + } + this.enumType = (EnumDescriptor)typeDescriptor; + } else { + throw new DescriptorValidationException(this, + "Field with primitive type has type_name."); + } + } else { + if (getJavaType() == JavaType.MESSAGE || + getJavaType() == JavaType.ENUM) { + throw new DescriptorValidationException(this, + "Field with message or enum type missing type_name."); + } + } + + // We don't attempt to parse the default value until here because for + // enums we need the enum type's descriptor. + if (proto.hasDefaultValue()) { + if (isRepeated()) { + throw new DescriptorValidationException(this, + "Repeated fields cannot have default values."); + } + + try { + switch (getType()) { + case INT32: + case SINT32: + case SFIXED32: + defaultValue = TextFormat.parseInt32(proto.getDefaultValue()); + break; + case UINT32: + case FIXED32: { + defaultValue = TextFormat.parseUInt32(proto.getDefaultValue()); + break; + } + case INT64: + case SINT64: + case SFIXED64: + defaultValue = TextFormat.parseInt64(proto.getDefaultValue()); + break; + case UINT64: + case FIXED64: { + defaultValue = TextFormat.parseUInt64(proto.getDefaultValue()); + break; + } + case FLOAT: + defaultValue = Float.valueOf(proto.getDefaultValue()); + break; + case DOUBLE: + defaultValue = Double.valueOf(proto.getDefaultValue()); + break; + case BOOL: + defaultValue = Boolean.valueOf(proto.getDefaultValue()); + break; + case STRING: + defaultValue = proto.getDefaultValue(); + break; + case BYTES: + try { + defaultValue = + TextFormat.unescapeBytes(proto.getDefaultValue()); + } catch (TextFormat.InvalidEscapeSequence e) { + throw new DescriptorValidationException(this, + "Couldn't parse default value: " + e.getMessage()); + } + break; + case ENUM: + defaultValue = enumType.findValueByName(proto.getDefaultValue()); + if (defaultValue == null) { + throw new DescriptorValidationException(this, + "Unknown enum default value: \"" + + proto.getDefaultValue() + "\""); + } + break; + case MESSAGE: + case GROUP: + throw new DescriptorValidationException(this, + "Message type had default value."); + } + } catch (NumberFormatException e) { + DescriptorValidationException validationException = + new DescriptorValidationException(this, + "Could not parse default value: \"" + + proto.getDefaultValue() + "\""); + validationException.initCause(e); + throw validationException; + } + } else { + // Determine the default default for this field. + if (isRepeated()) { + defaultValue = Collections.EMPTY_LIST; + } else { + switch (getJavaType()) { + case ENUM: + // We guarantee elsewhere that an enum type always has at least + // one possible value. + defaultValue = enumType.getValues().get(0); + break; + case MESSAGE: + defaultValue = null; + break; + default: + defaultValue = getJavaType().defaultDefault; + break; + } + } + } + + if (!isExtension()) { + file.pool.addFieldByNumber(this); + } + + if (containingType != null && + containingType.getOptions().getMessageSetWireFormat()) { + if (isExtension()) { + if (!isOptional() || getType() != Type.MESSAGE) { + throw new DescriptorValidationException(this, + "Extensions of MessageSets must be optional messages."); + } + } else { + throw new DescriptorValidationException(this, + "MessageSets cannot have fields, only extensions."); + } + } + } + } + + // ================================================================= + + /** Describes an enum type. */ + public static final class EnumDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public EnumDescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** If this is a nested type, get the outer descriptor, otherwise null. */ + public Descriptor getContainingType() { return containingType; } + + /** Get the {@code EnumOptions}, defined in {@code descriptor.proto}. */ + public EnumOptions getOptions() { return proto.getOptions(); } + + /** Get a list of defined values for this enum. */ + public List getValues() { + return Collections.unmodifiableList(Arrays.asList(values)); + } + + /** + * Find an enum value by name. + * @param name The unqualified name of the value (e.g. "FOO"). + * @return the value's decsriptor, or {@code null} if not found. + */ + public EnumValueDescriptor findValueByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof EnumValueDescriptor) { + return (EnumValueDescriptor)result; + } else { + return null; + } + } + + /** + * Find an enum value by number. If multiple enum values have the same + * number, this returns the first defined value with that number. + * @param number The value's number. + * @return the value's decsriptor, or {@code null} if not found. + */ + public EnumValueDescriptor findValueByNumber(int number) { + return file.pool.enumValuesByNumber.get( + new DescriptorPool.DescriptorIntPair(this, number)); + } + + private final int index; + private final EnumDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final Descriptor containingType; + private EnumValueDescriptor[] values; + + private EnumDescriptor(EnumDescriptorProto proto, + FileDescriptor file, + Descriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, parent, proto.getName()); + this.file = file; + this.containingType = parent; + + if (proto.getValueCount() == 0) { + // We cannot allow enums with no values because this would mean there + // would be no valid default value for fields of this type. + throw new DescriptorValidationException(this, + "Enums must contain at least one value."); + } + + values = new EnumValueDescriptor[proto.getValueCount()]; + for (int i = 0; i < proto.getValueCount(); i++) { + this.values[i] = new EnumValueDescriptor( + proto.getValue(i), file, this, i); + } + + file.pool.addSymbol(this); + } + } + + // ================================================================= + + /** + * Describes one value within an enum type. Note that multiple defined + * values may have the same number. In generated Java code, all values + * with the same number after the first become aliases of the first. + * However, they still have independent EnumValueDescriptors. + */ + public static final class EnumValueDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public EnumValueDescriptorProto toProto() { return proto; } + + /** Get the value's unqualified name. */ + public String getName() { return proto.getName(); } + + /** Get the value's number. */ + public int getNumber() { return proto.getNumber(); } + + /** + * Get the value's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the value's enum type. */ + public EnumDescriptor getType() { return type; } + + /** + * Get the {@code EnumValueOptions}, defined in {@code descriptor.proto}. + */ + public EnumValueOptions getOptions() { return proto.getOptions(); } + + private final int index; + private final EnumValueDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final EnumDescriptor type; + + private EnumValueDescriptor(EnumValueDescriptorProto proto, + FileDescriptor file, + EnumDescriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.file = file; + this.type = parent; + + this.fullName = parent.getFullName() + "." + proto.getName(); + + file.pool.addSymbol(this); + file.pool.addEnumValueByNumber(this); + } + } + + // ================================================================= + + /** Describes a service type. */ + public static final class ServiceDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public ServiceDescriptorProto toProto() { return proto; } + + /** Get the type's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the type's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the {@code ServiceOptions}, defined in {@code descriptor.proto}. */ + public ServiceOptions getOptions() { return proto.getOptions(); } + + /** Get a list of methods for this service. */ + public List getMethods() { + return Collections.unmodifiableList(Arrays.asList(methods)); + } + + /** + * Find a method by name. + * @param name The unqualified name of the method (e.g. "Foo"). + * @return the method's decsriptor, or {@code null} if not found. + */ + public MethodDescriptor findMethodByName(String name) { + GenericDescriptor result = file.pool.findSymbol(fullName + "." + name); + if (result != null && result instanceof MethodDescriptor) { + return (MethodDescriptor)result; + } else { + return null; + } + } + + private final int index; + private final ServiceDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private MethodDescriptor[] methods; + + private ServiceDescriptor(ServiceDescriptorProto proto, + FileDescriptor file, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.fullName = computeFullName(file, null, proto.getName()); + this.file = file; + + this.methods = new MethodDescriptor[proto.getMethodCount()]; + for (int i = 0; i < proto.getMethodCount(); i++) { + this.methods[i] = new MethodDescriptor( + proto.getMethod(i), file, this, i); + } + + file.pool.addSymbol(this); + } + + private void crossLink() throws DescriptorValidationException { + for (int i = 0; i < methods.length; i++) { + methods[i].crossLink(); + } + } + } + + // ================================================================= + + /** + * Describes one method within a service type. + */ + public static final class MethodDescriptor implements GenericDescriptor { + /** + * Get the index of this descriptor within its parent. + * * @see Descriptors.Descriptor#getIndex() + */ + public int getIndex() { return index; } + + /** Convert the descriptor to its protocol message representation. */ + public MethodDescriptorProto toProto() { return proto; } + + /** Get the method's unqualified name. */ + public String getName() { return proto.getName(); } + + /** + * Get the method's fully-qualified name. + * @see Descriptors.Descriptor#getFullName() + */ + public String getFullName() { return fullName; } + + /** Get the {@link FileDescriptor} containing this descriptor. */ + public FileDescriptor getFile() { return file; } + + /** Get the method's service type. */ + public ServiceDescriptor getService() { return service; } + + /** Get the method's input type. */ + public Descriptor getInputType() { return inputType; } + + /** Get the method's output type. */ + public Descriptor getOutputType() { return outputType; } + + /** + * Get the {@code MethodOptions}, defined in {@code descriptor.proto}. + */ + public MethodOptions getOptions() { return proto.getOptions(); } + + private final int index; + private final MethodDescriptorProto proto; + private final String fullName; + private final FileDescriptor file; + private final ServiceDescriptor service; + + // Initialized during cross-linking. + private Descriptor inputType; + private Descriptor outputType; + + private MethodDescriptor(MethodDescriptorProto proto, + FileDescriptor file, + ServiceDescriptor parent, + int index) + throws DescriptorValidationException { + this.index = index; + this.proto = proto; + this.file = file; + this.service = parent; + + this.fullName = parent.getFullName() + "." + proto.getName(); + + file.pool.addSymbol(this); + } + + private void crossLink() throws DescriptorValidationException { + GenericDescriptor inputType = + file.pool.lookupSymbol(proto.getInputType(), this); + if (!(inputType instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getInputType() + "\" is not a message type."); + } + this.inputType = (Descriptor)inputType; + + GenericDescriptor outputType = + file.pool.lookupSymbol(proto.getOutputType(), this); + if (!(outputType instanceof Descriptor)) { + throw new DescriptorValidationException(this, + "\"" + proto.getOutputType() + "\" is not a message type."); + } + this.outputType = (Descriptor)outputType; + } + } + + // ================================================================= + + private static String computeFullName(FileDescriptor file, + Descriptor parent, + String name) { + if (parent != null) { + return parent.getFullName() + "." + name; + } else if (file.getPackage().length() > 0) { + return file.getPackage() + "." + name; + } else { + return name; + } + } + + // ================================================================= + + /** + * All descriptors except {@code FileDescriptor} implement this to make + * {@code DescriptorPool}'s life easier. + */ + private static interface GenericDescriptor { + Message toProto(); + String getName(); + String getFullName(); + FileDescriptor getFile(); + } + + /** + * Thrown when building descriptors fails because the source DescriptorProtos + * are not valid. + */ + public static class DescriptorValidationException extends Exception { + /** Gets the full name of the descriptor where the error occurred. */ + public String getProblemSymbolName() { return name; } + + /** + * Gets the the protocol message representation of the invalid descriptor. + */ + public Message getProblemProto() { return proto; } + + /** + * Gets a human-readable description of the error. + */ + public String getDescription() { return description; } + + private final String name; + private final Message proto; + private final String description; + + private DescriptorValidationException(GenericDescriptor problemDescriptor, + String description) { + super(problemDescriptor.getFullName() + ": " + description); + + // Note that problemDescriptor may be partially uninitialized, so we + // don't want to expose it directly to the user. So, we only provide + // the name and the original proto. + this.name = problemDescriptor.getFullName(); + this.proto = problemDescriptor.toProto(); + this.description = description; + } + + private DescriptorValidationException(FileDescriptor problemDescriptor, + String description) { + super(problemDescriptor.getName() + ": " + description); + + // Note that problemDescriptor may be partially uninitialized, so we + // don't want to expose it directly to the user. So, we only provide + // the name and the original proto. + this.name = problemDescriptor.getName(); + this.proto = problemDescriptor.toProto(); + this.description = description; + } + } + + // ================================================================= + + /** + * A private helper class which contains lookup tables containing all the + * descriptors defined in a particular file. + */ + private static final class DescriptorPool { + DescriptorPool(FileDescriptor[] dependencies) { + this.dependencies = new DescriptorPool[dependencies.length]; + + for (int i = 0; i < dependencies.length; i++) { + this.dependencies[i] = dependencies[i].pool; + } + + for (int i = 0; i < dependencies.length; i++) { + try { + addPackage(dependencies[i].getPackage(), dependencies[i]); + } catch (DescriptorValidationException e) { + // Can't happen, because addPackage() only fails when the name + // conflicts with a non-package, but we have not yet added any + // non-packages at this point. + assert false; + } + } + } + + final DescriptorPool[] dependencies; + + final Map descriptorsByName = + new HashMap(); + final Map fieldsByNumber = + new HashMap(); + final Map enumValuesByNumber = + new HashMap(); + + /** Find a generic descriptor by fully-qualified name. */ + GenericDescriptor findSymbol(String fullName) { + GenericDescriptor result = descriptorsByName.get(fullName); + if (result != null) return result; + + for (int i = 0; i < dependencies.length; i++) { + result = dependencies[i].descriptorsByName.get(fullName); + if (result != null) return result; + } + + return null; + } + + /** + * Look up a descriptor by name, relative to some other descriptor. + * The name may be fully-qualified (with a leading '.'), + * partially-qualified, or unqualified. C++-like name lookup semantics + * are used to search for the matching descriptor. + */ + GenericDescriptor lookupSymbol(String name, + GenericDescriptor relativeTo) + throws DescriptorValidationException { + // TODO(kenton): This could be optimized in a number of ways. + + GenericDescriptor result; + if (name.startsWith(".")) { + // Fully-qualified name. + result = findSymbol(name.substring(1)); + } else { + // If "name" is a compound identifier, we want to search for the + // first component of it, then search within it for the rest. + int firstPartLength = name.indexOf('.'); + String firstPart; + if (firstPartLength == -1) { + firstPart = name; + } else { + firstPart = name.substring(0, firstPartLength); + } + + // We will search each parent scope of "relativeTo" looking for the + // symbol. + StringBuilder scopeToTry = new StringBuilder(relativeTo.getFullName()); + + while (true) { + // Chop off the last component of the scope. + int dotpos = scopeToTry.lastIndexOf("."); + if (dotpos == -1) { + result = findSymbol(name); + break; + } else { + scopeToTry.setLength(dotpos + 1); + + // Append firstPart and try to find. + scopeToTry.append(firstPart); + result = findSymbol(scopeToTry.toString()); + + if (result != null) { + if (firstPartLength != -1) { + // We only found the first part of the symbol. Now look for + // the whole thing. If this fails, we *don't* want to keep + // searching parent scopes. + scopeToTry.setLength(dotpos + 1); + scopeToTry.append(name); + result = findSymbol(scopeToTry.toString()); + } + break; + } + + // Not found. Remove the name so we can try again. + scopeToTry.setLength(dotpos); + } + } + } + + if (result == null) { + throw new DescriptorValidationException(relativeTo, + "\"" + name + "\" is not defined."); + } else { + return result; + } + } + + /** + * Adds a symbol to the symbol table. If a symbol with the same name + * already exists, throws an error. + */ + void addSymbol(GenericDescriptor descriptor) + throws DescriptorValidationException { + validateSymbolName(descriptor); + + String fullName = descriptor.getFullName(); + int dotpos = fullName.lastIndexOf('.'); + + GenericDescriptor old = descriptorsByName.put(fullName, descriptor); + if (old != null) { + descriptorsByName.put(fullName, old); + + if (descriptor.getFile() == old.getFile()) { + if (dotpos == -1) { + throw new DescriptorValidationException(descriptor, + "\"" + fullName + "\" is already defined."); + } else { + throw new DescriptorValidationException(descriptor, + "\"" + fullName.substring(dotpos + 1) + + "\" is already defined in \"" + + fullName.substring(0, dotpos) + "\"."); + } + } else { + throw new DescriptorValidationException(descriptor, + "\"" + fullName + "\" is already defined in file \"" + + old.getFile().getName() + "\"."); + } + } + } + + /** + * Represents a package in the symbol table. We use PackageDescriptors + * just as placeholders so that someone cannot define, say, a message type + * that has the same name as an existing package. + */ + static final class PackageDescriptor implements GenericDescriptor { + public Message toProto() { return file.toProto(); } + public String getName() { return name; } + public String getFullName() { return fullName; } + public FileDescriptor getFile() { return file; } + + PackageDescriptor(String name, String fullName, FileDescriptor file) { + this.file = file; + this.fullName = fullName; + this.name = name; + } + + String name; + String fullName; + FileDescriptor file; + } + + /** + * Adds a package to the symbol tables. If a package by the same name + * already exists, that is fine, but if some other kind of symbol exists + * under the same name, an exception is thrown. If the package has + * multiple components, this also adds the parent package(s). + */ + void addPackage(String fullName, FileDescriptor file) + throws DescriptorValidationException { + int dotpos = fullName.lastIndexOf('.'); + String name; + if (dotpos != -1) { + addPackage(fullName.substring(0, dotpos), file); + name = fullName.substring(dotpos + 1); + } else { + name = fullName; + } + + GenericDescriptor old = + descriptorsByName.put(fullName, + new PackageDescriptor(fullName, name, file)); + if (old != null) { + descriptorsByName.put(fullName, old); + if (!(old instanceof PackageDescriptor)) { + throw new DescriptorValidationException(file, + "\"" + name + "\" is already defined (as something other than a " + + "package) in file \"" + old.getFile().getName() + "\"."); + } + } + } + + /** A (GenericDescriptor, int) pair, used as a map key. */ + static final class DescriptorIntPair { + final GenericDescriptor descriptor; + final int number; + + DescriptorIntPair(GenericDescriptor descriptor, int number) { + this.descriptor = descriptor; + this.number = number; + } + + public int hashCode() { + return descriptor.hashCode() * ((1 << 16) - 1) + number; + } + public boolean equals(Object obj) { + if (!(obj instanceof DescriptorIntPair)) return false; + DescriptorIntPair other = (DescriptorIntPair)obj; + return descriptor == other.descriptor && number == other.number; + } + } + + /** + * Adds a field to the fieldsByNumber table. Throws an exception if a + * field with hte same containing type and number already exists. + */ + void addFieldByNumber(FieldDescriptor field) + throws DescriptorValidationException { + DescriptorIntPair key = + new DescriptorIntPair(field.getContainingType(), field.getNumber()); + FieldDescriptor old = fieldsByNumber.put(key, field); + if (old != null) { + fieldsByNumber.put(key, old); + throw new DescriptorValidationException(field, + "Field number " + field.getNumber() + + "has already been used in \"" + + field.getContainingType().getFullName() + + "\" by field \"" + old.getName() + "\"."); + } + } + + /** + * Adds an enum value to the enumValuesByNumber table. If an enum value + * with the same type and number already exists, does nothing. (This is + * allowed; the first value define with the number takes precedence.) + */ + void addEnumValueByNumber(EnumValueDescriptor value) { + DescriptorIntPair key = + new DescriptorIntPair(value.getType(), value.getNumber()); + EnumValueDescriptor old = enumValuesByNumber.put(key, value); + if (old != null) { + enumValuesByNumber.put(key, old); + // Not an error: Multiple enum values may have the same number, but + // we only want the first one in the map. + } + } + + /** + * Verifies that the descriptor's name is valid (i.e. it contains only + * letters, digits, and underscores, and does not start with a digit). + */ + void validateSymbolName(GenericDescriptor descriptor) + throws DescriptorValidationException { + String name = descriptor.getName(); + if (name.length() == 0) { + throw new DescriptorValidationException(descriptor, "Missing name."); + } else { + boolean valid = true; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + // Non-ASCII characters are not valid in protobuf identifiers, even + // if they are letters or digits. + if (c >= 128) { + valid = false; + } + // First character must be letter or _. Subsequent characters may + // be letters, numbers, or digits. + if (Character.isLetter(c) || c == '_' || + (Character.isDigit(c) && i > 0)) { + // Valid + } else { + valid = false; + } + } + if (!valid) { + throw new DescriptorValidationException(descriptor, + "\"" + name + "\" is not a valid identifier."); + } + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/DynamicMessage.java b/java/src/main/java/com/google/protobuf/DynamicMessage.java new file mode 100644 index 00000000..d9a39f0e --- /dev/null +++ b/java/src/main/java/com/google/protobuf/DynamicMessage.java @@ -0,0 +1,391 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.io.InputStream; +import java.io.IOException; +import java.util.Map; + +/** + * An implementation of {@link Message} that can represent arbitrary types, + * given a {@link Descriptors.Descriptor}. + * + * @author kenton@google.com Kenton Varda + */ +public final class DynamicMessage extends AbstractMessage { + private final Descriptor type; + private final FieldSet fields; + private final UnknownFieldSet unknownFields; + private int memoizedSize = -1; + + /** + * Construct a {@code DynamicMessage} using the given {@code FieldSet}. + */ + private DynamicMessage(Descriptor type, FieldSet fields, + UnknownFieldSet unknownFields) { + this.type = type; + this.fields = fields; + this.unknownFields = unknownFields; + } + + /** + * Get a {@code DynamicMessage} representing the default instance of the + * given type. + */ + public static DynamicMessage getDefaultInstance(Descriptor type) { + return new DynamicMessage(type, FieldSet.emptySet(), + UnknownFieldSet.getDefaultInstance()); + } + + /** Parse a message of the given type from the given input stream. */ + public static DynamicMessage parseFrom(Descriptor type, + CodedInputStream input) + throws IOException { + return newBuilder(type).mergeFrom(input).buildParsed(); + } + + /** Parse a message of the given type from the given input stream. */ + public static DynamicMessage parseFrom( + Descriptor type, + CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, ByteString data) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, byte[] data) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data).buildParsed(); + } + + /** Parse {@code data} as a message of the given type and return it. */ + public static DynamicMessage parseFrom(Descriptor type, byte[] data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException { + return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed(); + } + + /** Parse a message of the given type from {@code input} and return it. */ + public static DynamicMessage parseFrom(Descriptor type, InputStream input) + throws IOException { + return newBuilder(type).mergeFrom(input).buildParsed(); + } + + /** Parse a message of the given type from {@code input} and return it. */ + public static DynamicMessage parseFrom(Descriptor type, InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed(); + } + + /** Construct a {@link Message.Builder} for the given type. */ + public static Builder newBuilder(Descriptor type) { + return new Builder(type); + } + + /** + * Construct a {@link Message.Builder} for a message of the same type as + * {@code prototype}, and initialize it with {@code prototype}'s contents. + */ + public static Builder newBuilder(Message prototype) { + return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype); + } + + // ----------------------------------------------------------------- + // Implementation of Message interface. + + public Descriptor getDescriptorForType() { + return type; + } + + public DynamicMessage getDefaultInstanceForType() { + return getDefaultInstance(type); + } + + public Map getAllFields() { + return fields.getAllFields(); + } + + public boolean hasField(FieldDescriptor field) { + verifyContainingType(field); + return fields.hasField(field); + } + + public Object getField(FieldDescriptor field) { + verifyContainingType(field); + Object result = fields.getField(field); + if (result == null) { + result = getDefaultInstance(field.getMessageType()); + } + return result; + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + verifyContainingType(field); + return fields.getRepeatedFieldCount(field); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + verifyContainingType(field); + return fields.getRepeatedField(field, index); + } + + public UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + public boolean isInitialized() { + return fields.isInitialized(type); + } + + public void writeTo(CodedOutputStream output) throws IOException { + fields.writeTo(output); + if (type.getOptions().getMessageSetWireFormat()) { + unknownFields.writeAsMessageSetTo(output); + } else { + unknownFields.writeTo(output); + } + } + + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = fields.getSerializedSize(); + if (type.getOptions().getMessageSetWireFormat()) { + size += unknownFields.getSerializedSizeAsMessageSet(); + } else { + size += unknownFields.getSerializedSize(); + } + + memoizedSize = size; + return size; + } + + public Builder newBuilderForType() { + return new Builder(type); + } + + /** Verifies that the field is a field of this message. */ + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != type) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + + // ================================================================= + + /** + * Builder for {@link DynamicMessage}s. + */ + public static final class Builder extends AbstractMessage.Builder { + private final Descriptor type; + private FieldSet fields; + private UnknownFieldSet unknownFields; + + /** Construct a {@code Builder} for the given type. */ + private Builder(Descriptor type) { + this.type = type; + this.fields = FieldSet.newFieldSet(); + this.unknownFields = UnknownFieldSet.getDefaultInstance(); + } + + // --------------------------------------------------------------- + // Implementation of Message.Builder interface. + + public Builder clear() { + fields.clear(); + return this; + } + + public Builder mergeFrom(Message other) { + if (other.getDescriptorForType() != type) { + throw new IllegalArgumentException( + "mergeFrom(Message) can only merge messages of the same type."); + } + + fields.mergeFrom(other); + return this; + } + + public DynamicMessage build() { + if (!isInitialized()) { + throw new UninitializedMessageException( + new DynamicMessage(type, fields, unknownFields)); + } + return buildPartial(); + } + + /** + * Helper for DynamicMessage.parseFrom() methods to call. Throws + * {@link InvalidProtocolBufferException} instead of + * {@link UninitializedMessageException}. + */ + private DynamicMessage buildParsed() throws InvalidProtocolBufferException { + if (!isInitialized()) { + throw new UninitializedMessageException( + new DynamicMessage(type, fields, unknownFields)) + .asInvalidProtocolBufferException(); + } + return buildPartial(); + } + + public DynamicMessage buildPartial() { + fields.makeImmutable(); + DynamicMessage result = + new DynamicMessage(type, fields, unknownFields); + fields = null; + unknownFields = null; + return result; + } + + public Builder clone() { + Builder result = new Builder(type); + result.fields.mergeFrom(fields); + return result; + } + + public boolean isInitialized() { + return fields.isInitialized(type); + } + + public Builder mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException { + UnknownFieldSet.Builder unknownFieldsBuilder = + UnknownFieldSet.newBuilder(unknownFields); + fields.mergeFrom(input, unknownFieldsBuilder, extensionRegistry, this); + unknownFields = unknownFieldsBuilder.build(); + return this; + } + + public Descriptor getDescriptorForType() { + return type; + } + + public DynamicMessage getDefaultInstanceForType() { + return getDefaultInstance(type); + } + + public Map getAllFields() { + return fields.getAllFields(); + } + + public Builder newBuilderForField(FieldDescriptor field) { + verifyContainingType(field); + + if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "newBuilderForField is only valid for fields with message type."); + } + + return new Builder(field.getMessageType()); + } + + public boolean hasField(FieldDescriptor field) { + verifyContainingType(field); + return fields.hasField(field); + } + + public Object getField(FieldDescriptor field) { + verifyContainingType(field); + Object result = fields.getField(field); + if (result == null) { + result = getDefaultInstance(field.getMessageType()); + } + return result; + } + + public Builder setField(FieldDescriptor field, Object value) { + verifyContainingType(field); + fields.setField(field, value); + return this; + } + + public Builder clearField(FieldDescriptor field) { + verifyContainingType(field); + fields.clearField(field); + return this; + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + verifyContainingType(field); + return fields.getRepeatedFieldCount(field); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + verifyContainingType(field); + return fields.getRepeatedField(field, index); + } + + public Builder setRepeatedField(FieldDescriptor field, + int index, Object value) { + verifyContainingType(field); + fields.setRepeatedField(field, index, value); + return this; + } + + public Builder addRepeatedField(FieldDescriptor field, Object value) { + verifyContainingType(field); + fields.addRepeatedField(field, value); + return this; + } + + public UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + public Builder setUnknownFields(UnknownFieldSet unknownFields) { + this.unknownFields = unknownFields; + return this; + } + + public Builder mergeUnknownFields(UnknownFieldSet unknownFields) { + this.unknownFields = + UnknownFieldSet.newBuilder(this.unknownFields) + .mergeFrom(unknownFields) + .build(); + return this; + } + + /** Verifies that the field is a field of this message. */ + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != type) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/ExtensionRegistry.java b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java new file mode 100644 index 00000000..6954b3b6 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/ExtensionRegistry.java @@ -0,0 +1,237 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * A table of known extensions, searchable by name or field number. When + * parsing a protocol message that might have extensions, you must provide + * an {@code ExtensionRegistry} in which you have registered any extensions + * that you want to be able to parse. Otherwise, those extensions will just + * be treated like unknown fields. + * + *

For example, if you had the {@code .proto} file: + * + *

+ * option java_class = "MyProto";
+ *
+ * message Foo {
+ *   extensions 1000 to max;
+ * }
+ *
+ * extend Foo {
+ *   optional int32 bar;
+ * }
+ * 
+ * + * Then you might write code like: + * + *
+ * ExtensionRegistry registry = ExtensionRegistry.newInstance();
+ * registry.add(MyProto.bar);
+ * MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
+ * 
+ * + *

Background: + * + *

You might wonder why this is necessary. Two alternatives might come to + * mind. First, you might imagine a system where generated extensions are + * automatically registered when their containing classes are loaded. This + * is a popular technique, but is bad design; among other things, it creates a + * situation where behavior can change depending on what classes happen to be + * loaded. It also introduces a security vulnerability, because an + * unprivileged class could cause its code to be called unexpectedly from a + * privileged class by registering itself as an extension of the right type. + * + *

Another option you might consider is lazy parsing: do not parse an + * extension until it is first requested, at which point the caller must + * provide a type to use. This introduces a different set of problems. First, + * it would require a mutex lock any time an extension was accessed, which + * would be slow. Second, corrupt data would not be detected until first + * access, at which point it would be much harder to deal with it. Third, it + * could violate the expectation that message objects are immutable, since the + * type provided could be any arbitrary message class. An unpriviledged user + * could take advantage of this to inject a mutable object into a message + * belonging to priviledged code and create mischief. + * + * @author kenton@google.com Kenton Varda + */ +public final class ExtensionRegistry { + /** Construct a new, empty instance. */ + public static ExtensionRegistry newInstance() { + return new ExtensionRegistry( + new HashMap(), + new HashMap()); + } + + /** Get the unmodifiable singleton empty instance. */ + public static ExtensionRegistry getEmptyRegistry() { + return EMPTY; + } + + /** Returns an unmodifiable view of the registry. */ + public ExtensionRegistry getUnmodifiable() { + return new ExtensionRegistry( + Collections.unmodifiableMap(extensionsByName), + Collections.unmodifiableMap(extensionsByNumber)); + } + + /** A (Descriptor, Message) pair, returned by lookup methods. */ + public static final class ExtensionInfo { + /** The extension's descriptor. */ + public final FieldDescriptor descriptor; + + /** + * A default instance of the extension's type, if it has a message type. + * Otherwise, {@code null}. + */ + public final Message defaultInstance; + + private ExtensionInfo(FieldDescriptor descriptor) { + this.descriptor = descriptor; + this.defaultInstance = null; + } + private ExtensionInfo(FieldDescriptor descriptor, Message defaultInstance) { + this.descriptor = descriptor; + this.defaultInstance = defaultInstance; + } + } + + /** + * Find an extension by fully-qualified field name, in the proto namespace. + * I.e. {@code result.descriptor.fullName()} will match {@code fullName} if + * a match is found. + * + * @return Information about the extension if found, or {@code null} + * otherwise. + */ + public ExtensionInfo findExtensionByName(String fullName) { + return extensionsByName.get(fullName); + } + + /** + * Find an extension by containing type and field number. + * + * @return Information about the extension if found, or {@code null} + * otherwise. + */ + public ExtensionInfo findExtensionByNumber(Descriptor containingType, + int fieldNumber) { + return extensionsByNumber.get( + new DescriptorIntPair(containingType, fieldNumber)); + } + + /** Add an extension from a generated file to the registry. */ + public void add(GeneratedMessage.GeneratedExtension extension) { + if (extension.getDescriptor().getJavaType() == + FieldDescriptor.JavaType.MESSAGE) { + add(new ExtensionInfo(extension.getDescriptor(), + extension.getMessageDefaultInstance())); + } else { + add(new ExtensionInfo(extension.getDescriptor(), null)); + } + } + + /** Add a non-message-type extension to the registry by descriptor. */ + public void add(FieldDescriptor type) { + if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() must be provided a default instance when " + + "adding an embedded message extension."); + } + add(new ExtensionInfo(type, null)); + } + + /** Add a message-type extension to the registry by descriptor. */ + public void add(FieldDescriptor type, Message defaultInstance) { + if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() provided a default instance for a " + + "non-message extension."); + } + add(new ExtensionInfo(type, defaultInstance)); + } + + // ================================================================= + // Private stuff. + + private ExtensionRegistry( + Map extensionsByName, + Map extensionsByNumber) { + this.extensionsByName = extensionsByName; + this.extensionsByNumber = extensionsByNumber; + } + + private final Map extensionsByName; + private final Map extensionsByNumber; + + private static final ExtensionRegistry EMPTY = + new ExtensionRegistry( + Collections.emptyMap(), + Collections.emptyMap()); + + private void add(ExtensionInfo extension) { + if (!extension.descriptor.isExtension()) { + throw new IllegalArgumentException( + "ExtensionRegistry.add() was given a FieldDescriptor for a regular " + + "(non-extension) field."); + } + + extensionsByName.put(extension.descriptor.getFullName(), extension); + extensionsByNumber.put( + new DescriptorIntPair(extension.descriptor.getContainingType(), + extension.descriptor.getNumber()), + extension); + + FieldDescriptor field = extension.descriptor; + if (field.getContainingType().getOptions().getMessageSetWireFormat() && + field.getType() == FieldDescriptor.Type.MESSAGE && + field.isOptional() && + field.getExtensionScope() == field.getMessageType()) { + // This is an extension of a MessageSet type defined within the extension + // type's own scope. For backwards-compatibility, allow it to be looked + // up by type name. + extensionsByName.put(field.getMessageType().getFullName(), extension); + } + } + + /** A (GenericDescriptor, int) pair, used as a map key. */ + private static final class DescriptorIntPair { + final Descriptor descriptor; + final int number; + + DescriptorIntPair(Descriptor descriptor, int number) { + this.descriptor = descriptor; + this.number = number; + } + + public int hashCode() { + return descriptor.hashCode() * ((1 << 16) - 1) + number; + } + public boolean equals(Object obj) { + if (!(obj instanceof DescriptorIntPair)) return false; + DescriptorIntPair other = (DescriptorIntPair)obj; + return descriptor == other.descriptor && number == other.number; + } + } +} diff --git a/java/src/main/java/com/google/protobuf/FieldSet.java b/java/src/main/java/com/google/protobuf/FieldSet.java new file mode 100644 index 00000000..3a5dc488 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/FieldSet.java @@ -0,0 +1,662 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.TreeMap; +import java.util.List; +import java.util.Map; + +/** + * A class which represents an arbitrary set of fields of some message type. + * This is used to implement {@link DynamicMessage}, and also to represent + * extensions in {@link GeneratedMessage}. This class is package-private, + * since outside users should probably be using {@link DynamicMessage}. + * + * @author kenton@google.com Kenton Varda + */ +final class FieldSet { + private Map fields; + + /** Construct a new FieldSet. */ + private FieldSet() { + // Use a TreeMap because fields need to be in canonical order when + // serializing. + this.fields = new TreeMap(); + } + + /** + * Construct a new FieldSet with the given map. This is only used by + * DEFAULT_INSTANCE, to pass in an immutable empty map. + */ + private FieldSet(Map fields) { + this.fields = fields; + } + + /** Construct a new FieldSet. */ + public static FieldSet newFieldSet() { + return new FieldSet(); + } + + /** Get an immutable empty FieldSet. */ + public static FieldSet emptySet() { + return DEFAULT_INSTANCE; + } + private static final FieldSet DEFAULT_INSTANCE = + new FieldSet(Collections.emptyMap()); + + /** Make this FieldSet immutable from this point forward. */ + @SuppressWarnings("unchecked") + public void makeImmutable() { + for (Map.Entry entry: fields.entrySet()) { + if (entry.getKey().isRepeated()) { + List value = (List)entry.getValue(); + entry.setValue(Collections.unmodifiableList(value)); + } + } + fields = Collections.unmodifiableMap(fields); + } + + // ================================================================= + + /** See {@link Message.Builder#clear()}. */ + public void clear() { + fields.clear(); + } + + /** See {@link Message#getAllFields()}. */ + public Map getAllFields() { + return Collections.unmodifiableMap(fields); + } + + /** + * Get an interator to the field map. This iterator should not be leaked + * out of the protobuf library as it is not protected from mutation. + */ + public Iterator> iterator() { + return fields.entrySet().iterator(); + } + + /** See {@link Message#hasField(Descriptors.FieldDescriptor)}. */ + public boolean hasField(Descriptors.FieldDescriptor field) { + if (field.isRepeated()) { + throw new IllegalArgumentException( + "hasField() can only be called on non-repeated fields."); + } + + return fields.containsKey(field); + } + + /** + * See {@link Message#getField(Descriptors.FieldDescriptor)}. This method + * returns {@code null} if the field is a singular message type and is not + * set; in this case it is up to the caller to fetch the message's default + * instance. + */ + public Object getField(Descriptors.FieldDescriptor field) { + Object result = fields.get(field); + if (result == null) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + return Collections.emptyList(); + } else { + return null; + } + } else { + return field.getDefaultValue(); + } + } else { + return result; + } + } + + /** See {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}. */ + @SuppressWarnings("unchecked") + public void setField(Descriptors.FieldDescriptor field, Object value) { + if (field.isRepeated()) { + if (!(value instanceof List)) { + throw new IllegalArgumentException( + "Wrong object type used with protocol message reflection."); + } + + // Wrap the contents in a new list so that the caller cannot change + // the list's contents after setting it. + List newList = new ArrayList(); + newList.addAll((List)value); + for (Object element : newList) { + verifyType(field, element); + } + value = newList; + } else { + verifyType(field, value); + } + + fields.put(field, value); + } + + /** See {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}. */ + public void clearField(Descriptors.FieldDescriptor field) { + fields.remove(field); + } + + /** See {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}. */ + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "getRepeatedFieldCount() can only be called on repeated fields."); + } + + return ((List)getField(field)).size(); + } + + /** See {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}. */ + public Object getRepeatedField(Descriptors.FieldDescriptor field, int index) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "getRepeatedField() can only be called on repeated fields."); + } + + return ((List)getField(field)).get(index); + } + + /** See {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}. */ + @SuppressWarnings("unchecked") + public void setRepeatedField(Descriptors.FieldDescriptor field, int index, + Object value) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "setRepeatedField() can only be called on repeated fields."); + } + + verifyType(field, value); + + List list = (List)fields.get(field); + if (list == null) { + throw new IndexOutOfBoundsException(); + } + + list.set(index, value); + } + + /** See {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}. */ + @SuppressWarnings("unchecked") + public void addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + if (!field.isRepeated()) { + throw new IllegalArgumentException( + "setRepeatedField() can only be called on repeated fields."); + } + + verifyType(field, value); + + List list = (List)fields.get(field); + if (list == null) { + list = new ArrayList(); + fields.put(field, list); + } + + list.add(value); + } + + /** + * Verifies that the given object is of the correct type to be a valid + * value for the given field. (For repeated fields, this checks if the + * object is the right type to be one element of the field.) + * + * @throws IllegalArgumentException The value is not of the right type. + */ + private void verifyType(FieldDescriptor field, Object value) { + boolean isValid = false; + switch (field.getJavaType()) { + case INT: isValid = value instanceof Integer ; break; + case LONG: isValid = value instanceof Long ; break; + case FLOAT: isValid = value instanceof Float ; break; + case DOUBLE: isValid = value instanceof Double ; break; + case BOOLEAN: isValid = value instanceof Boolean ; break; + case STRING: isValid = value instanceof String ; break; + case BYTE_STRING: isValid = value instanceof ByteString; break; + case ENUM: + isValid = value instanceof EnumValueDescriptor && + ((EnumValueDescriptor)value).getType() == field.getEnumType(); + break; + case MESSAGE: + isValid = value instanceof Message && + ((Message)value).getDescriptorForType() == field.getMessageType(); + break; + } + + if (!isValid) { + // When chaining calls to setField(), it can be hard to tell from + // the stack trace which exact call failed, since the whole chain is + // considered one line of code. So, let's make sure to include the + // field name and other useful info in the exception. + throw new IllegalArgumentException( + "Wrong object type used with protocol message reflection. " + + "Message type \"" + field.getContainingType().getFullName() + + "\", field \"" + + (field.isExtension() ? field.getFullName() : field.getName()) + + "\", value was type \"" + value.getClass().getName() + "\"."); + } + } + + // ================================================================= + // Parsing and serialization + + /** + * See {@link Message#isInitialized()}. Note: Since {@code FieldSet} + * itself does not have any way of knowing about required fields that + * aren't actually present in the set, it is up to the caller to check + * that all required fields are present. + */ + @SuppressWarnings("unchecked") + public boolean isInitialized() { + for (Map.Entry entry : fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + for (Message element : (List) entry.getValue()) { + if (!element.isInitialized()) { + return false; + } + } + } else { + if (!((Message) entry.getValue()).isInitialized()) { + return false; + } + } + } + } + + return true; + } + + /** + * Like {@link #isInitialized()}, but also checks for the presence of + * all required fields in the given type. + */ + @SuppressWarnings("unchecked") + public boolean isInitialized(Descriptor type) { + // Check that all required fields are present. + for (FieldDescriptor field : type.getFields()) { + if (field.isRequired()) { + if (!hasField(field)) { + return false; + } + } + } + + // Check that embedded messages are initialized. + return isInitialized(); + } + + /** See {@link Message.Builder#mergeFrom(Message)}. */ + @SuppressWarnings("unchecked") + public void mergeFrom(Message other) { + // Note: We don't attempt to verify that other's fields have valid + // types. Doing so would be a losing battle. We'd have to verify + // all sub-messages as well, and we'd have to make copies of all of + // them to insure that they don't change after verification (since + // the Message interface itself cannot enforce immutability of + // implementations). + // TODO(kenton): Provide a function somewhere called makeDeepCopy() + // which allows people to make secure deep copies of messages. + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + List existingValue = (List)fields.get(field); + if (existingValue == null) { + existingValue = new ArrayList(); + fields.put(field, existingValue); + } + existingValue.addAll((List)entry.getValue()); + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)fields.get(field); + if (existingValue == null) { + setField(field, entry.getValue()); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)entry.getValue()) + .build()); + } + } else { + setField(field, entry.getValue()); + } + } + } + + /** + * Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}. + */ + @SuppressWarnings("unchecked") + public void mergeFrom(FieldSet other) { + for (Map.Entry entry : other.fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.isRepeated()) { + List existingValue = (List)fields.get(field); + if (existingValue == null) { + existingValue = new ArrayList(); + fields.put(field, existingValue); + } + existingValue.addAll((List)value); + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + Message existingValue = (Message)fields.get(field); + if (existingValue == null) { + setField(field, value); + } else { + setField(field, + existingValue.newBuilderForType() + .mergeFrom(existingValue) + .mergeFrom((Message)value) + .build()); + } + } else { + setField(field, value); + } + } + } + + // TODO(kenton): Move parsing code into AbstractMessage, since it no longer + // uses any special knowledge from FieldSet. + + /** + * See {@link Message.Builder#mergeFrom(CodedInputStream)}. + * @param builder The {@code Builder} for the target message. + */ + public static void mergeFrom(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws java.io.IOException { + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (!mergeFieldFrom(input, unknownFields, extensionRegistry, + builder, tag)) { + // end group tag + break; + } + } + } + + /** + * Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder, + * ExtensionRegistry, Message.Builder)}, but parses a single field. + * @param tag The tag, which should have already been read. + * @return {@code true} unless the tag is an end-group tag. + */ + @SuppressWarnings("unchecked") + public static boolean mergeFieldFrom( + CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder, + int tag) throws java.io.IOException { + Descriptor type = builder.getDescriptorForType(); + + if (type.getOptions().getMessageSetWireFormat() && + tag == WireFormat.MESSAGE_SET_ITEM_TAG) { + mergeMessageSetExtensionFromCodedStream( + input, unknownFields, extensionRegistry, builder); + return true; + } + + int wireType = WireFormat.getTagWireType(tag); + int fieldNumber = WireFormat.getTagFieldNumber(tag); + + FieldDescriptor field; + Message defaultInstance = null; + + if (type.isExtensionNumber(fieldNumber)) { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(type, fieldNumber); + if (extension == null) { + field = null; + } else { + field = extension.descriptor; + defaultInstance = extension.defaultInstance; + } + } else { + field = type.findFieldByNumber(fieldNumber); + } + + if (field == null || + wireType != WireFormat.getWireFormatForFieldType(field.getType())) { + // Unknown field or wrong wire type. Skip. + return unknownFields.mergeFieldFrom(tag, input); + } else { + Object value; + switch (field.getType()) { + case GROUP: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readGroup(field.getNumber(), subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + case MESSAGE: { + Message.Builder subBuilder; + if (defaultInstance != null) { + subBuilder = defaultInstance.newBuilderForType(); + } else { + subBuilder = builder.newBuilderForField(field); + } + if (!field.isRepeated()) { + subBuilder.mergeFrom((Message) builder.getField(field)); + } + input.readMessage(subBuilder, extensionRegistry); + value = subBuilder.build(); + break; + } + case ENUM: { + int rawValue = input.readEnum(); + value = field.getEnumType().findValueByNumber(rawValue); + // If the number isn't recognized as a valid value for this enum, + // drop it. + if (value == null) { + unknownFields.mergeVarintField(fieldNumber, rawValue); + return true; + } + break; + } + default: + value = input.readPrimitiveField(field.getType()); + break; + } + + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } + } + + return true; + } + + /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */ + private static void mergeMessageSetExtensionFromCodedStream( + CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + Message.Builder builder) throws java.io.IOException { + Descriptor type = builder.getDescriptorForType(); + + // The wire format for MessageSet is: + // message MessageSet { + // repeated group Item = 1 { + // required int32 typeId = 2; + // required bytes message = 3; + // } + // } + // "typeId" is the extension's field number. The extension can only be + // a message type, where "message" contains the encoded bytes of that + // message. + // + // In practice, we will probably never see a MessageSet item in which + // the message appears before the type ID, or where either field does not + // appear exactly once. However, in theory such cases are valid, so we + // should be prepared to accept them. + + int typeId = 0; + ByteString rawBytes = null; // If we encounter "message" before "typeId" + Message.Builder subBuilder = null; + FieldDescriptor field = null; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) { + typeId = input.readUInt32(); + // Zero is not a valid type ID. + if (typeId != 0) { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(type, typeId); + if (extension != null) { + field = extension.descriptor; + subBuilder = extension.defaultInstance.newBuilderForType(); + Message originalMessage = (Message)builder.getField(field); + if (originalMessage != null) { + subBuilder.mergeFrom(originalMessage); + } + if (rawBytes != null) { + // We already encountered the message. Parse it now. + subBuilder.mergeFrom( + CodedInputStream.newInstance(rawBytes.newInput())); + rawBytes = null; + } + } else { + // Unknown extension number. If we already saw data, put it + // in rawBytes. + if (rawBytes != null) { + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(rawBytes) + .build()); + rawBytes = null; + } + } + } + } else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) { + if (typeId == 0) { + // We haven't seen a type ID yet, so we have to store the raw bytes + // for now. + rawBytes = input.readBytes(); + } else if (subBuilder == null) { + // We don't know how to parse this. Ignore it. + unknownFields.mergeField(typeId, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(input.readBytes()) + .build()); + } else { + // We already know the type, so we can parse directly from the input + // with no copying. Hooray! + input.readMessage(subBuilder, extensionRegistry); + } + } else { + // Unknown tag. Skip it. + if (!input.skipField(tag)) { + break; // end of group + } + } + } + + input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG); + + if (subBuilder != null) { + builder.setField(field, subBuilder.build()); + } + } + + /** See {@link Message#writeTo(CodedOutputStream)}. */ + public void writeTo(CodedOutputStream output) + throws java.io.IOException { + for (Map.Entry entry : fields.entrySet()) { + writeField(entry.getKey(), entry.getValue(), output); + } + } + + /** Write a single field. */ + public void writeField(FieldDescriptor field, Object value, + CodedOutputStream output) throws java.io.IOException { + if (field.isExtension() && + field.getContainingType().getOptions().getMessageSetWireFormat()) { + output.writeMessageSetExtension(field.getNumber(), (Message)value); + } else { + if (field.isRepeated()) { + for (Object element : (List)value) { + output.writeField(field.getType(), field.getNumber(), element); + } + } else { + output.writeField(field.getType(), field.getNumber(), value); + } + } + } + + /** + * See {@link Message#getSerializedSize()}. It's up to the caller to cache + * the resulting size if desired. + */ + public int getSerializedSize() { + int size = 0; + for (Map.Entry entry : fields.entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.isExtension() && + field.getContainingType().getOptions().getMessageSetWireFormat()) { + size += CodedOutputStream.computeMessageSetExtensionSize( + field.getNumber(), (Message)value); + } else { + if (field.isRepeated()) { + for (Object element : (List)value) { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), element); + } + } else { + size += CodedOutputStream.computeFieldSize( + field.getType(), field.getNumber(), value); + } + } + } + return size; + } +} diff --git a/java/src/main/java/com/google/protobuf/GeneratedMessage.java b/java/src/main/java/com/google/protobuf/GeneratedMessage.java new file mode 100644 index 00000000..957965b7 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/GeneratedMessage.java @@ -0,0 +1,1219 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * All generated protocol message classes extend this class. This class + * implements most of the Message and Builder interfaces using Java reflection. + * Users can ignore this class and pretend that generated messages implement + * the Message interface directly. + * + * @author kenton@google.com Kenton Varda + */ +public abstract class GeneratedMessage extends AbstractMessage { + protected GeneratedMessage() {} + + private UnknownFieldSet unknownFields = UnknownFieldSet.getDefaultInstance(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + protected abstract FieldAccessorTable internalGetFieldAccessorTable(); + + public Descriptor getDescriptorForType() { + return internalGetFieldAccessorTable().descriptor; + } + + /** Internal helper which returns a mutable map. */ + private final Map getAllFieldsMutable() { + TreeMap result = + new TreeMap(); + Descriptor descriptor = internalGetFieldAccessorTable().descriptor; + for (FieldDescriptor field : descriptor.getFields()) { + if (field.isRepeated()) { + List value = (List)getField(field); + if (!value.isEmpty()) { + result.put(field, value); + } + } else { + if (hasField(field)) { + result.put(field, getField(field)); + } + } + } + return result; + } + + public Map getAllFields() { + return Collections.unmodifiableMap(getAllFieldsMutable()); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).has(this); + } + + public Object getField(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).get(this); + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field) + .getRepeatedCount(this); + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + return internalGetFieldAccessorTable().getField(field) + .getRepeated(this, index); + } + + public final UnknownFieldSet getUnknownFields() { + return unknownFields; + } + + @SuppressWarnings("unchecked") + public abstract static class Builder + extends AbstractMessage.Builder { + protected Builder() {} + + /** + * Get the message being built. We don't just pass this to the + * constructor because it becomes null when build() is called. + */ + protected abstract GeneratedMessage internalGetResult(); + + /** + * Get the FieldAccessorTable for this type. We can't have the message + * class pass this in to the constructor because of bootstrapping trouble + * with DescriptorProtos. + */ + private FieldAccessorTable internalGetFieldAccessorTable() { + return internalGetResult().internalGetFieldAccessorTable(); + } + + public BuilderType mergeFrom(Message other) { + if (other.getDescriptorForType() != + internalGetFieldAccessorTable().descriptor) { + throw new IllegalArgumentException("Message type mismatch."); + } + + for (Map.Entry entry : + other.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + if (field.isRepeated()) { + // Concatenate repeated fields. + for (Object element : (List) entry.getValue()) { + addRepeatedField(field, element); + } + } else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE && + hasField(field)) { + // Merge singular embedded messages. + Message oldValue = (Message) getField(field); + setField(field, + oldValue.newBuilderForType() + .mergeFrom(oldValue) + .mergeFrom((Message) entry.getValue()) + .buildPartial()); + } else { + // Just overwrite. + setField(field, entry.getValue()); + } + } + return (BuilderType)this; + } + + public Descriptor getDescriptorForType() { + return internalGetFieldAccessorTable().descriptor; + } + + public Map getAllFields() { + return internalGetResult().getAllFields(); + } + + public Message.Builder newBuilderForField( + Descriptors.FieldDescriptor field) { + return internalGetFieldAccessorTable().getField(field).newBuilder(); + } + + public boolean hasField(Descriptors.FieldDescriptor field) { + return internalGetResult().hasField(field); + } + + public Object getField(Descriptors.FieldDescriptor field) { + if (field.isRepeated()) { + // The underlying list object is still modifiable at this point. + // Make sure not to expose the modifiable list to the caller. + return Collections.unmodifiableList( + (List)internalGetResult().getField(field)); + } else { + return internalGetResult().getField(field); + } + } + + public BuilderType setField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).set(this, value); + return (BuilderType)this; + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + internalGetFieldAccessorTable().getField(field).clear(this); + return (BuilderType)this; + } + + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return internalGetResult().getRepeatedFieldCount(field); + } + + public Object getRepeatedField(Descriptors.FieldDescriptor field, + int index) { + return internalGetResult().getRepeatedField(field, index); + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + internalGetFieldAccessorTable().getField(field) + .setRepeated(this, index, value); + return (BuilderType)this; + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + internalGetFieldAccessorTable().getField(field).addRepeated(this, value); + return (BuilderType)this; + } + + public final UnknownFieldSet getUnknownFields() { + return internalGetResult().unknownFields; + } + + public final BuilderType setUnknownFields(UnknownFieldSet unknownFields) { + internalGetResult().unknownFields = unknownFields; + return (BuilderType)this; + } + + public final BuilderType mergeUnknownFields(UnknownFieldSet unknownFields) { + GeneratedMessage result = internalGetResult(); + result.unknownFields = + UnknownFieldSet.newBuilder(result.unknownFields) + .mergeFrom(unknownFields) + .build(); + return (BuilderType)this; + } + + public boolean isInitialized() { + return internalGetResult().isInitialized(); + } + + /** + * Called by subclasses to parse an unknown field. + * @return {@code true} unless the tag is an end-group tag. + */ + protected boolean parseUnknownField(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + int tag) + throws IOException { + return unknownFields.mergeFieldFrom(tag, input); + } + + protected void addAll(Iterable values, Collection list) { + if (values instanceof Collection) { + @SuppressWarnings("unsafe") + Collection collection = (Collection) values; + list.addAll(collection); + } else { + for (T value : values) { + list.add(value); + } + } + } + } + + // ================================================================= + // Extensions-related stuff + + /** + * Generated message classes for message types that contain extension ranges + * subclass this. + * + *

This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "has", "get", and "getCount" -- but for extensions. The extensions + * are identified using instances of the class {@link GeneratedExtension}; + * the protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then you might write code like: + * + *

+   * MyProto.Foo foo = getFoo();
+   * int i = foo.getExtension(MyProto.bar);
+   * 
+ * + *

See also {@link ExtendableBuilder}. + */ + public abstract static class ExtendableMessage< + MessageType extends ExtendableMessage> + extends GeneratedMessage { + protected ExtendableMessage() {} + private final FieldSet extensions = FieldSet.newFieldSet(); + + private final void verifyExtensionContainingType( + GeneratedExtension extension) { + if (extension.getDescriptor().getContainingType() != + getDescriptorForType()) { + // This can only happen if someone uses unchecked operations. + throw new IllegalArgumentException( + "Extension is for type \"" + + extension.getDescriptor().getContainingType().getFullName() + + "\" which does not match message type \"" + + getDescriptorForType().getFullName() + "\"."); + } + } + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + GeneratedExtension extension) { + verifyExtensionContainingType(extension); + return extensions.hasField(extension.getDescriptor()); + } + + /** Get the number of elements in a repeated extension. */ + public final int getExtensionCount( + GeneratedExtension> extension) { + verifyExtensionContainingType(extension); + return extensions.getRepeatedFieldCount(extension.getDescriptor()); + } + + /** Get the value of an extension. */ + @SuppressWarnings("unchecked") + public final Type getExtension( + GeneratedExtension extension) { + verifyExtensionContainingType(extension); + Object value = extensions.getField(extension.getDescriptor()); + if (value == null) { + return (Type)extension.getMessageDefaultInstance(); + } else { + return (Type)extension.fromReflectionType(value); + } + } + + /** Get one element of a repeated extension. */ + @SuppressWarnings("unchecked") + public final Type getExtension( + GeneratedExtension> extension, int index) { + verifyExtensionContainingType(extension); + return (Type)extension.singularFromReflectionType( + extensions.getRepeatedField(extension.getDescriptor(), index)); + } + + /** Called by subclasses to check if all extensions are initialized. */ + protected boolean extensionsAreInitialized() { + return extensions.isInitialized(); + } + + /** + * Used by subclasses to serialize extensions. Extension ranges may be + * interleaved with field numbers, but we must write them in canonical + * (sorted by field number) order. ExtensionWriter helps us write + * individual ranges of extensions at once. + */ + protected class ExtensionWriter { + // Imagine how much simpler this code would be if Java iterators had + // a way to get the next element without advancing the iterator. + + final Iterator> iter = + extensions.iterator(); + Map.Entry next = null; + + private ExtensionWriter() { + if (iter.hasNext()) { + next = iter.next(); + } + } + + public void writeUntil(int end, CodedOutputStream output) + throws IOException { + while (next != null && next.getKey().getNumber() < end) { + extensions.writeField(next.getKey(), next.getValue(), output); + if (iter.hasNext()) { + next = iter.next(); + } else { + next = null; + } + } + } + } + + protected ExtensionWriter newExtensionWriter() { + return new ExtensionWriter(); + } + + /** Called by subclasses to compute the size of extensions. */ + protected int extensionsSerializedSize() { + return extensions.getSerializedSize(); + } + + // --------------------------------------------------------------- + // Reflection + + public Map getAllFields() { + Map result = super.getAllFieldsMutable(); + result.putAll(extensions.getAllFields()); + return Collections.unmodifiableMap(result); + } + + public boolean hasField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.hasField(field); + } else { + return super.hasField(field); + } + } + + public Object getField(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + Object value = extensions.getField(field); + if (value == null) { + // Lacking an ExtensionRegistry, we have no way to determine the + // extension's real type, so we return a DynamicMessage. + return DynamicMessage.getDefaultInstance(field.getMessageType()); + } else { + return value; + } + } else { + return super.getField(field); + } + } + + public int getRepeatedFieldCount(FieldDescriptor field) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedFieldCount(field); + } else { + return super.getRepeatedFieldCount(field); + } + } + + public Object getRepeatedField(FieldDescriptor field, int index) { + if (field.isExtension()) { + verifyContainingType(field); + return extensions.getRepeatedField(field, index); + } else { + return super.getRepeatedField(field, index); + } + } + + private void verifyContainingType(FieldDescriptor field) { + if (field.getContainingType() != getDescriptorForType()) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } + } + } + + /** + * Generated message builders for message types that contain extension ranges + * subclass this. + * + *

This class implements type-safe accessors for extensions. They + * implement all the same operations that you can do with normal fields -- + * e.g. "get", "set", and "add" -- but for extensions. The extensions are + * identified using instances of the class {@link GeneratedExtension}; the + * protocol compiler generates a static instance of this class for every + * extension in its input. Through the magic of generics, all is made + * type-safe. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then you might write code like: + * + *

+   * MyProto.Foo foo =
+   *   MyProto.Foo.newBuilder()
+   *     .setExtension(MyProto.bar, 123)
+   *     .build();
+   * 
+ * + *

See also {@link ExtendableMessage}. + */ + @SuppressWarnings("unchecked") + public abstract static class ExtendableBuilder< + MessageType extends ExtendableMessage, + BuilderType extends ExtendableBuilder> + extends GeneratedMessage.Builder { + protected ExtendableBuilder() {} + protected abstract ExtendableMessage internalGetResult(); + + /** Check if a singular extension is present. */ + public final boolean hasExtension( + GeneratedExtension extension) { + return internalGetResult().hasExtension(extension); + } + + /** Get the number of elements in a repeated extension. */ + public final int getExtensionCount( + GeneratedExtension> extension) { + return internalGetResult().getExtensionCount(extension); + } + + /** Get the value of an extension. */ + public final Type getExtension( + GeneratedExtension extension) { + return internalGetResult().getExtension(extension); + } + + /** Get one element of a repeated extension. */ + public final Type getExtension( + GeneratedExtension> extension, int index) { + return internalGetResult().getExtension(extension, index); + } + + /** Set the value of an extension. */ + public final BuilderType setExtension( + GeneratedExtension extension, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setField(extension.getDescriptor(), + extension.toReflectionType(value)); + return (BuilderType)this; + } + + /** Set the value of one element of a repeated extension. */ + public final BuilderType setExtension( + GeneratedExtension> extension, + int index, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.setRepeatedField( + extension.getDescriptor(), index, + extension.singularToReflectionType(value)); + return (BuilderType)this; + } + + /** Append a value to a repeated extension. */ + public final BuilderType addExtension( + GeneratedExtension> extension, Type value) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.addRepeatedField( + extension.getDescriptor(), extension.singularToReflectionType(value)); + return (BuilderType)this; + } + + /** Clear an extension. */ + public final BuilderType clearExtension( + GeneratedExtension extension) { + ExtendableMessage message = internalGetResult(); + message.verifyExtensionContainingType(extension); + message.extensions.clearField(extension.getDescriptor()); + return (BuilderType)this; + } + + /** + * Called by subclasses to parse an unknown field or an extension. + * @return {@code true} unless the tag is an end-group tag. + */ + protected boolean parseUnknownField(CodedInputStream input, + UnknownFieldSet.Builder unknownFields, + ExtensionRegistry extensionRegistry, + int tag) + throws IOException { + ExtendableMessage message = internalGetResult(); + return message.extensions.mergeFieldFrom( + input, unknownFields, extensionRegistry, this, tag); + } + + // --------------------------------------------------------------- + // Reflection + + // We don't have to override the get*() methods here because they already + // just forward to the underlying message. + + public BuilderType setField(FieldDescriptor field, Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setField(field, value); + return (BuilderType)this; + } else { + return super.setField(field, value); + } + } + + public BuilderType clearField(Descriptors.FieldDescriptor field) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.clearField(field); + return (BuilderType)this; + } else { + return super.clearField(field); + } + } + + public BuilderType setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.setRepeatedField(field, index, value); + return (BuilderType)this; + } else { + return super.setRepeatedField(field, index, value); + } + } + + public BuilderType addRepeatedField(Descriptors.FieldDescriptor field, + Object value) { + if (field.isExtension()) { + ExtendableMessage message = internalGetResult(); + message.verifyContainingType(field); + message.extensions.addRepeatedField(field, value); + return (BuilderType)this; + } else { + return super.addRepeatedField(field, value); + } + } + } + + // ----------------------------------------------------------------- + + /** For use by generated code only. */ + public static + GeneratedExtension + newGeneratedExtension(FieldDescriptor descriptor, Class type) { + if (descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newRepeatedGeneratedExtension() for repeated types."); + } + return new GeneratedExtension(descriptor, type); + } + + /** For use by generated code only. */ + public static + GeneratedExtension> + newRepeatedGeneratedExtension( + FieldDescriptor descriptor, Class type) { + if (!descriptor.isRepeated()) { + throw new IllegalArgumentException( + "Must call newGeneratedExtension() for non-repeated types."); + } + return new GeneratedExtension>(descriptor, type); + } + + /** + * Type used to represent generated extensions. The protocol compiler + * generates a static singleton instance of this class for each extension. + * + *

For example, imagine you have the {@code .proto} file: + * + *

+   * option java_class = "MyProto";
+   *
+   * message Foo {
+   *   extensions 1000 to max;
+   * }
+   *
+   * extend Foo {
+   *   optional int32 bar;
+   * }
+   * 
+ * + *

Then, {@code MyProto.Foo.bar} has type + * {@code GeneratedExtension}. + * + *

In general, users should ignore the details of this type, and simply use + * these static singletons as parameters to the extension accessors defined + * in {@link ExtendableMessage} and {@link ExtendableBuilder}. + */ + public static final class GeneratedExtension< + ContainingType extends Message, Type> { + // TODO(kenton): Find ways to avoid using Java reflection within this + // class. Also try to avoid suppressing unchecked warnings. + + private GeneratedExtension(FieldDescriptor descriptor, Class type) { + if (!descriptor.isExtension()) { + throw new IllegalArgumentException( + "GeneratedExtension given a regular (non-extension) field."); + } + + this.descriptor = descriptor; + this.type = type; + + switch (descriptor.getJavaType()) { + case MESSAGE: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = + (Message)invokeOrDie(getMethodOrDie(type, "getDefaultInstance"), + null); + break; + case ENUM: + enumValueOf = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + enumGetValueDescriptor = getMethodOrDie(type, "getValueDescriptor"); + messageDefaultInstance = null; + break; + default: + enumValueOf = null; + enumGetValueDescriptor = null; + messageDefaultInstance = null; + break; + } + } + + private final FieldDescriptor descriptor; + private final Class type; + private final Method enumValueOf; + private final Method enumGetValueDescriptor; + private final Message messageDefaultInstance; + + public FieldDescriptor getDescriptor() { return descriptor; } + + /** + * If the extension is an embedded message or group, returns the default + * instance of the message. + */ + @SuppressWarnings("unchecked") + public Message getMessageDefaultInstance() { + return messageDefaultInstance; + } + + /** + * Convert from the type used by the reflection accessors to the type used + * by native accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object fromReflectionType(Object value) { + if (descriptor.isRepeated()) { + if (descriptor.getJavaType() == FieldDescriptor.JavaType.MESSAGE || + descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { + // Must convert the whole list. + List result = new ArrayList(); + for (Object element : (List)value) { + result.add(singularFromReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularFromReflectionType(value); + } + } + + /** + * Like {@link #fromReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularFromReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case MESSAGE: + if (type.isInstance(value)) { + return value; + } else { + // It seems the copy of the embedded message stored inside the + // extended message is not of the exact type the user was + // expecting. This can happen if a user defines a + // GeneratedExtension manually and gives it a different type. + // This should not happen in normal use. But, to be nice, we'll + // copy the message to whatever type the caller was expecting. + return messageDefaultInstance.newBuilderForType() + .mergeFrom((Message)value).build(); + } + case ENUM: + return invokeOrDie(enumValueOf, null, (EnumValueDescriptor)value); + default: + return value; + } + } + + /** + * Convert from the type used by the native accessors to the type used + * by reflection accessors. E.g., for enums, the reflection accessors use + * EnumValueDescriptors but the native accessors use the generated enum + * type. + */ + @SuppressWarnings("unchecked") + private Object toReflectionType(Object value) { + if (descriptor.isRepeated()) { + if (descriptor.getJavaType() == FieldDescriptor.JavaType.ENUM) { + // Must convert the whole list. + List result = new ArrayList(); + for (Object element : (List)value) { + result.add(singularToReflectionType(element)); + } + return result; + } else { + return value; + } + } else { + return singularToReflectionType(value); + } + } + + /** + * Like {@link #toReflectionType(Object)}, but if the type is a repeated + * type, this converts a single element. + */ + private Object singularToReflectionType(Object value) { + switch (descriptor.getJavaType()) { + case ENUM: + return invokeOrDie(enumGetValueDescriptor, value); + default: + return value; + } + } + } + + // ================================================================= + + /** Calls Class.getMethod and throws a RuntimeException if it fails. */ + @SuppressWarnings("unchecked") + private static Method getMethodOrDie( + Class clazz, String name, Class... params) { + try { + return clazz.getMethod(name, params); + } catch (NoSuchMethodException e) { + throw new RuntimeException( + "Generated message class \"" + clazz.getName() + + "\" missing method \"" + name + "\".", e); + } + } + + /** Calls invoke and throws a RuntimeException if it fails. */ + private static Object invokeOrDie( + Method method, Object object, Object... params) { + try { + return method.invoke(object, params); + } catch (IllegalAccessException e) { + throw new RuntimeException( + "Couldn't use Java reflection to implement protocol message " + + "reflection.", e); + } catch (java.lang.reflect.InvocationTargetException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException)cause; + } else if (cause instanceof Error) { + throw (Error)cause; + } else { + throw new RuntimeException( + "Unexpected exception thrown by generated accessor method.", cause); + } + } + } + + /** + * Users should ignore this class. This class provides the implementation + * with access to the fields of a message object using Java reflection. + */ + public static final class FieldAccessorTable { + /** + * Construct a FieldAccessorTable for a particular message class. Only + * one FieldAccessorTable should ever be constructed per class. + * + * @param descriptor The type's descriptor. + * @param camelCaseNames The camelcase names of all fields in the message. + * These are used to derive the accessor method names. + * @param messageClass The message type. + * @param builderClass The builder type. + */ + public FieldAccessorTable( + Descriptor descriptor, + String[] camelCaseNames, + Class messageClass, + Class builderClass) { + this.descriptor = descriptor; + fields = new FieldAccessor[descriptor.getFields().size()]; + + for (int i = 0; i < fields.length; i++) { + FieldDescriptor field = descriptor.getFields().get(i); + if (field.isRepeated()) { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new RepeatedMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new RepeatedEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new RepeatedFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } else { + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + fields[i] = new SingularMessageFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else if (field.getJavaType() == FieldDescriptor.JavaType.ENUM) { + fields[i] = new SingularEnumFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } else { + fields[i] = new SingularFieldAccessor( + field, camelCaseNames[i], messageClass, builderClass); + } + } + } + } + + private final Descriptor descriptor; + private final FieldAccessor[] fields; + + /** Get the FieldAccessor for a particular field. */ + private FieldAccessor getField(FieldDescriptor field) { + if (field.getContainingType() != descriptor) { + throw new IllegalArgumentException( + "FieldDescriptor does not match message type."); + } else if (field.isExtension()) { + // If this type had extensions, it would subclass ExtendableMessage, + // which overrides the reflection interface to handle extensions. + throw new IllegalArgumentException( + "This type does not have extensions."); + } + return fields[field.getIndex()]; + } + + /** + * Abstract interface that provides access to a single field. This is + * implemented differently depending on the field type and cardinality. + */ + private static interface FieldAccessor { + Object get(GeneratedMessage message); + void set(GeneratedMessage.Builder builder, Object value); + Object getRepeated(GeneratedMessage message, int index); + void setRepeated(GeneratedMessage.Builder builder, + int index, Object value); + void addRepeated(GeneratedMessage.Builder builder, Object value); + boolean has(GeneratedMessage message); + int getRepeatedCount(GeneratedMessage message); + void clear(GeneratedMessage.Builder builder); + Message.Builder newBuilder(); + } + + // --------------------------------------------------------------- + + private static class SingularFieldAccessor implements FieldAccessor { + SingularFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName); + type = getMethod.getReturnType(); + setMethod = getMethodOrDie(builderClass, "set" + camelCaseName, type); + hasMethod = + getMethodOrDie(messageClass, "has" + camelCaseName); + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + // Note: We use Java reflection to call public methods rather than + // access private fields directly as this avoids runtime security + // checks. + Class type; + Method getMethod; + Method setMethod; + Method hasMethod; + Method clearMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(setMethod, builder, value); + } + public Object getRepeated(GeneratedMessage message, int index) { + throw new UnsupportedOperationException( + "getRepeatedField() called on a singular field."); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + throw new UnsupportedOperationException( + "setRepeatedField() called on a singular field."); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + throw new UnsupportedOperationException( + "addRepeatedField() called on a singular field."); + } + public boolean has(GeneratedMessage message) { + return (Boolean)invokeOrDie(hasMethod, message); + } + public int getRepeatedCount(GeneratedMessage message) { + throw new UnsupportedOperationException( + "getRepeatedFieldSize() called on a singular field."); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + private static class RepeatedFieldAccessor implements FieldAccessor { + RepeatedFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + getMethod = getMethodOrDie(messageClass, "get" + camelCaseName + "List"); + + getRepeatedMethod = + getMethodOrDie(messageClass, "get" + camelCaseName, Integer.TYPE); + type = getRepeatedMethod.getReturnType(); + setRepeatedMethod = + getMethodOrDie(builderClass, "set" + camelCaseName, + Integer.TYPE, type); + addRepeatedMethod = + getMethodOrDie(builderClass, "add" + camelCaseName, type); + getCountMethod = + getMethodOrDie(messageClass, "get" + camelCaseName + "Count"); + + clearMethod = getMethodOrDie(builderClass, "clear" + camelCaseName); + } + + Class type; + Method getMethod; + Method getRepeatedMethod; + Method setRepeatedMethod; + Method addRepeatedMethod; + Method getCountMethod; + Method clearMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getMethod, message); + } + public void set(GeneratedMessage.Builder builder, Object value) { + // Add all the elements individually. This serves two purposes: + // 1) Verifies that each element has the correct type. + // 2) Insures that the caller cannot modify the list later on and + // have the modifications be reflected in the message. + clear(builder); + for (Object element : (List)value) { + addRepeated(builder, element); + } + } + public Object getRepeated(GeneratedMessage message, int index) { + return invokeOrDie(getRepeatedMethod, message, index); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + invokeOrDie(setRepeatedMethod, builder, index, value); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + invokeOrDie(addRepeatedMethod, builder, value); + } + public boolean has(GeneratedMessage message) { + throw new UnsupportedOperationException( + "hasField() called on a singular field."); + } + public int getRepeatedCount(GeneratedMessage message) { + return (Integer)invokeOrDie(getCountMethod, message); + } + public void clear(GeneratedMessage.Builder builder) { + invokeOrDie(clearMethod, builder); + } + public Message.Builder newBuilder() { + throw new UnsupportedOperationException( + "newBuilderForField() called on a non-Message type."); + } + } + + // --------------------------------------------------------------- + + private static final class SingularEnumFieldAccessor + extends SingularFieldAccessor { + SingularEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + public Object get(GeneratedMessage message) { + return invokeOrDie(getValueDescriptorMethod, super.get(message)); + } + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + private static final class RepeatedEnumFieldAccessor + extends RepeatedFieldAccessor { + RepeatedEnumFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + valueOfMethod = getMethodOrDie(type, "valueOf", + EnumValueDescriptor.class); + getValueDescriptorMethod = + getMethodOrDie(type, "getValueDescriptor"); + } + + private Method valueOfMethod; + private Method getValueDescriptorMethod; + + @SuppressWarnings("unchecked") + public Object get(GeneratedMessage message) { + List newList = new ArrayList(); + for (Object element : (List)super.get(message)) { + newList.add(invokeOrDie(getValueDescriptorMethod, element)); + } + return Collections.unmodifiableList(newList); + } + public Object getRepeated(GeneratedMessage message, int index) { + return invokeOrDie(getValueDescriptorMethod, + super.getRepeated(message, index)); + } + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + super.setRepeated(builder, index, invokeOrDie(valueOfMethod, null, value)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, invokeOrDie(valueOfMethod, null, value)); + } + } + + // --------------------------------------------------------------- + + private static final class SingularMessageFieldAccessor + extends SingularFieldAccessor { + SingularMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // The value is not the exact right message type. However, if it + // is an alternative implementation of the same type -- e.g. a + // DynamicMessage -- we should accept it. In this case we can make + // a copy of the message. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void set(GeneratedMessage.Builder builder, Object value) { + super.set(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + + private static final class RepeatedMessageFieldAccessor + extends RepeatedFieldAccessor { + RepeatedMessageFieldAccessor( + FieldDescriptor descriptor, String camelCaseName, + Class messageClass, + Class builderClass) { + super(descriptor, camelCaseName, messageClass, builderClass); + + newBuilderMethod = getMethodOrDie(type, "newBuilder"); + } + + private Method newBuilderMethod; + + private Object coerceType(Object value) { + if (type.isInstance(value)) { + return value; + } else { + // The value is not the exact right message type. However, if it + // is an alternative implementation of the same type -- e.g. a + // DynamicMessage -- we should accept it. In this case we can make + // a copy of the message. + return ((Message.Builder)invokeOrDie(newBuilderMethod, null)) + .mergeFrom((Message)value).build(); + } + } + + public void setRepeated(GeneratedMessage.Builder builder, + int index, Object value) { + super.setRepeated(builder, index, coerceType(value)); + } + public void addRepeated(GeneratedMessage.Builder builder, Object value) { + super.addRepeated(builder, coerceType(value)); + } + public Message.Builder newBuilder() { + return (Message.Builder)invokeOrDie(newBuilderMethod, null); + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java new file mode 100644 index 00000000..d57da4ed --- /dev/null +++ b/java/src/main/java/com/google/protobuf/InvalidProtocolBufferException.java @@ -0,0 +1,77 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +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 InvalidProtocolBufferException extends IOException { + public InvalidProtocolBufferException(String description) { + super(description); + } + + static InvalidProtocolBufferException truncatedMessage() { + return new InvalidProtocolBufferException( + "While parsing a protocol message, the input ended unexpectedly " + + "in the middle of a field. This could mean either than the " + + "input has been truncated or that an embedded message " + + "misreported its own length."); + } + + static InvalidProtocolBufferException negativeSize() { + return new InvalidProtocolBufferException( + "CodedInputStream encountered an embedded string or message " + + "which claimed to have negative size."); + } + + static InvalidProtocolBufferException malformedVarint() { + return new InvalidProtocolBufferException( + "CodedInputStream encountered a malformed varint."); + } + + static InvalidProtocolBufferException invalidTag() { + return new InvalidProtocolBufferException( + "Protocol message contained an invalid tag (zero)."); + } + + static InvalidProtocolBufferException invalidEndTag() { + return new InvalidProtocolBufferException( + "Protocol message end-group tag did not match expected tag."); + } + + static InvalidProtocolBufferException invalidWireType() { + return new InvalidProtocolBufferException( + "Protocol message tag had invalid wire type."); + } + + static InvalidProtocolBufferException recursionLimitExceeded() { + return new InvalidProtocolBufferException( + "Protocol message had too many levels of nesting. May be malicious. " + + "Use CodedInputStream.setRecursionLimit() to increase the depth limit."); + } + + static InvalidProtocolBufferException sizeLimitExceeded() { + return new InvalidProtocolBufferException( + "Protocol message was too large. May be malicious. " + + "Use CodedInputStream.setSizeLimit() to increase the size limit."); + } +} diff --git a/java/src/main/java/com/google/protobuf/Message.java b/java/src/main/java/com/google/protobuf/Message.java new file mode 100644 index 00000000..f45f7df9 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Message.java @@ -0,0 +1,415 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// TODO(kenton): Use generics? E.g. Builder, then +// mergeFrom*() could return BuilderType for better type-safety. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +/** + * Abstract interface implemented by Protocol Message objects. + * + * @author kenton@google.com Kenton Varda + */ +public interface Message { + /** + * Get the message's type's descriptor. This differs from the + * {@code getDescriptor()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDescriptor()} is a static method of a specific class. + * They return the same thing. + */ + Descriptors.Descriptor getDescriptorForType(); + + /** + * Get an instance of the type with all fields set to their default values. + * This may or may not be a singleton. This differs from the + * {@code getDefaultInstance()} method of generated message classes in that + * this method is an abstract method of the {@code Message} interface + * whereas {@code getDefaultInstance()} is a static method of a specific + * class. They return the same thing. + */ + Message getDefaultInstanceForType(); + + /** + * Returns a collection of all the fields in this message which are set + * and their corresponding values. A singular ("required" or "optional") + * field is set iff hasField() returns true for that field. A "repeated" + * field is set iff getRepeatedFieldSize() is greater than zero. The + * values are exactly what would be returned by calling + * {@link #getField(Descriptors.FieldDescriptor)} for each field. The map + * is guaranteed to be a sorted map, so iterating over it will return fields + * in order by field number. + */ + Map getAllFields(); + + /** + * Returns true if the given field is set. This is exactly equivalent to + * calling the generated "has" accessor method corresponding to the field. + * @throws IllegalArgumentException The field is a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** + * Obtains the value of the given field, or the default value if it is + * not set. For primitive fields, the boxed primitive value is returned. + * For enum fields, the EnumValueDescriptor for the value is returend. For + * embedded message fields, the sub-message is returned. For repeated + * fields, a java.util.List is returned. + */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Gets the number of elements of a repeated field. This is exactly + * equivalent to calling the generated "Count" accessor method corresponding + * to the field. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Gets an element of a repeated field. For primitive fields, the boxed + * primitive value is returned. For enum fields, the EnumValueDescriptor + * for the value is returend. For embedded message fields, the sub-message + * is returned. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); + + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + + /** + * Serializes the message and writes it to {@code output}. This does not + * flush or close the stream. + */ + void writeTo(CodedOutputStream output) throws IOException; + + /** + * Get the number of bytes required to encode this message. The result + * is only computed on the first call and memoized after that. + */ + int getSerializedSize(); + + // ----------------------------------------------------------------- + // Comparison and hashing + + /** + * Compares the specified object with this message for equality. Returns + * true if the given object is a message of the same type (as + * defined by {@code getDescriptorForType()}) and has identical values for + * all of its fields. + * + * @param other object to be compared for equality with this message + * @return true if the specified object is equal to this message + */ + boolean equals(Object other); + + /** + * Returns the hash code value for this message. The hash code of a message + * is defined to be getDescriptor().hashCode() ^ map.hashCode(), + * where map is a map of field numbers to field values. + * + * @return the hash code value for this message + * @see Map#hashCode() + */ + int hashCode(); + + // ----------------------------------------------------------------- + // Convenience methods. + + /** + * Converts the message to a string in protocol buffer text format. This is + * just a trivial wrapper around {@link TextFormat#printToString(Message)}. + */ + String toString(); + + /** + * Serializes the message to a {@code ByteString} and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + ByteString toByteString(); + + /** + * Serializes the message to a {@code byte} array and returns it. This is + * just a trivial wrapper around + * {@link #writeTo(CodedOutputStream)}. + */ + byte[] toByteArray(); + + /** + * Serializes the message and writes it to {@code output}. This is just a + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does + * not flush or close the stream. + */ + void writeTo(OutputStream output) throws IOException; + + // ================================================================= + // Builders + + /** + * Constructs a new builder for a message of the same type as this message. + */ + Builder newBuilderForType(); + + /** + * Abstract interface implemented by Protocol Message builders. + */ + public static interface Builder extends Cloneable { + /** Resets all fields to their default values. */ + Builder clear(); + + /** + * Merge {@code other} into the message being built. {@code other} must + * have the exact same type as {@code this} (i.e. + * {@code getDescriptorForType() == other.getDescriptorForType()}). + * + * Merging occurs as follows. For each field:
+ * * For singular primitive fields, if the field is set in {@code other}, + * then {@code other}'s value overwrites the value in this message.
+ * * For singular message fields, if the field is set in {@code other}, + * it is merged into the corresponding sub-message of this message + * using the same merging rules.
+ * * For repeated fields, the elements in {@code other} are concatenated + * with the elements in this message. + * + * This is equivalent to the {@code Message::MergeFrom} method in C++. + */ + Builder mergeFrom(Message other); + + /** + * Construct the final message. Once this is called, the Builder is no + * longer valid, and calling any other method may throw a + * NullPointerException. If you need to continue working with the builder + * after calling {@code build()}, {@code clone()} it first. + * @throws UninitializedMessageException The message is missing one or more + * required fields (i.e. {@link #isInitialized()} returns false). + * Use {@link #buildPartial()} to bypass this check. + */ + Message build(); + + /** + * Like {@link #build()}, but does not throw an exception if the message + * is missing required fields. Instead, a partial message is returned. + */ + Message buildPartial(); + + /** + * Clones the Builder. + * @see Object#clone() + */ + Builder clone(); + + /** + * Returns true if all required fields in the message and all embedded + * messages are set, false otherwise. + */ + boolean isInitialized(); + + /** + * Parses a message of this type from the input and merges it with this + * message, as if using {@link Builder#mergeFrom(Message)}. + * + *

Warning: This does not verify that all required fields are present in + * the input message. If you call {@link #build()} without setting all + * required fields, it will throw an {@link UninitializedMessageException}, + * which is a {@code RuntimeException} and thus might not be caught. There + * are a few good ways to deal with this: + *

    + *
  • Call {@link #isInitialized()} to verify that all required fields + * are set before building. + *
  • Parse the message separately using one of the static + * {@code parseFrom} methods, then use {@link #mergeFrom(Message)} + * to merge it with this one. {@code parseFrom} will throw an + * {@link InvalidProtocolBufferException} (an {@code IOException}) + * if some required fields are missing. + *
  • Use {@code buildPartial()} to build, which ignores missing + * required fields. + *
+ * + *

Note: The caller should call + * {@link CodedInputStream#checkLastTagWas(int)} after calling this to + * verify that the last tag seen was the appropriate end-group tag, + * or zero for EOF. + */ + Builder mergeFrom(CodedInputStream input) throws IOException; + + /** + * Like {@link Builder#mergeFrom(CodedInputStream)}, but also + * parses extensions. The extensions that you want to be able to parse + * must be registered in {@code extensionRegistry}. Extensions not in + * the registry will be treated as unknown fields. + */ + Builder mergeFrom(CodedInputStream input, + ExtensionRegistry extensionRegistry) + throws IOException; + + /** + * Get the message's type's descriptor. + * See {@link Message#getDescriptorForType()}. + */ + Descriptors.Descriptor getDescriptorForType(); + + /** + * Get the message's type's default instance. + * See {@link Message#getDefaultInstanceForType()}. + */ + Message getDefaultInstanceForType(); + + /** + * Like {@link Message#getAllFields()}. The returned map may or may not + * reflect future changes to the builder. Either way, the returned map is + * itself unmodifiable. + */ + Map getAllFields(); + + /** + * Create a Builder for messages of the appropriate type for the given + * field. Messages built with this can then be passed to setField(), + * setRepeatedField(), or addRepeatedField(). + */ + Builder newBuilderForField(Descriptors.FieldDescriptor field); + + /** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */ + boolean hasField(Descriptors.FieldDescriptor field); + + /** Like {@link Message#getField(Descriptors.FieldDescriptor)} */ + Object getField(Descriptors.FieldDescriptor field); + + /** + * Sets a field to the given value. The value must be of the correct type + * for this field, i.e. the same type that + * {@link Message#getField(Descriptors.FieldDescriptor)} would return. + */ + Builder setField(Descriptors.FieldDescriptor field, Object value); + + /** + * Clears the field. This is exactly equivalent to calling the generated + * "clear" accessor method corresponding to the field. + */ + Builder clearField(Descriptors.FieldDescriptor field); + + /** + * Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)} + */ + int getRepeatedFieldCount(Descriptors.FieldDescriptor field); + + /** + * Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} + */ + Object getRepeatedField(Descriptors.FieldDescriptor field, int index); + + /** + * Sets an element of a repeated field to the given value. The value must + * be of the correct type for this field, i.e. the same type that + * {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would + * return. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Builder setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value); + + /** + * Like {@code setRepeatedField}, but appends the value as a new element. + * @throws IllegalArgumentException The field is not a repeated field, or + * {@code field.getContainingType() != getDescriptorForType()}. + */ + Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value); + + /** Get the {@link UnknownFieldSet} for this message. */ + UnknownFieldSet getUnknownFields(); + + /** Set the {@link UnknownFieldSet} for this message. */ + Builder setUnknownFields(UnknownFieldSet unknownFields); + + /** + * Merge some unknown fields into the {@link UnknownFieldSet} for this + * message. + */ + Builder mergeUnknownFields(UnknownFieldSet unknownFields); + + // --------------------------------------------------------------- + // Convenience methods. + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(ByteString data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException; + + /** + * Parse {@code data} as a message of this type and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(byte[] data, + ExtensionRegistry extensionRegistry) + throws InvalidProtocolBufferException; + + /** + * Parse a message of this type from {@code input} and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. Note that this method always + * reads the entire input (unless it throws an exception). If you + * want it to stop earlier, you will need to wrap your input in some + * wrapper stream that limits reading. Despite usually reading the entire + * input, this does not close the stream. + */ + Builder mergeFrom(InputStream input) throws IOException; + + /** + * Parse a message of this type from {@code input} and merge it with the + * message being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}. + */ + Builder mergeFrom(InputStream input, + ExtensionRegistry extensionRegistry) + throws IOException; + } +} diff --git a/java/src/main/java/com/google/protobuf/RpcCallback.java b/java/src/main/java/com/google/protobuf/RpcCallback.java new file mode 100644 index 00000000..a99077fa --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcCallback.java @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + * Interface for an RPC callback, normally called when an RPC completes. + * {@code ParameterType} is normally the method's response message type. + * + * @author kenton@google.com Kenton Varda + */ +public interface RpcCallback { + void run(ParameterType parameter); +} diff --git a/java/src/main/java/com/google/protobuf/RpcChannel.java b/java/src/main/java/com/google/protobuf/RpcChannel.java new file mode 100644 index 00000000..1c7dcfc0 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcChannel.java @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + *

Abstract interface for an RPC channel. An {@code RpcChannel} represents a + * communication line to a {@link Service} which can be used to call that + * {@link Service}'s methods. The {@link Service} may be running on another + * machine. Normally, you should not call an {@code RpcChannel} directly, but + * instead construct a stub {@link Service} wrapping it. Example: + * + *

+ *   RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
+ *   RpcController controller = rpcImpl.newController();
+ *   MyService service = MyService.newStub(channel);
+ *   service.myMethod(controller, request, callback);
+ * 
+ * + * @author kenton@google.com Kenton Varda + */ +public interface RpcChannel { + /** + * Call the given method of the remote service. This method is similar to + * {@code Service.callMethod()} with one important difference: the caller + * decides the types of the {@code Message} objects, not the callee. The + * request may be of any type as long as + * {@code request.getDescriptor() == method.getInputType()}. + * The response passed to the callback will be of the same type as + * {@code responsePrototype} (which must have + * {@code getDescriptor() == method.getOutputType()}). + */ + void callMethod(Descriptors.MethodDescriptor method, + RpcController controller, + Message request, + Message responsePrototype, + RpcCallback done); +} diff --git a/java/src/main/java/com/google/protobuf/RpcController.java b/java/src/main/java/com/google/protobuf/RpcController.java new file mode 100644 index 00000000..7495bb8a --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcController.java @@ -0,0 +1,98 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + *

An {@code RpcController} mediates a single method call. The primary + * purpose of the controller is to provide a way to manipulate settings + * specific to the RPC implementation and to find out about RPC-level errors. + * + *

The methods provided by the {@code RpcController} interface are intended + * to be a "least common denominator" set of features which we expect all + * implementations to support. Specific implementations may provide more + * advanced features (e.g. deadline propagation). + * + * @author kenton@google.com Kenton Varda + */ +public interface RpcController { + // ----------------------------------------------------------------- + // These calls may be made from the client side only. Their results + // are undefined on the server side (may throw RuntimeExceptions). + + /** + * Resets the RpcController to its initial state so that it may be reused in + * a new call. This can be called from the client side only. It must not + * be called while an RPC is in progress. + */ + void reset(); + + /** + * After a call has finished, returns true if the call failed. The possible + * reasons for failure depend on the RPC implementation. {@code failed()} + * most only be called on the client side, and must not be called before a + * call has finished. + */ + boolean failed(); + + /** + * If {@code failed()} is {@code true}, returns a human-readable description + * of the error. + */ + String errorText(); + + /** + * Advises the RPC system that the caller desires that the RPC call be + * canceled. The RPC system may cancel it immediately, may wait awhile and + * then cancel it, or may not even cancel the call at all. If the call is + * canceled, the "done" callback will still be called and the RpcController + * will indicate that the call failed at that time. + */ + void startCancel(); + + // ----------------------------------------------------------------- + // These calls may be made from the server side only. Their results + // are undefined on the client side (may throw RuntimeExceptions). + + /** + * Causes {@code failed()} to return true on the client side. {@code reason} + * will be incorporated into the message returned by {@code errorText()}. + * If you find you need to return machine-readable information about + * failures, you should incorporate it into your response protocol buffer + * and should NOT call {@code setFailed()}. + */ + void setFailed(String reason); + + /** + * If {@code true}, indicates that the client canceled the RPC, so the server + * may as well give up on replying to it. This method must be called on the + * server side only. The server should still call the final "done" callback. + */ + boolean isCanceled(); + + /** + * Asks that the given callback be called when the RPC is canceled. The + * parameter passed to the callback will always be {@code null}. The + * callback will always be called exactly once. If the RPC completes without + * being canceled, the callback will be called after completion. If the RPC + * has already been canceled when NotifyOnCancel() is called, the callback + * will be called immediately. + * + *

{@code notifyOnCancel()} must be called no more than once per request. + * It must be called on the server side only. + */ + void notifyOnCancel(RpcCallback callback); +} diff --git a/java/src/main/java/com/google/protobuf/RpcUtil.java b/java/src/main/java/com/google/protobuf/RpcUtil.java new file mode 100644 index 00000000..13cd4aca --- /dev/null +++ b/java/src/main/java/com/google/protobuf/RpcUtil.java @@ -0,0 +1,118 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + * Grab-bag of utility functions useful when dealing with RPCs. + * + * @author kenton@google.com Kenton Varda + */ +public final class RpcUtil { + private RpcUtil() {} + + /** + * Take an {@code RcpCallabck} and convert it to an + * {@code RpcCallback} accepting a specific message type. This is always + * type-safe (parameter type contravariance). + */ + @SuppressWarnings("unchecked") + public static RpcCallback + specializeCallback(final RpcCallback originalCallback) { + return (RpcCallback)originalCallback; + // The above cast works, but only due to technical details of the Java + // implementation. A more theoretically correct -- but less efficient -- + // implementation would be as follows: + // return new RpcCallback() { + // public void run(Type parameter) { + // originalCallback.run(parameter); + // } + // }; + } + + /** + * Take an {@code RcpCallabck} accepting a specific message type and convert + * it to an {@code RcpCallabck}. The generalized callback will + * accept any message object which has the same descriptor, and will convert + * it to the correct class before calling the original callback. However, + * if the generalized callback is given a message with a different descriptor, + * an exception will be thrown. + */ + public static + RpcCallback generalizeCallback( + final RpcCallback originalCallback, + final Class originalClass, + final Type defaultInstance) { + return new RpcCallback() { + public void run(Message parameter) { + Type typedParameter; + try { + typedParameter = originalClass.cast(parameter); + } catch (ClassCastException e) { + typedParameter = copyAsType(defaultInstance, parameter); + } + originalCallback.run(typedParameter); + } + }; + } + + /** + * Creates a new message of type "Type" which is a copy of "source". "source" + * must have the same descriptor but may be a different class (e.g. + * DynamicMessage). + */ + @SuppressWarnings("unchecked") + private static Type copyAsType( + Type typeDefaultInstance, Message source) { + return (Type)typeDefaultInstance.newBuilderForType() + .mergeFrom(source) + .build(); + } + + /** + * Creates a callback which can only be called once. This may be useful for + * security, when passing a callback to untrusted code: most callbacks do + * not expect to be called more than once, so doing so may expose bugs if it + * is not prevented. + */ + public static + RpcCallback newOneTimeCallback( + final RpcCallback originalCallback) { + return new RpcCallback() { + boolean alreadyCalled = false; + public void run(ParameterType parameter) { + synchronized(this) { + if (alreadyCalled) { + throw new AlreadyCalledException(); + } + alreadyCalled = true; + } + + originalCallback.run(parameter); + } + }; + } + + /** + * Exception thrown when a one-time callback is called more than once. + */ + public static final class AlreadyCalledException extends RuntimeException { + public AlreadyCalledException() { + super("This RpcCallback was already called and cannot be called " + + "multiple times."); + } + } +} diff --git a/java/src/main/java/com/google/protobuf/Service.java b/java/src/main/java/com/google/protobuf/Service.java new file mode 100644 index 00000000..53b2557e --- /dev/null +++ b/java/src/main/java/com/google/protobuf/Service.java @@ -0,0 +1,97 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + * Abstract base interface for protocol-buffer-based RPC services. Services + * themselves are abstract classes (implemented either by servers or as + * stubs), but they subclass this base interface. The methods of this + * interface can be used to call the methods of the service without knowing + * its exact type at compile time (analogous to the Message interface). + * + * @author kenton@google.com Kenton Varda + */ +public interface Service { + /** + * Get the {@code ServiceDescriptor} describing this service and its methods. + */ + Descriptors.ServiceDescriptor getDescriptorForType(); + + /** + *

Call a method of the service specified by MethodDescriptor. This is + * normally implemented as a simple {@code switch()} that calls the standard + * definitions of the service's methods. + * + *

Preconditions: + *

    + *
  • {@code method.getService() == getDescriptorForType()} + *
  • {@code request} is of the exact same class as the object returned by + * {@code getRequestPrototype(method)}. + *
  • {@code controller} is of the correct type for the RPC implementation + * being used by this Service. For stubs, the "correct type" depends + * on the RpcChannel which the stub is using. Server-side Service + * implementations are expected to accept whatever type of + * {@code RpcController} the server-side RPC implementation uses. + *
+ * + *

Postconditions: + *

    + *
  • {@code done} will be called when the method is complete. This may be + * before {@code callMethod()} returns or it may be at some point in + * the future. + *
  • The parameter to {@code done} is the response. It must be of the + * exact same type as would be returned by + * {@code getResponsePrototype(method)}. + *
  • If the RPC failed, the parameter to {@code done} will be + * {@code null}. Further details about the failure can be found by + * querying {@code controller}. + *
+ */ + void callMethod(Descriptors.MethodDescriptor method, + RpcController controller, + Message request, + RpcCallback done); + + /** + *

{@code callMethod()} requires that the request passed in is of a + * particular subclass of {@code Message}. {@code getRequestPrototype()} + * gets the default instances of this type for a given method. You can then + * call {@code Message.newBuilderForType()} on this instance to + * construct a builder to build an object which you can then pass to + * {@code callMethod()}. + * + *

Example: + *

+   *   MethodDescriptor method =
+   *     service.getDescriptorForType().findMethodByName("Foo");
+   *   Message request =
+   *     stub.getRequestPrototype(method).newBuilderForType()
+   *         .mergeFrom(input).build();
+   *   service.callMethod(method, request, callback);
+   * 
+ */ + Message getRequestPrototype(Descriptors.MethodDescriptor method); + + /** + * Like {@code getRequestPrototype()}, but gets a prototype of the response + * message. {@code getResponsePrototype()} is generally not needed because + * the {@code Service} implementation constructs the response message itself, + * but it may be useful in some cases to know ahead of time what type of + * object will be returned. + */ + Message getResponsePrototype(Descriptors.MethodDescriptor method); +} diff --git a/java/src/main/java/com/google/protobuf/TextFormat.java b/java/src/main/java/com/google/protobuf/TextFormat.java new file mode 100644 index 00000000..2d949b65 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/TextFormat.java @@ -0,0 +1,1276 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; + +import java.io.IOException; +import java.nio.CharBuffer; +import java.math.BigInteger; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Provide ascii text parsing and formatting support for proto2 instances. + * The implementation largely follows google/protobuf/text_format.cc. + * + * @author wenboz@google.com Wenbo Zhu + * @author kenton@google.com Kenton Varda + */ +public final class TextFormat { + + /** + * Outputs a textual representation of the Protocol Message supplied into + * the parameter output. (This representation is the new version of the + * classic "ProtocolPrinter" output from the original Protocol Buffer system) + */ + public static void print(Message message, Appendable output) + throws IOException { + TextGenerator generator = new TextGenerator(output); + print(message, generator); + } + + /** Outputs a textual representation of {@code fields} to {@code output}. */ + public static void print(UnknownFieldSet fields, Appendable output) + throws IOException { + TextGenerator generator = new TextGenerator(output); + printUnknownFields(fields, generator); + } + + /** + * Like {@code print()}, but writes directly to a {@code String} and + * returns it. + */ + public static String printToString(Message message) { + try { + StringBuilder text = new StringBuilder(); + print(message, text); + return text.toString(); + } catch (IOException e) { + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); + } + } + + /** + * Like {@code print()}, but writes directly to a {@code String} and + * returns it. + */ + public static String printToString(UnknownFieldSet fields) { + try { + StringBuilder text = new StringBuilder(); + print(fields, text); + return text.toString(); + } catch (IOException e) { + throw new RuntimeException( + "Writing to a StringBuilder threw an IOException (should never " + + "happen).", e); + } + } + + private static void print(Message message, TextGenerator generator) + throws IOException { + Descriptor descriptor = message.getDescriptorForType(); + for (Map.Entry field : + message.getAllFields().entrySet()) { + printField(field.getKey(), field.getValue(), generator); + } + printUnknownFields(message.getUnknownFields(), generator); + } + + public static void printField(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + if (field.isRepeated()) { + // Repeated field. Print each element. + for (Object element : (List) value) { + printSingleField(field, element, generator); + } + } else { + printSingleField(field, value, generator); + } + } + + private static void printSingleField(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + if (field.isExtension()) { + generator.print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field.getContainingType().getOptions().getMessageSetWireFormat() + && (field.getType() == FieldDescriptor.Type.MESSAGE) + && (field.isOptional()) + // object equality + && (field.getExtensionScope() == field.getMessageType())) { + generator.print(field.getMessageType().getFullName()); + } else { + generator.print(field.getFullName()); + } + generator.print("]"); + } else { + if (field.getType() == FieldDescriptor.Type.GROUP) { + // Groups must be serialized with their original capitalization. + generator.print(field.getMessageType().getName()); + } else { + generator.print(field.getName()); + } + } + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.print(" {\n"); + generator.indent(); + } else { + generator.print(": "); + } + + printFieldValue(field, value, generator); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + generator.outdent(); + generator.print("}"); + } + generator.print("\n"); + } + + private static void printFieldValue(FieldDescriptor field, + Object value, + TextGenerator generator) + throws IOException { + switch (field.getType()) { + case INT32: + case INT64: + case SINT32: + case SINT64: + case SFIXED32: + case SFIXED64: + case FLOAT: + case DOUBLE: + case BOOL: + // Good old toString() does what we want for these types. + generator.print(value.toString()); + break; + + case UINT32: + case FIXED32: + generator.print(unsignedToString((Integer) value)); + break; + + case UINT64: + case FIXED64: + generator.print(unsignedToString((Long) value)); + break; + + case STRING: + generator.print("\""); + generator.print(escapeText((String) value)); + generator.print("\""); + break; + + case BYTES: { + generator.print("\""); + generator.print(escapeBytes((ByteString) value)); + generator.print("\""); + break; + } + + case ENUM: { + generator.print(((EnumValueDescriptor) value).getName()); + break; + } + + case MESSAGE: + case GROUP: + print((Message) value, generator); + break; + } + } + + private static void printUnknownFields(UnknownFieldSet unknownFields, + TextGenerator generator) + throws IOException { + for (Map.Entry entry : + unknownFields.asMap().entrySet()) { + String prefix = entry.getKey().toString() + ": "; + UnknownFieldSet.Field field = entry.getValue(); + + for (long value : field.getVarintList()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(unsignedToString(value)); + generator.print("\n"); + } + for (int value : field.getFixed32List()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(String.format((Locale) null, "0x%08x", value)); + generator.print("\n"); + } + for (long value : field.getFixed64List()) { + generator.print(entry.getKey().toString()); + generator.print(": "); + generator.print(String.format((Locale) null, "0x%016x", value)); + generator.print("\n"); + } + for (ByteString value : field.getLengthDelimitedList()) { + generator.print(entry.getKey().toString()); + generator.print(": \""); + generator.print(escapeBytes(value)); + generator.print("\"\n"); + } + for (UnknownFieldSet value : field.getGroupList()) { + generator.print(entry.getKey().toString()); + generator.print(" {\n"); + generator.indent(); + printUnknownFields(value, generator); + generator.outdent(); + generator.print("}\n"); + } + } + } + + /** Convert an unsigned 32-bit integer to a string. */ + private static String unsignedToString(int value) { + if (value >= 0) { + return Integer.toString(value); + } else { + return Long.toString(((long) value) & 0x00000000FFFFFFFFL); + } + } + + /** Convert an unsigned 64-bit integer to a string. */ + private static String unsignedToString(long value) { + if (value >= 0) { + return Long.toString(value); + } else { + // Pull off the most-significant bit so that BigInteger doesn't think + // the number is negative, then set it again using setBit(). + return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL) + .setBit(63).toString(); + } + } + + /** + * An inner class for writing text to the output stream. + */ + static private final class TextGenerator { + + Appendable output; + boolean atStartOfLine = true; + StringBuilder indent = new StringBuilder(); + + public TextGenerator(Appendable output) { + this.output = output; + } + + /** + * Indent text by two spaces. After calling Indent(), two spaces will be + * inserted at the beginning of each line of text. Indent() may be called + * multiple times to produce deeper indents. + */ + public void indent() { + indent.append(" "); + } + + /** + * Reduces the current indent level by two spaces, or crashes if the indent + * level is zero. + */ + public void outdent() { + int length = indent.length(); + if (length == 0) { + throw new IllegalArgumentException( + " Outdent() without matching Indent()."); + } + indent.delete(length - 2, length); + } + + /** + * Print text to the output stream. + */ + public void print(CharSequence text) throws IOException { + int size = text.length(); + int pos = 0; + + for (int i = 0; i < size; i++) { + if (text.charAt(i) == '\n') { + write(text.subSequence(pos, size), i - pos + 1); + pos = i + 1; + atStartOfLine = true; + } + } + write(text.subSequence(pos, size), size - pos); + } + + private void write(CharSequence data, int size) throws IOException { + if (size == 0) { + return; + } + if (atStartOfLine) { + atStartOfLine = false; + output.append(indent); + } + output.append(data); + } + } + + // ================================================================= + // Parsing + + /** + * Represents a stream of tokens parsed from a {@code String}. + * + *

The Java standard library provides many classes that you might think + * would be useful for implementing this, but aren't. For example: + * + *

    + *
  • {@code java.io.StreamTokenizer}: This almost does what we want -- or, + * at least, something that would get us close to what we want -- except + * for one fatal flaw: It automatically un-escapes strings using Java + * escape sequences, which do not include all the escape sequences we + * need to support (e.g. '\x'). + *
  • {@code java.util.Scanner}: This seems like a great way at least to + * parse regular expressions out of a stream (so we wouldn't have to load + * the entire input into a single string before parsing). Sadly, + * {@code Scanner} requires that tokens be delimited with some delimiter. + * Thus, although the text "foo:" should parse to two tokens ("foo" and + * ":"), {@code Scanner} would recognize it only as a single token. + * Furthermore, {@code Scanner} provides no way to inspect the contents + * of delimiters, making it impossible to keep track of line and column + * numbers. + *
+ * + *

Luckily, Java's regular expression support does manage to be useful to + * us. (Barely: We need {@code Matcher.usePattern()}, which is new in + * Java 1.5.) So, we can use that, at least. Unfortunately, this implies + * that we need to have the entire input in one contiguous string. + */ + private static final class Tokenizer { + private final CharSequence text; + private final Matcher matcher; + private String currentToken; + + // The character index within this.text at which the current token begins. + private int pos = 0; + + // The line and column numbers of the current token. + private int line = 0; + private int column = 0; + + // The line and column numbers of the previous token (allows throwing + // errors *after* consuming). + private int previousLine = 0; + private int previousColumn = 0; + + private static Pattern WHITESPACE = + Pattern.compile("(\\s|(#.*$))+", Pattern.MULTILINE); + private static Pattern TOKEN = Pattern.compile( + "[a-zA-Z_][0-9a-zA-Z_+-]*|" + // an identifier + "[0-9+-][0-9a-zA-Z_.+-]*|" + // a number + "\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|" + // a double-quoted string + "\'([^\"\n\\\\]|\\\\.)*(\'|\\\\?$)", // a single-quoted string + Pattern.MULTILINE); + + private static Pattern DOUBLE_INFINITY = Pattern.compile( + "-?inf(inity)?", + Pattern.CASE_INSENSITIVE); + private static Pattern FLOAT_INFINITY = Pattern.compile( + "-?inf(inity)?f?", + Pattern.CASE_INSENSITIVE); + private static Pattern FLOAT_NAN = Pattern.compile( + "nanf?", + Pattern.CASE_INSENSITIVE); + + /** Construct a tokenizer that parses tokens from the given text. */ + public Tokenizer(CharSequence text) { + this.text = text; + this.matcher = WHITESPACE.matcher(text); + skipWhitespace(); + nextToken(); + } + + /** Are we at the end of the input? */ + public boolean atEnd() { + return currentToken.length() == 0; + } + + /** Advance to the next token. */ + public void nextToken() { + previousLine = line; + previousColumn = column; + + // Advance the line counter to the current position. + while (pos < matcher.regionStart()) { + if (text.charAt(pos) == '\n') { + ++line; + column = 0; + } else { + ++column; + } + ++pos; + } + + // Match the next token. + if (matcher.regionStart() == matcher.regionEnd()) { + // EOF + currentToken = ""; + } else { + matcher.usePattern(TOKEN); + if (matcher.lookingAt()) { + currentToken = matcher.group(); + matcher.region(matcher.end(), matcher.regionEnd()); + } else { + // Take one character. + currentToken = String.valueOf(text.charAt(pos)); + matcher.region(pos + 1, matcher.regionEnd()); + } + + skipWhitespace(); + } + } + + /** + * Skip over any whitespace so that the matcher region starts at the next + * token. + */ + private void skipWhitespace() { + matcher.usePattern(WHITESPACE); + if (matcher.lookingAt()) { + matcher.region(matcher.end(), matcher.regionEnd()); + } + } + + /** + * If the next token exactly matches {@code token}, consume it and return + * {@code true}. Otherwise, return {@code false} without doing anything. + */ + public boolean tryConsume(String token) { + if (currentToken.equals(token)) { + nextToken(); + return true; + } else { + return false; + } + } + + /** + * If the next token exactly matches {@code token}, consume it. Otherwise, + * throw a {@link ParseException}. + */ + public void consume(String token) throws ParseException { + if (!tryConsume(token)) { + throw parseException("Expected \"" + token + "\"."); + } + } + + /** + * Returns {@code true} if the next token is an integer, but does + * not consume it. + */ + public boolean lookingAtInteger() { + if (currentToken.length() == 0) { + return false; + } + + char c = currentToken.charAt(0); + return ('0' <= c && c <= '9') || + c == '-' || c == '+'; + } + + /** + * If the next token is an identifier, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public String consumeIdentifier() throws ParseException { + for (int i = 0; i < currentToken.length(); i++) { + char c = currentToken.charAt(i); + if (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + (c == '_') || (c == '.')) { + // OK + } else { + throw parseException("Expected identifier."); + } + } + + String result = currentToken; + nextToken(); + return result; + } + + /** + * If the next token is a 32-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeInt32() throws ParseException { + try { + int result = parseInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 32-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeUInt32() throws ParseException { + try { + int result = parseUInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeInt64() throws ParseException { + try { + long result = parseInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeUInt64() throws ParseException { + try { + long result = parseUInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a double, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public double consumeDouble() throws ParseException { + // We need to parse infinity and nan separately because + // Double.parseDouble() does not accept "inf", "infinity", or "nan". + if (DOUBLE_INFINITY.matcher(currentToken).matches()) { + boolean negative = currentToken.startsWith("-"); + nextToken(); + return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + if (currentToken.equalsIgnoreCase("nan")) { + nextToken(); + return Double.NaN; + } + try { + double result = Double.parseDouble(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a float, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public float consumeFloat() throws ParseException { + // We need to parse infinity and nan separately because + // Float.parseFloat() does not accept "inf", "infinity", or "nan". + if (FLOAT_INFINITY.matcher(currentToken).matches()) { + boolean negative = currentToken.startsWith("-"); + nextToken(); + return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; + } + if (FLOAT_NAN.matcher(currentToken).matches()) { + nextToken(); + return Float.NaN; + } + try { + float result = Float.parseFloat(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a boolean, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public boolean consumeBoolean() throws ParseException { + if (currentToken.equals("true")) { + nextToken(); + return true; + } else if (currentToken.equals("false")) { + nextToken(); + return false; + } else { + throw parseException("Expected \"true\" or \"false\"."); + } + } + + /** + * If the next token is a string, consume it and return its (unescaped) + * value. Otherwise, throw a {@link ParseException}. + */ + public String consumeString() throws ParseException { + return consumeByteString().toStringUtf8(); + } + + /** + * If the next token is a string, consume it, unescape it as a + * {@link ByteString}, and return it. Otherwise, throw a + * {@link ParseException}. + */ + public ByteString consumeByteString() throws ParseException { + char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; + if (quote != '\"' && quote != '\'') { + throw parseException("Expected string."); + } + + if (currentToken.length() < 2 || + currentToken.charAt(currentToken.length() - 1) != quote) { + throw parseException("String missing ending quote."); + } + + try { + String escaped = currentToken.substring(1, currentToken.length() - 1); + ByteString result = unescapeBytes(escaped); + nextToken(); + return result; + } catch (InvalidEscapeSequence e) { + throw parseException(e.getMessage()); + } + } + + /** + * Returns a {@link ParseException} with the current line and column + * numbers in the description, suitable for throwing. + */ + public ParseException parseException(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (line + 1) + ":" + (column + 1) + ": " + description); + } + + /** + * Returns a {@link ParseException} with the line and column numbers of + * the previous token in the description, suitable for throwing. + */ + public ParseException parseExceptionPreviousToken(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse an integer. + */ + private ParseException integerParseException(NumberFormatException e) { + return parseException("Couldn't parse integer: " + e.getMessage()); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse a float or double. + */ + private ParseException floatParseException(NumberFormatException e) { + return parseException("Couldn't parse number: " + e.getMessage()); + } + } + + /** Thrown when parsing an invalid text format message. */ + public static class ParseException extends IOException { + public ParseException(String message) { + super(message); + } + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. + */ + public static void merge(Readable input, + Message.Builder builder) + throws ParseException, IOException { + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. + */ + public static void merge(CharSequence input, + Message.Builder builder) + throws ParseException { + merge(input, ExtensionRegistry.getEmptyRegistry(), builder); + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. Extensions will be recognized if they are + * registered in {@code extensionRegistry}. + */ + public static void merge(Readable input, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException, IOException { + // Read the entire input to a String then parse that. + + // If StreamTokenizer were not quite so crippled, or if there were a kind + // of Reader that could read in chunks that match some particular regex, + // or if we wanted to write a custom Reader to tokenize our stream, then + // we would not have to read to one big String. Alas, none of these is + // the case. Oh well. + + merge(toStringBuilder(input), extensionRegistry, builder); + } + + private static final int BUFFER_SIZE = 4096; + + // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) + // overhead is worthwhile + private static StringBuilder toStringBuilder(Readable input) + throws IOException { + StringBuilder text = new StringBuilder(); + CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); + while (true) { + int n = input.read(buffer); + if (n == -1) { + break; + } + buffer.flip(); + text.append(buffer, 0, n); + } + return text; + } + + /** + * Parse a text-format message from {@code input} and merge the contents + * into {@code builder}. Extensions will be recognized if they are + * registered in {@code extensionRegistry}. + */ + public static void merge(CharSequence input, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException { + Tokenizer tokenizer = new Tokenizer(input); + + while (!tokenizer.atEnd()) { + mergeField(tokenizer, extensionRegistry, builder); + } + } + + /** + * Parse a single field from {@code tokenizer} and merge it into + * {@code builder}. + */ + private static void mergeField(Tokenizer tokenizer, + ExtensionRegistry extensionRegistry, + Message.Builder builder) + throws ParseException { + FieldDescriptor field; + Descriptor type = builder.getDescriptorForType(); + ExtensionRegistry.ExtensionInfo extension = null; + + if (tokenizer.tryConsume("[")) { + // An extension. + StringBuilder name = new StringBuilder(tokenizer.consumeIdentifier()); + while (tokenizer.tryConsume(".")) { + name.append("."); + name.append(tokenizer.consumeIdentifier()); + } + + extension = extensionRegistry.findExtensionByName(name.toString()); + + if (extension == null) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" not found in the ExtensionRegistry."); + } else if (extension.descriptor.getContainingType() != type) { + throw tokenizer.parseExceptionPreviousToken( + "Extension \"" + name + "\" does not extend message type \"" + + type.getFullName() + "\"."); + } + + tokenizer.consume("]"); + + field = extension.descriptor; + } else { + String name = tokenizer.consumeIdentifier(); + field = type.findFieldByName(name); + + // Group names are expected to be capitalized as they appear in the + // .proto file, which actually matches their type names, not their field + // names. + if (field == null) { + // Explicitly specify US locale so that this code does not break when + // executing in Turkey. + String lowerName = name.toLowerCase(Locale.US); + field = type.findFieldByName(lowerName); + // If the case-insensitive match worked but the field is NOT a group, + if (field != null && field.getType() != FieldDescriptor.Type.GROUP) { + field = null; + } + } + // Again, special-case group names as described above. + if (field != null && field.getType() == FieldDescriptor.Type.GROUP && + !field.getMessageType().getName().equals(name)) { + field = null; + } + + if (field == null) { + throw tokenizer.parseExceptionPreviousToken( + "Message type \"" + type.getFullName() + + "\" has no field named \"" + name + "\"."); + } + } + + Object value = null; + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + tokenizer.tryConsume(":"); // optional + + String endToken; + if (tokenizer.tryConsume("<")) { + endToken = ">"; + } else { + tokenizer.consume("{"); + endToken = "}"; + } + + Message.Builder subBuilder; + if (extension == null) { + subBuilder = builder.newBuilderForField(field); + } else { + subBuilder = extension.defaultInstance.newBuilderForType(); + } + + while (!tokenizer.tryConsume(endToken)) { + if (tokenizer.atEnd()) { + throw tokenizer.parseException( + "Expected \"" + endToken + "\"."); + } + mergeField(tokenizer, extensionRegistry, subBuilder); + } + + value = subBuilder.build(); + + } else { + tokenizer.consume(":"); + + switch (field.getType()) { + case INT32: + case SINT32: + case SFIXED32: + value = tokenizer.consumeInt32(); + break; + + case INT64: + case SINT64: + case SFIXED64: + value = tokenizer.consumeInt64(); + break; + + case UINT32: + case FIXED32: + value = tokenizer.consumeUInt32(); + break; + + case UINT64: + case FIXED64: + value = tokenizer.consumeUInt64(); + break; + + case FLOAT: + value = tokenizer.consumeFloat(); + break; + + case DOUBLE: + value = tokenizer.consumeDouble(); + break; + + case BOOL: + value = tokenizer.consumeBoolean(); + break; + + case STRING: + value = tokenizer.consumeString(); + break; + + case BYTES: + value = tokenizer.consumeByteString(); + break; + + case ENUM: { + EnumDescriptor enumType = field.getEnumType(); + + if (tokenizer.lookingAtInteger()) { + int number = tokenizer.consumeInt32(); + value = enumType.findValueByNumber(number); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value with number " + number + "."); + } + } else { + String id = tokenizer.consumeIdentifier(); + value = enumType.findValueByName(id); + if (value == null) { + throw tokenizer.parseExceptionPreviousToken( + "Enum type \"" + enumType.getFullName() + + "\" has no value named \"" + id + "\"."); + } + } + + break; + } + + case MESSAGE: + case GROUP: + throw new RuntimeException("Can't get here."); + } + } + + if (field.isRepeated()) { + builder.addRepeatedField(field, value); + } else { + builder.setField(field, value); + } + } + + // ================================================================= + // Utility functions + // + // Some of these methods are package-private because Descriptors.java uses + // them. + + /** + * Escapes bytes in the format used in protocol buffer text format, which + * is the same as the format used for C string literals. All bytes + * that are not printable 7-bit ASCII characters are escaped, as well as + * backslash, single-quote, and double-quote characters. Characters for + * which no defined short-hand escape sequence is defined will be escaped + * using 3-digit octal sequences. + */ + static String escapeBytes(ByteString input) { + StringBuilder builder = new StringBuilder(input.size()); + for (int i = 0; i < input.size(); i++) { + byte b = input.byteAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case 0x07: builder.append("\\a" ); break; + case '\b': builder.append("\\b" ); break; + case '\f': builder.append("\\f" ); break; + case '\n': builder.append("\\n" ); break; + case '\r': builder.append("\\r" ); break; + case '\t': builder.append("\\t" ); break; + case 0x0b: builder.append("\\v" ); break; + case '\\': builder.append("\\\\"); break; + case '\'': builder.append("\\\'"); break; + case '"' : builder.append("\\\""); break; + default: + if (b >= 0x20) { + builder.append((char) b); + } else { + builder.append('\\'); + builder.append((char) ('0' + ((b >>> 6) & 3))); + builder.append((char) ('0' + ((b >>> 3) & 7))); + builder.append((char) ('0' + (b & 7))); + } + break; + } + } + return builder.toString(); + } + + /** + * Un-escape a byte sequence as escaped using + * {@link #escapeBytes(ByteString)}. Two-digit hex escapes (starting with + * "\x") are also recognized. + */ + static ByteString unescapeBytes(CharSequence input) + throws InvalidEscapeSequence { + byte[] result = new byte[input.length()]; + int pos = 0; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '\\') { + if (i + 1 < input.length()) { + ++i; + c = input.charAt(i); + if (isOctal(c)) { + // Octal escape. + int code = digitValue(c); + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + } else { + switch (c) { + case 'a' : result[pos++] = 0x07; break; + case 'b' : result[pos++] = '\b'; break; + case 'f' : result[pos++] = '\f'; break; + case 'n' : result[pos++] = '\n'; break; + case 'r' : result[pos++] = '\r'; break; + case 't' : result[pos++] = '\t'; break; + case 'v' : result[pos++] = 0x0b; break; + case '\\': result[pos++] = '\\'; break; + case '\'': result[pos++] = '\''; break; + case '"' : result[pos++] = '\"'; break; + + case 'x': + // hex escape + int code = 0; + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = digitValue(input.charAt(i)); + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\x' with no digits"); + } + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = code * 16 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + break; + + default: + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\" + c + "'"); + } + } + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\' at end of string."); + } + } else { + result[pos++] = (byte)c; + } + } + + return ByteString.copyFrom(result, 0, pos); + } + + /** + * Thrown by {@link TextFormat#unescapeBytes} and + * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. + */ + static class InvalidEscapeSequence extends IOException { + public InvalidEscapeSequence(String description) { + super(description); + } + } + + /** + * Like {@link #escapeBytes(ByteString)}, but escapes a text string. + * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped + * individually as a 3-digit octal escape. Yes, it's weird. + */ + static String escapeText(String input) { + return escapeBytes(ByteString.copyFromUtf8(input)); + } + + /** + * Un-escape a text string as escaped using {@link #escapeText(String)}. + * Two-digit hex escapes (starting with "\x") are also recognized. + */ + static String unescapeText(String input) throws InvalidEscapeSequence { + return unescapeBytes(input).toStringUtf8(); + } + + /** Is this an octal digit? */ + private static boolean isOctal(char c) { + return '0' <= c && c <= '7'; + } + + /** Is this a hex digit? */ + private static boolean isHex(char c) { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); + } + + /** + * Interpret a character as a digit (in any base up to 36) and return the + * numeric value. This is like {@code Character.digit()} but we don't accept + * non-ASCII digits. + */ + private static int digitValue(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'z') { + return c - 'a' + 10; + } else { + return c - 'A' + 10; + } + } + + /** + * Parse a 32-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static int parseInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, true, false); + } + + /** + * Parse a 32-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code int} when returned since Java has + * no unsigned integer type. + */ + static int parseUInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, false, false); + } + + /** + * Parse a 64-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static long parseInt64(String text) throws NumberFormatException { + return parseInteger(text, true, true); + } + + /** + * Parse a 64-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code long} when returned since Java has + * no unsigned long type. + */ + static long parseUInt64(String text) throws NumberFormatException { + return parseInteger(text, false, true); + } + + private static long parseInteger(String text, + boolean isSigned, + boolean isLong) + throws NumberFormatException { + int pos = 0; + + boolean negative = false; + if (text.startsWith("-", pos)) { + if (!isSigned) { + throw new NumberFormatException("Number must be positive: " + text); + } + ++pos; + negative = true; + } + + int radix = 10; + if (text.startsWith("0x", pos)) { + pos += 2; + radix = 16; + } else if (text.startsWith("0", pos)) { + radix = 8; + } + + String numberText = text.substring(pos); + + long result = 0; + if (numberText.length() < 16) { + // Can safely assume no overflow. + result = Long.parseLong(numberText, radix); + if (negative) { + result = -result; + } + + // Check bounds. + // No need to check for 64-bit numbers since they'd have to be 16 chars + // or longer to overflow. + if (!isLong) { + if (isSigned) { + if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (result >= (1L << 32) || result < 0) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } + } else { + BigInteger bigValue = new BigInteger(numberText, radix); + if (negative) { + bigValue = bigValue.negate(); + } + + // Check bounds. + if (!isLong) { + if (isSigned) { + if (bigValue.bitLength() > 31) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 32) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } else { + if (isSigned) { + if (bigValue.bitLength() > 63) { + throw new NumberFormatException( + "Number out of range for 64-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 64) { + throw new NumberFormatException( + "Number out of range for 64-bit unsigned integer: " + text); + } + } + } + + result = bigValue.longValue(); + } + + return result; + } +} diff --git a/java/src/main/java/com/google/protobuf/UninitializedMessageException.java b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java new file mode 100644 index 00000000..61c3e248 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UninitializedMessageException.java @@ -0,0 +1,146 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FieldDescriptor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Thrown when attempting to build a protocol message that is missing required + * fields. This is a {@code RuntimeException} because it normally represents + * a programming error: it happens when some code which constructs a message + * fails to set all the fields. {@code parseFrom()} methods do not + * throw this; they throw an {@link InvalidProtocolBufferException} if + * required fields are missing, because it is not a programming error to + * receive an incomplete message. In other words, + * {@code UninitializedMessageException} should never be thrown by correct + * code, but {@code InvalidProtocolBufferException} might be. + * + * @author kenton@google.com Kenton Varda + */ +public class UninitializedMessageException extends RuntimeException { + public UninitializedMessageException(Message message) { + this(findMissingFields(message)); + } + + private UninitializedMessageException(List missingFields) { + super(buildDescription(missingFields)); + this.missingFields = missingFields; + } + + private final List missingFields; + + /** + * Get a list of human-readable names of required fields missing from this + * message. Each name is a full path to a field, e.g. "foo.bar[5].baz". + */ + public List getMissingFields() { + return Collections.unmodifiableList(missingFields); + } + + /** + * Converts this exception to an {@link InvalidProtocolBufferException}. + * When a parsed message is missing required fields, this should be thrown + * instead of {@code UninitializedMessageException}. + */ + public InvalidProtocolBufferException asInvalidProtocolBufferException() { + return new InvalidProtocolBufferException(getMessage()); + } + + /** Construct the description string for this exception. */ + private static String buildDescription(List missingFields) { + StringBuilder description = + new StringBuilder("Message missing required fields: "); + boolean first = true; + for (String field : missingFields) { + if (first) { + first = false; + } else { + description.append(", "); + } + description.append(field); + } + return description.toString(); + } + + /** + * Populates {@code this.missingFields} with the full "path" of each + * missing required field in the given message. + */ + private static List findMissingFields(Message message) { + List results = new ArrayList(); + findMissingFields(message, "", results); + return results; + } + + /** Recursive helper implementing {@link #findMissingFields(Message)}. */ + private static void findMissingFields(Message message, String prefix, + List results) { + for (FieldDescriptor field : message.getDescriptorForType().getFields()) { + if (field.isRequired() && !message.hasField(field)) { + results.add(prefix + field.getName()); + } + } + + for (Map.Entry entry : + message.getAllFields().entrySet()) { + FieldDescriptor field = entry.getKey(); + Object value = entry.getValue(); + + if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) { + if (field.isRepeated()) { + int i = 0; + for (Object element : (List) value) { + findMissingFields((Message) element, + subMessagePrefix(prefix, field, i++), + results); + } + } else { + if (message.hasField(field)) { + findMissingFields((Message) value, + subMessagePrefix(prefix, field, -1), + results); + } + } + } + } + } + + private static String subMessagePrefix(String prefix, + FieldDescriptor field, + int index) { + StringBuilder result = new StringBuilder(prefix); + if (field.isExtension()) { + result.append('(') + .append(field.getFullName()) + .append(')'); + } else { + result.append(field.getName()); + } + if (index != -1) { + result.append('[') + .append(index) + .append(']'); + } + result.append('.'); + return result.toString(); + } +} diff --git a/java/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java new file mode 100644 index 00000000..ba4dd5ad --- /dev/null +++ b/java/src/main/java/com/google/protobuf/UnknownFieldSet.java @@ -0,0 +1,746 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.TreeMap; +import java.util.List; +import java.util.Map; + +/** + * {@code UnknownFieldSet} is used to keep track of fields which were seen when + * parsing a protocol message but whose field numbers or types are unrecognized. + * This most frequently occurs when new fields are added to a message type + * and then messages containing those feilds are read by old software that was + * compiled before the new types were added. + * + *

Every {@link Message} contains an {@code UnknownFieldSet} (and every + * {@link Message.Builder} contains an {@link UnknownFieldSet.Builder}). + * + *

Most users will never need to use this class. + * + * @author kenton@google.com Kenton Varda + */ +public final class UnknownFieldSet { + private UnknownFieldSet() {} + + /** Create a new {@link UnknownFieldSet.Builder}. */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Create a new {@link UnknownFieldSet.Builder} and initialize it to be a copy + * of {@code copyFrom}. + */ + public static Builder newBuilder(UnknownFieldSet copyFrom) { + return new Builder().mergeFrom(copyFrom); + } + + /** Get an empty {@code UnknownFieldSet}. */ + public static UnknownFieldSet getDefaultInstance() { + return defaultInstance; + } + private static UnknownFieldSet defaultInstance = + new UnknownFieldSet(Collections.emptyMap()); + + /** + * Construct an {@code UnknownFieldSet} around the given map. The map is + * expected to be immutable. + */ + private UnknownFieldSet(Map fields) { + this.fields = fields; + } + private Map fields; + + /** Get a map of fields in the set by number. */ + public Map asMap() { + return fields; + } + + /** Check if the given field number is present in the set. */ + public boolean hasField(int number) { + return fields.containsKey(number); + } + + /** + * Get a field by number. Returns an empty field if not present. Never + * returns {@code null}. + */ + public Field getField(int number) { + Field result = fields.get(number); + return (result == null) ? Field.getDefaultInstance() : result; + } + + /** Serializes the set and writes it to {@code output}. */ + public void writeTo(CodedOutputStream output) throws IOException { + for (Map.Entry entry : fields.entrySet()) { + entry.getValue().writeTo(entry.getKey(), output); + } + } + + /** + * Converts the set to a string in protocol buffer text format. This is + * just a trivial wrapper around + * {@link TextFormat#printToString(UnknownFieldSet)}. + */ + public final String toString() { + return TextFormat.printToString(this); + } + + /** + * Serializes the message to a {@code ByteString} and returns it. This is + * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final ByteString toByteString() { + try { + ByteString.CodedBuilder out = + ByteString.newCodedBuilder(getSerializedSize()); + writeTo(out.getCodedOutput()); + return out.build(); + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Serializes the message to a {@code byte} array and returns it. This is + * just a trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final byte[] toByteArray() { + try { + byte[] result = new byte[getSerializedSize()]; + CodedOutputStream output = CodedOutputStream.newInstance(result); + writeTo(output); + output.checkNoSpaceLeft(); + return result; + } catch (IOException e) { + throw new RuntimeException( + "Serializing to a byte array threw an IOException " + + "(should never happen).", e); + } + } + + /** + * Serializes the message and writes it to {@code output}. This is just a + * trivial wrapper around {@link #writeTo(CodedOutputStream)}. + */ + public final void writeTo(OutputStream output) throws IOException { + CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); + writeTo(codedOutput); + codedOutput.flush(); + } + + /** Get the number of bytes required to encode this set. */ + public int getSerializedSize() { + int result = 0; + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSize(entry.getKey()); + } + return result; + } + + /** + * Serializes the set and writes it to {@code output} using + * {@code MessageSet} wire format. + */ + public void writeAsMessageSetTo(CodedOutputStream output) + throws IOException { + for (Map.Entry entry : fields.entrySet()) { + entry.getValue().writeAsMessageSetExtensionTo( + entry.getKey(), output); + } + } + + /** + * Get the number of bytes required to encode this set using + * {@code MessageSet} wire format. + */ + public int getSerializedSizeAsMessageSet() { + int result = 0; + for (Map.Entry entry : fields.entrySet()) { + result += entry.getValue().getSerializedSizeAsMessageSetExtension( + entry.getKey()); + } + return result; + } + + /** Parse an {@code UnknownFieldSet} from the given input stream. */ + static public UnknownFieldSet parseFrom(CodedInputStream input) + throws IOException { + return newBuilder().mergeFrom(input).build(); + } + + /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ + public static UnknownFieldSet parseFrom(ByteString data) + throws InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).build(); + } + + /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ + public static UnknownFieldSet parseFrom(byte[] data) + throws InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).build(); + } + + /** Parse an {@code UnknownFieldSet} from {@code input} and return it. */ + public static UnknownFieldSet parseFrom(InputStream input) + throws IOException { + return newBuilder().mergeFrom(input).build(); + } + + /** + * Builder for {@link UnknownFieldSet}s. + * + *

Note that this class maintains {@link Field.Builder}s for all fields + * in the set. Thus, adding one element to an existing {@link Field} does not + * require making a copy. This is important for efficient parsing of + * unknown repeated fields. However, it implies that {@link Field}s cannot + * be constructed independently, nor can two {@link UnknownFieldSet}s share + * the same {@code Field} object. + * + *

Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}. + */ + public static final class Builder { + private Builder() {} + private Map fields = new TreeMap(); + + // Optimization: We keep around a builder for the last field that was + // modified so that we can efficiently add to it multiple times in a + // row (important when parsing an unknown repeated field). + int lastFieldNumber = 0; + Field.Builder lastField = null; + + /** + * Get a field builder for the given field number which includes any + * values that already exist. + */ + private Field.Builder getFieldBuilder(int number) { + if (lastField != null) { + if (number == lastFieldNumber) { + return lastField; + } + // Note: addField() will reset lastField and lastFieldNumber. + addField(lastFieldNumber, lastField.build()); + } + if (number == 0) { + return null; + } else { + Field existing = fields.get(number); + lastFieldNumber = number; + lastField = Field.newBuilder(); + if (existing != null) { + lastField.mergeFrom(existing); + } + return lastField; + } + } + + /** + * Build the {@link UnknownFieldSet} and return it. + * + *

Once {@code build()} has been called, the {@code Builder} will no + * longer be usable. Calling any method after {@code build()} will throw + * {@code NullPointerException}. + */ + public UnknownFieldSet build() { + getFieldBuilder(0); // Force lastField to be built. + UnknownFieldSet result; + if (fields.isEmpty()) { + result = getDefaultInstance(); + } else { + result = new UnknownFieldSet(Collections.unmodifiableMap(fields)); + } + fields = null; + return result; + } + + /** Reset the builder to an empty set. */ + public Builder clear() { + fields = new TreeMap(); + lastFieldNumber = 0; + lastField = null; + return this; + } + + /** + * Merge the fields from {@code other} into this set. If a field number + * exists in both sets, {@code other}'s values for that field will be + * appended to the values in this set. + */ + public Builder mergeFrom(UnknownFieldSet other) { + if (other != getDefaultInstance()) { + for (Map.Entry entry : other.fields.entrySet()) { + mergeField(entry.getKey(), entry.getValue()); + } + } + return this; + } + + /** + * Add a field to the {@code UnknownFieldSet}. If a field with the same + * number already exists, the two are merged. + */ + public Builder mergeField(int number, Field field) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + if (hasField(number)) { + getFieldBuilder(number).mergeFrom(field); + } else { + // Optimization: We could call getFieldBuilder(number).mergeFrom(field) + // in this case, but that would create a copy of the Field object. + // We'd rather reuse the one passed to us, so call addField() instead. + addField(number, field); + } + return this; + } + + /** + * Convenience method for merging a new field containing a single varint + * value. This is used in particular when an unknown enum value is + * encountered. + */ + public Builder mergeVarintField(int number, int value) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + getFieldBuilder(number).addVarint(value); + return this; + } + + /** Check if the given field number is present in the set. */ + public boolean hasField(int number) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + return number == lastFieldNumber || fields.containsKey(number); + } + + /** + * Add a field to the {@code UnknownFieldSet}. If a field with the same + * number already exists, it is removed. + */ + public Builder addField(int number, Field field) { + if (number == 0) { + throw new IllegalArgumentException("Zero is not a valid field number."); + } + if (lastField != null && lastFieldNumber == number) { + // Discard this. + lastField = null; + lastFieldNumber = 0; + } + fields.put(number, field); + return this; + } + + /** + * Get all present {@code Field}s as an immutable {@code Map}. If more + * fields are added, the changes may or may not be reflected in this map. + */ + public Map asMap() { + getFieldBuilder(0); // Force lastField to be built. + return Collections.unmodifiableMap(fields); + } + + /** + * Parse an entire message from {@code input} and merge its fields into + * this set. + */ + public Builder mergeFrom(CodedInputStream input) throws IOException { + while (true) { + int tag = input.readTag(); + if (tag == 0 || !mergeFieldFrom(tag, input)) { + break; + } + } + return this; + } + + /** + * Parse a single field from {@code input} and merge it into this set. + * @param tag The field's tag number, which was already parsed. + * @return {@code false} if the tag is an engroup tag. + */ + public boolean mergeFieldFrom(int tag, CodedInputStream input) + throws IOException { + int number = WireFormat.getTagFieldNumber(tag); + switch (WireFormat.getTagWireType(tag)) { + case WireFormat.WIRETYPE_VARINT: + getFieldBuilder(number).addVarint(input.readInt64()); + return true; + case WireFormat.WIRETYPE_FIXED64: + getFieldBuilder(number).addFixed64(input.readFixed64()); + return true; + case WireFormat.WIRETYPE_LENGTH_DELIMITED: + getFieldBuilder(number).addLengthDelimited(input.readBytes()); + return true; + case WireFormat.WIRETYPE_START_GROUP: { + UnknownFieldSet.Builder subBuilder = UnknownFieldSet.newBuilder(); + input.readUnknownGroup(number, subBuilder); + getFieldBuilder(number).addGroup(subBuilder.build()); + return true; + } + case WireFormat.WIRETYPE_END_GROUP: + return false; + case WireFormat.WIRETYPE_FIXED32: + getFieldBuilder(number).addFixed32(input.readFixed32()); + return true; + default: + throw InvalidProtocolBufferException.invalidWireType(); + } + } + + /** + * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(ByteString data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = data.newCodedInput(); + mergeFrom(input); + input.checkLastTagWas(0); + return this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a ByteString threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Parse {@code data} as an {@code UnknownFieldSet} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(byte[] data) + throws InvalidProtocolBufferException { + try { + CodedInputStream input = CodedInputStream.newInstance(data); + mergeFrom(input); + input.checkLastTagWas(0); + return this; + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException( + "Reading from a byte array threw an IOException (should " + + "never happen).", e); + } + } + + /** + * Parse an {@code UnknownFieldSet} from {@code input} and merge it with the + * set being built. This is just a small wrapper around + * {@link #mergeFrom(CodedInputStream)}. + */ + public Builder mergeFrom(InputStream input) throws IOException { + CodedInputStream codedInput = CodedInputStream.newInstance(input); + mergeFrom(codedInput); + codedInput.checkLastTagWas(0); + return this; + } + } + + /** + * Represents a single field in an {@code UnknownFieldSet}. + * + *

A {@code Field} consists of five lists of values. The lists correspond + * to the five "wire types" used in the protocol buffer binary format. + * The wire type of each field can be determined from the encoded form alone, + * without knowing the field's declared type. So, we are able to parse + * unknown values at least this far and separate them. Normally, only one + * of the five lists will contain any values, since it is impossible to + * define a valid message type that declares two different types for the + * same field number. However, the code is designed to allow for the case + * where the same unknown field number is encountered using multiple different + * wire types. + * + *

{@code Field} is an immutable class. To construct one, you must use a + * {@link Field.Builder}. + * + * @see UnknownFieldSet + */ + public static final class Field { + private Field() {} + + /** Construct a new {@link Builder}. */ + public static Builder newBuilder() { + return new Builder(); + } + + /** + * Construct a new {@link Builder} and initialize it to a copy of + * {@code copyFrom}. + */ + public static Builder newBuilder(Field copyFrom) { + return new Builder().mergeFrom(copyFrom); + } + + /** Get an empty {@code Field}. */ + public static Field getDefaultInstance() { + return defaultInstance; + } + private static Field defaultInstance = newBuilder().build(); + + /** Get the list of varint values for this field. */ + public List getVarintList() { return varint; } + + /** Get the list of fixed32 values for this field. */ + public List getFixed32List() { return fixed32; } + + /** Get the list of fixed64 values for this field. */ + public List getFixed64List() { return fixed64; } + + /** Get the list of length-delimited values for this field. */ + public List getLengthDelimitedList() { return lengthDelimited; } + + /** + * Get the list of embedded group values for this field. These are + * represented using {@link UnknownFieldSet}s rather than {@link Message}s + * since the group's type is presumably unknown. + */ + public List getGroupList() { return group; } + + /** + * Serializes the field, including field number, and writes it to + * {@code output}. + */ + public void writeTo(int fieldNumber, CodedOutputStream output) + throws IOException { + for (long value : varint) { + output.writeUInt64(fieldNumber, value); + } + for (int value : fixed32) { + output.writeFixed32(fieldNumber, value); + } + for (long value : fixed64) { + output.writeFixed64(fieldNumber, value); + } + for (ByteString value : lengthDelimited) { + output.writeBytes(fieldNumber, value); + } + for (UnknownFieldSet value : group) { + output.writeUnknownGroup(fieldNumber, value); + } + } + + /** + * Get the number of bytes required to encode this field, including field + * number. + */ + public int getSerializedSize(int fieldNumber) { + int result = 0; + for (long value : varint) { + result += CodedOutputStream.computeUInt64Size(fieldNumber, value); + } + for (int value : fixed32) { + result += CodedOutputStream.computeFixed32Size(fieldNumber, value); + } + for (long value : fixed64) { + result += CodedOutputStream.computeFixed64Size(fieldNumber, value); + } + for (ByteString value : lengthDelimited) { + result += CodedOutputStream.computeBytesSize(fieldNumber, value); + } + for (UnknownFieldSet value : group) { + result += CodedOutputStream.computeUnknownGroupSize(fieldNumber, value); + } + return result; + } + + /** + * Serializes the field, including field number, and writes it to + * {@code output}, using {@code MessageSet} wire format. + */ + public void writeAsMessageSetExtensionTo( + int fieldNumber, + CodedOutputStream output) + throws IOException { + for (ByteString value : lengthDelimited) { + output.writeRawMessageSetExtension(fieldNumber, value); + } + } + + /** + * Get the number of bytes required to encode this field, including field + * number, using {@code MessageSet} wire format. + */ + public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { + int result = 0; + for (ByteString value : lengthDelimited) { + result += CodedOutputStream.computeRawMessageSetExtensionSize( + fieldNumber, value); + } + return result; + } + + private List varint; + private List fixed32; + private List fixed64; + private List lengthDelimited; + private List group; + + /** + * Used to build a {@link Field} within an {@link UnknownFieldSet}. + * + *

Use {@link Field#newBuilder()} to construct a {@code Builder}. + */ + public static final class Builder { + private Builder() {} + private Field result = new Field(); + + /** + * Build the field. After {@code build()} has been called, the + * {@code Builder} is no longer usable. Calling any other method will + * throw a {@code NullPointerException}. + */ + public Field build() { + if (result.varint == null) { + result.varint = Collections.emptyList(); + } else { + result.varint = Collections.unmodifiableList(result.varint); + } + if (result.fixed32 == null) { + result.fixed32 = Collections.emptyList(); + } else { + result.fixed32 = Collections.unmodifiableList(result.fixed32); + } + if (result.fixed64 == null) { + result.fixed64 = Collections.emptyList(); + } else { + result.fixed64 = Collections.unmodifiableList(result.fixed64); + } + if (result.lengthDelimited == null) { + result.lengthDelimited = Collections.emptyList(); + } else { + result.lengthDelimited = + Collections.unmodifiableList(result.lengthDelimited); + } + if (result.group == null) { + result.group = Collections.emptyList(); + } else { + result.group = Collections.unmodifiableList(result.group); + } + + Field returnMe = result; + result = null; + return returnMe; + } + + /** Discard the field's contents. */ + public Builder clear() { + result = new Field(); + return this; + } + + /** + * Merge the values in {@code other} into this field. For each list + * of values, {@code other}'s values are append to the ones in this + * field. + */ + public Builder mergeFrom(Field other) { + if (!other.varint.isEmpty()) { + if (result.varint == null) { + result.varint = new ArrayList(); + } + result.varint.addAll(other.varint); + } + if (!other.fixed32.isEmpty()) { + if (result.fixed32 == null) { + result.fixed32 = new ArrayList(); + } + result.fixed32.addAll(other.fixed32); + } + if (!other.fixed64.isEmpty()) { + if (result.fixed64 == null) { + result.fixed64 = new ArrayList(); + } + result.fixed64.addAll(other.fixed64); + } + if (!other.lengthDelimited.isEmpty()) { + if (result.lengthDelimited == null) { + result.lengthDelimited = new ArrayList(); + } + result.lengthDelimited.addAll(other.lengthDelimited); + } + if (!other.group.isEmpty()) { + if (result.group == null) { + result.group = new ArrayList(); + } + result.group.addAll(other.group); + } + return this; + } + + /** Add a varint value. */ + public Builder addVarint(long value) { + if (result.varint == null) { + result.varint = new ArrayList(); + } + result.varint.add(value); + return this; + } + + /** Add a fixed32 value. */ + public Builder addFixed32(int value) { + if (result.fixed32 == null) { + result.fixed32 = new ArrayList(); + } + result.fixed32.add(value); + return this; + } + + /** Add a fixed64 value. */ + public Builder addFixed64(long value) { + if (result.fixed64 == null) { + result.fixed64 = new ArrayList(); + } + result.fixed64.add(value); + return this; + } + + /** Add a length-delimited value. */ + public Builder addLengthDelimited(ByteString value) { + if (result.lengthDelimited == null) { + result.lengthDelimited = new ArrayList(); + } + result.lengthDelimited.add(value); + return this; + } + + /** Add an embedded group. */ + public Builder addGroup(UnknownFieldSet value) { + if (result.group == null) { + result.group = new ArrayList(); + } + result.group.add(value); + return this; + } + } + } +} diff --git a/java/src/main/java/com/google/protobuf/WireFormat.java b/java/src/main/java/com/google/protobuf/WireFormat.java new file mode 100644 index 00000000..31bfd402 --- /dev/null +++ b/java/src/main/java/com/google/protobuf/WireFormat.java @@ -0,0 +1,99 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +/** + * 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 protocol2} 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 WireFormat { + // Do not allow instantiation. + private WireFormat() {} + + 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(int tag) { + return tag & TAG_TYPE_MASK; + } + + /** Given a tag value, determines the field number (the upper 29 bits). */ + public static int getTagFieldNumber(int tag) { + return tag >>> TAG_TYPE_BITS; + } + + /** Makes a tag value given a field number and wire type. */ + static int makeTag(int fieldNumber, int wireType) { + return (fieldNumber << TAG_TYPE_BITS) | wireType; + } + + static int getWireFormatForFieldType(Descriptors.FieldDescriptor.Type type) { + switch (type) { + case DOUBLE : return WIRETYPE_FIXED64; + case FLOAT : return WIRETYPE_FIXED32; + case INT64 : return WIRETYPE_VARINT; + case UINT64 : return WIRETYPE_VARINT; + case INT32 : return WIRETYPE_VARINT; + case FIXED64 : return WIRETYPE_FIXED64; + case FIXED32 : return WIRETYPE_FIXED32; + case BOOL : return WIRETYPE_VARINT; + case STRING : return WIRETYPE_LENGTH_DELIMITED; + case GROUP : return WIRETYPE_START_GROUP; + case MESSAGE : return WIRETYPE_LENGTH_DELIMITED; + case BYTES : return WIRETYPE_LENGTH_DELIMITED; + case UINT32 : return WIRETYPE_VARINT; + case ENUM : return WIRETYPE_VARINT; + case SFIXED32: return WIRETYPE_FIXED32; + case SFIXED64: return WIRETYPE_FIXED64; + case SINT32 : return WIRETYPE_VARINT; + case SINT64 : return WIRETYPE_VARINT; + } + + throw new RuntimeException( + "There is no way to get here, but the compiler thinks otherwise."); + } + + // Field numbers for feilds in MessageSet wire format. + static final int MESSAGE_SET_ITEM = 1; + static final int MESSAGE_SET_TYPE_ID = 2; + static final int MESSAGE_SET_MESSAGE = 3; + + // Tag numbers. + static final int MESSAGE_SET_ITEM_TAG = + makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP); + static final int MESSAGE_SET_ITEM_END_TAG = + makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP); + static final int MESSAGE_SET_TYPE_ID_TAG = + makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT); + static final int MESSAGE_SET_MESSAGE_TAG = + makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED); +} diff --git a/java/src/test/java/com/google/protobuf/AbstractMessageTest.java b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java new file mode 100644 index 00000000..bbb88b86 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/AbstractMessageTest.java @@ -0,0 +1,362 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestOptimizeFor.TestOptimizedForSize; + +import junit.framework.TestCase; + +import java.util.Map; + +/** + * Unit test for {@link AbstractMessage}. + * + * @author kenton@google.com Kenton Varda + */ +public class AbstractMessageTest extends TestCase { + /** + * Extends AbstractMessage and wraps some other message object. The methods + * of the Message interface which aren't explicitly implemented by + * AbstractMessage are forwarded to the wrapped object. This allows us to + * test that AbstractMessage's implementations work even if the wrapped + * object does not use them. + */ + private static class AbstractMessageWrapper extends AbstractMessage { + private final Message wrappedMessage; + + public AbstractMessageWrapper(Message wrappedMessage) { + this.wrappedMessage = wrappedMessage; + } + + public Descriptors.Descriptor getDescriptorForType() { + return wrappedMessage.getDescriptorForType(); + } + public AbstractMessageWrapper getDefaultInstanceForType() { + return new AbstractMessageWrapper( + wrappedMessage.getDefaultInstanceForType()); + } + public Map getAllFields() { + return wrappedMessage.getAllFields(); + } + public boolean hasField(Descriptors.FieldDescriptor field) { + return wrappedMessage.hasField(field); + } + public Object getField(Descriptors.FieldDescriptor field) { + return wrappedMessage.getField(field); + } + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return wrappedMessage.getRepeatedFieldCount(field); + } + public Object getRepeatedField( + Descriptors.FieldDescriptor field, int index) { + return wrappedMessage.getRepeatedField(field, index); + } + public UnknownFieldSet getUnknownFields() { + return wrappedMessage.getUnknownFields(); + } + public Builder newBuilderForType() { + return new Builder(wrappedMessage.newBuilderForType()); + } + + static class Builder extends AbstractMessage.Builder { + private final Message.Builder wrappedBuilder; + + public Builder(Message.Builder wrappedBuilder) { + this.wrappedBuilder = wrappedBuilder; + } + + public AbstractMessageWrapper build() { + return new AbstractMessageWrapper(wrappedBuilder.build()); + } + public AbstractMessageWrapper buildPartial() { + return new AbstractMessageWrapper(wrappedBuilder.buildPartial()); + } + public Builder clone() { + return new Builder(wrappedBuilder.clone()); + } + public boolean isInitialized() { + return clone().buildPartial().isInitialized(); + } + public Descriptors.Descriptor getDescriptorForType() { + return wrappedBuilder.getDescriptorForType(); + } + public AbstractMessageWrapper getDefaultInstanceForType() { + return new AbstractMessageWrapper( + wrappedBuilder.getDefaultInstanceForType()); + } + public Map getAllFields() { + return wrappedBuilder.getAllFields(); + } + public Builder newBuilderForField(Descriptors.FieldDescriptor field) { + return new Builder(wrappedBuilder.newBuilderForField(field)); + } + public boolean hasField(Descriptors.FieldDescriptor field) { + return wrappedBuilder.hasField(field); + } + public Object getField(Descriptors.FieldDescriptor field) { + return wrappedBuilder.getField(field); + } + public Builder setField(Descriptors.FieldDescriptor field, Object value) { + wrappedBuilder.setField(field, value); + return this; + } + public Builder clearField(Descriptors.FieldDescriptor field) { + wrappedBuilder.clearField(field); + return this; + } + public int getRepeatedFieldCount(Descriptors.FieldDescriptor field) { + return wrappedBuilder.getRepeatedFieldCount(field); + } + public Object getRepeatedField( + Descriptors.FieldDescriptor field, int index) { + return wrappedBuilder.getRepeatedField(field, index); + } + public Builder setRepeatedField(Descriptors.FieldDescriptor field, + int index, Object value) { + wrappedBuilder.setRepeatedField(field, index, value); + return this; + } + public Builder addRepeatedField( + Descriptors.FieldDescriptor field, Object value) { + wrappedBuilder.addRepeatedField(field, value); + return this; + } + public UnknownFieldSet getUnknownFields() { + return wrappedBuilder.getUnknownFields(); + } + public Builder setUnknownFields(UnknownFieldSet unknownFields) { + wrappedBuilder.setUnknownFields(unknownFields); + return this; + } + } + } + + // ================================================================= + + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testClear() throws Exception { + AbstractMessageWrapper message = + new AbstractMessageWrapper.Builder( + TestAllTypes.newBuilder(TestUtil.getAllSet())) + .clear().build(); + TestUtil.assertClear((TestAllTypes) message.wrappedMessage); + } + + public void testCopy() throws Exception { + AbstractMessageWrapper message = + new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()) + .mergeFrom(TestUtil.getAllSet()).build(); + TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); + } + + public void testSerializedSize() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); + + assertEquals(message.getSerializedSize(), + abstractMessage.getSerializedSize()); + } + + public void testSerialization() throws Exception { + Message abstractMessage = new AbstractMessageWrapper(TestUtil.getAllSet()); + + TestUtil.assertAllFieldsSet( + TestAllTypes.parseFrom(abstractMessage.toByteString())); + + assertEquals(TestUtil.getAllSet().toByteString(), + abstractMessage.toByteString()); + } + + public void testParsing() throws Exception { + AbstractMessageWrapper.Builder builder = + new AbstractMessageWrapper.Builder(TestAllTypes.newBuilder()); + AbstractMessageWrapper message = + builder.mergeFrom(TestUtil.getAllSet().toByteString()).build(); + TestUtil.assertAllFieldsSet((TestAllTypes) message.wrappedMessage); + } + + public void testOptimizedForSize() throws Exception { + // We're mostly only checking that this class was compiled successfully. + TestOptimizedForSize message = + TestOptimizedForSize.newBuilder().setI(1).build(); + message = TestOptimizedForSize.parseFrom(message.toByteString()); + assertEquals(2, message.getSerializedSize()); + } + + // ----------------------------------------------------------------- + // Tests for isInitialized(). + + private static final TestRequired TEST_REQUIRED_UNINITIALIZED = + TestRequired.getDefaultInstance(); + private static final TestRequired TEST_REQUIRED_INITIALIZED = + TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); + + public void testIsInitialized() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + AbstractMessageWrapper.Builder abstractBuilder = + new AbstractMessageWrapper.Builder(builder); + + assertFalse(abstractBuilder.isInitialized()); + builder.setA(1); + assertFalse(abstractBuilder.isInitialized()); + builder.setB(1); + assertFalse(abstractBuilder.isInitialized()); + builder.setC(1); + assertTrue(abstractBuilder.isInitialized()); + } + + public void testForeignIsInitialized() throws Exception { + TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); + AbstractMessageWrapper.Builder abstractBuilder = + new AbstractMessageWrapper.Builder(builder); + + assertTrue(abstractBuilder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(abstractBuilder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); + assertTrue(abstractBuilder.isInitialized()); + + builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(abstractBuilder.isInitialized()); + + builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); + assertTrue(abstractBuilder.isInitialized()); + } + + // ----------------------------------------------------------------- + // Tests for mergeFrom + + static final TestAllTypes MERGE_SOURCE = + TestAllTypes.newBuilder() + .setOptionalInt32(1) + .setOptionalString("foo") + .setOptionalForeignMessage(ForeignMessage.getDefaultInstance()) + .addRepeatedString("bar") + .build(); + + static final TestAllTypes MERGE_DEST = + TestAllTypes.newBuilder() + .setOptionalInt64(2) + .setOptionalString("baz") + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build()) + .addRepeatedString("qux") + .build(); + + static final String MERGE_RESULT_TEXT = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_string: \"foo\"\n" + + "optional_foreign_message {\n" + + " c: 3\n" + + "}\n" + + "repeated_string: \"qux\"\n" + + "repeated_string: \"bar\"\n"; + + public void testMergeFrom() throws Exception { + AbstractMessageWrapper result = + new AbstractMessageWrapper.Builder( + TestAllTypes.newBuilder(MERGE_DEST)) + .mergeFrom(MERGE_SOURCE).build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + // ----------------------------------------------------------------- + // Tests for equals and hashCode + + public void testEqualsAndHashCode() { + TestAllTypes a = TestUtil.getAllSet(); + TestAllTypes b = TestAllTypes.newBuilder().build(); + TestAllTypes c = TestAllTypes.newBuilder(b).addRepeatedString("x").build(); + TestAllTypes d = TestAllTypes.newBuilder(c).addRepeatedString("y").build(); + TestAllExtensions e = TestUtil.getAllExtensionsSet(); + TestAllExtensions f = TestAllExtensions.newBuilder(e) + .addExtension(UnittestProto.repeatedInt32Extension, 999).build(); + + checkEqualsIsConsistent(a); + checkEqualsIsConsistent(b); + checkEqualsIsConsistent(c); + checkEqualsIsConsistent(d); + checkEqualsIsConsistent(e); + checkEqualsIsConsistent(f); + + checkNotEqual(a, b); + checkNotEqual(a, c); + checkNotEqual(a, d); + checkNotEqual(a, e); + checkNotEqual(a, f); + + checkNotEqual(b, c); + checkNotEqual(b, d); + checkNotEqual(b, e); + checkNotEqual(b, f); + + checkNotEqual(c, d); + checkNotEqual(c, e); + checkNotEqual(c, f); + + checkNotEqual(d, e); + checkNotEqual(d, f); + + checkNotEqual(e, f); + } + + /** + * Asserts that the given protos are equal and have the same hash code. + */ + private void checkEqualsIsConsistent(Message message) { + // Object should be equal to itself. + assertEquals(message, message); + + // Object should be equal to a dynamic copy of itself. + DynamicMessage dynamic = DynamicMessage.newBuilder(message).build(); + assertEquals(message, dynamic); + assertEquals(dynamic, message); + assertEquals(dynamic.hashCode(), message.hashCode()); + } + + /** + * Asserts that the given protos are not equal and have different hash codes. + * + * @warning It's valid for non-equal objects to have the same hash code, so + * this test is stricter than it needs to be. However, this should happen + * relatively rarely. + */ + private void checkNotEqual(Message m1, Message m2) { + String equalsError = String.format("%s should not be equal to %s", m1, m2); + assertFalse(equalsError, m1.equals(m2)); + assertFalse(equalsError, m2.equals(m1)); + + assertFalse( + String.format("%s should have a different hash code from %s", m1, m2), + m1.hashCode() == m2.hashCode()); + } +} diff --git a/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java new file mode 100644 index 00000000..b34e56f0 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/CodedInputStreamTest.java @@ -0,0 +1,401 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestRecursiveMessage; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; +import java.io.InputStream; +import java.io.IOException; + +/** + * Unit test for {@link CodedInputStream}. + * + * @author kenton@google.com Kenton Varda + */ +public class CodedInputStreamTest extends TestCase { + /** + * Helper to construct a byte array from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private byte[] bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return bytes; + } + + /** + * An InputStream which limits the number of bytes it reads at a time. + * We use this to make sure that CodedInputStream doesn't screw up when + * reading in small blocks. + */ + private static final class SmallBlockInputStream extends FilterInputStream { + private final int blockSize; + + public SmallBlockInputStream(byte[] data, int blockSize) { + this(new ByteArrayInputStream(data), blockSize); + } + + public SmallBlockInputStream(InputStream in, int blockSize) { + super(in); + this.blockSize = blockSize; + } + + public int read(byte[] b) throws IOException { + return super.read(b, 0, Math.min(b.length, blockSize)); + } + + public int read(byte[] b, int off, int len) throws IOException { + return super.read(b, off, Math.min(len, blockSize)); + } + } + + /** + * Parses the given bytes using readRawVarint32() and readRawVarint64() and + * checks that the result matches the given value. + */ + private void assertReadVarint(byte[] data, long value) throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals((int)value, input.readRawVarint32()); + + input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawVarint64()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals((int)value, input.readRawVarint32()); + + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawVarint64()); + } + } + + /** + * Parses the given bytes using readRawVarint32() and readRawVarint64() and + * expects them to fail with an InvalidProtocolBufferException whose + * description matches the given one. + */ + private void assertReadVarintFailure( + InvalidProtocolBufferException expected, byte[] data) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + try { + input.readRawVarint32(); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals(expected.getMessage(), e.getMessage()); + } + + input = CodedInputStream.newInstance(data); + try { + input.readRawVarint64(); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals(expected.getMessage(), e.getMessage()); + } + } + + /** Tests readRawVarint32() and readRawVarint64(). */ + public void testReadVarint() throws Exception { + assertReadVarint(bytes(0x00), 0); + assertReadVarint(bytes(0x01), 1); + assertReadVarint(bytes(0x7f), 127); + // 14882 + assertReadVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); + // 2961488830 + assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x0bL << 28)); + + // 64-bit + // 7256456126 + assertReadVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x1bL << 28)); + // 41256202580718336 + assertReadVarint( + bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49)); + // 11964378330978735131 + assertReadVarint( + bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | + (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); + + // Failures + assertReadVarintFailure( + InvalidProtocolBufferException.malformedVarint(), + bytes(0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x00)); + assertReadVarintFailure( + InvalidProtocolBufferException.truncatedMessage(), + bytes(0x80)); + } + + /** + * Parses the given bytes using readRawLittleEndian32() and checks + * that the result matches the given value. + */ + private void assertReadLittleEndian32(byte[] data, int value) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawLittleEndian32()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawLittleEndian32()); + } + } + + /** + * Parses the given bytes using readRawLittleEndian64() and checks + * that the result matches the given value. + */ + private void assertReadLittleEndian64(byte[] data, long value) + throws Exception { + CodedInputStream input = CodedInputStream.newInstance(data); + assertEquals(value, input.readRawLittleEndian64()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + input = CodedInputStream.newInstance( + new SmallBlockInputStream(data, blockSize)); + assertEquals(value, input.readRawLittleEndian64()); + } + } + + /** Tests readRawLittleEndian32() and readRawLittleEndian64(). */ + public void testReadLittleEndian() throws Exception { + assertReadLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); + assertReadLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); + + assertReadLittleEndian64( + bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), + 0x123456789abcdef0L); + assertReadLittleEndian64( + bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), + 0x9abcdef012345678L); + } + + /** Test decodeZigZag32() and decodeZigZag64(). */ + public void testDecodeZigZag() throws Exception { + assertEquals( 0, CodedInputStream.decodeZigZag32(0)); + assertEquals(-1, CodedInputStream.decodeZigZag32(1)); + assertEquals( 1, CodedInputStream.decodeZigZag32(2)); + assertEquals(-2, CodedInputStream.decodeZigZag32(3)); + assertEquals(0x3FFFFFFF, CodedInputStream.decodeZigZag32(0x7FFFFFFE)); + assertEquals(0xC0000000, CodedInputStream.decodeZigZag32(0x7FFFFFFF)); + assertEquals(0x7FFFFFFF, CodedInputStream.decodeZigZag32(0xFFFFFFFE)); + assertEquals(0x80000000, CodedInputStream.decodeZigZag32(0xFFFFFFFF)); + + assertEquals( 0, CodedInputStream.decodeZigZag64(0)); + assertEquals(-1, CodedInputStream.decodeZigZag64(1)); + assertEquals( 1, CodedInputStream.decodeZigZag64(2)); + assertEquals(-2, CodedInputStream.decodeZigZag64(3)); + assertEquals(0x000000003FFFFFFFL, + CodedInputStream.decodeZigZag64(0x000000007FFFFFFEL)); + assertEquals(0xFFFFFFFFC0000000L, + CodedInputStream.decodeZigZag64(0x000000007FFFFFFFL)); + assertEquals(0x000000007FFFFFFFL, + CodedInputStream.decodeZigZag64(0x00000000FFFFFFFEL)); + assertEquals(0xFFFFFFFF80000000L, + CodedInputStream.decodeZigZag64(0x00000000FFFFFFFFL)); + assertEquals(0x7FFFFFFFFFFFFFFFL, + CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFEL)); + assertEquals(0x8000000000000000L, + CodedInputStream.decodeZigZag64(0xFFFFFFFFFFFFFFFFL)); + } + + /** Tests reading and parsing a whole message with every field type. */ + public void testReadWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + byte[] rawBytes = message.toByteArray(); + assertEquals(rawBytes.length, message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + TestUtil.assertAllFieldsSet(message2); + + // Try different block sizes. + for (int blockSize = 1; blockSize < 256; blockSize *= 2) { + message2 = TestAllTypes.parseFrom( + new SmallBlockInputStream(rawBytes, blockSize)); + TestUtil.assertAllFieldsSet(message2); + } + } + + /** Tests skipField(). */ + public void testSkipWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + byte[] rawBytes = message.toByteArray(); + + // Create two parallel inputs. Parse one as unknown fields while using + // skipField() to skip each field on the other. Expect the same tags. + CodedInputStream input1 = CodedInputStream.newInstance(rawBytes); + CodedInputStream input2 = CodedInputStream.newInstance(rawBytes); + UnknownFieldSet.Builder unknownFields = UnknownFieldSet.newBuilder(); + + while (true) { + int tag = input1.readTag(); + assertEquals(tag, input2.readTag()); + if (tag == 0) { + break; + } + unknownFields.mergeFieldFrom(tag, input1); + input2.skipField(tag); + } + } + + public void testReadHugeBlob() throws Exception { + // Allocate and initialize a 1MB blob. + byte[] blob = new byte[1 << 20]; + for (int i = 0; i < blob.length; i++) { + blob[i] = (byte)i; + } + + // Make a message containing it. + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + builder.setOptionalBytes(ByteString.copyFrom(blob)); + TestAllTypes message = builder.build(); + + // Serialize and parse it. Make sure to parse from an InputStream, not + // directly from a ByteString, so that CodedInputStream uses buffered + // reading. + TestAllTypes message2 = + TestAllTypes.parseFrom(message.toByteString().newInput()); + + assertEquals(message.getOptionalBytes(), message2.getOptionalBytes()); + + // Make sure all the other fields were parsed correctly. + TestAllTypes message3 = TestAllTypes.newBuilder(message2) + .setOptionalBytes(TestUtil.getAllSet().getOptionalBytes()) + .build(); + TestUtil.assertAllFieldsSet(message3); + } + + public void testReadMaliciouslyLargeBlob() throws Exception { + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(0x7FFFFFFF); + output.writeRawBytes(new byte[32]); // Pad with a few random bytes. + output.flush(); + + CodedInputStream input = rawOutput.toByteString().newCodedInput(); + assertEquals(tag, input.readTag()); + + try { + input.readBytes(); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + private TestRecursiveMessage makeRecursiveMessage(int depth) { + if (depth == 0) { + return TestRecursiveMessage.newBuilder().setI(5).build(); + } else { + return TestRecursiveMessage.newBuilder() + .setA(makeRecursiveMessage(depth - 1)).build(); + } + } + + private void assertMessageDepth(TestRecursiveMessage message, int depth) { + if (depth == 0) { + assertFalse(message.hasA()); + assertEquals(5, message.getI()); + } else { + assertTrue(message.hasA()); + assertMessageDepth(message.getA(), depth - 1); + } + } + + public void testMaliciousRecursion() throws Exception { + ByteString data64 = makeRecursiveMessage(64).toByteString(); + ByteString data65 = makeRecursiveMessage(65).toByteString(); + + assertMessageDepth(TestRecursiveMessage.parseFrom(data64), 64); + + try { + TestRecursiveMessage.parseFrom(data65); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + + CodedInputStream input = data64.newCodedInput(); + input.setRecursionLimit(8); + try { + TestRecursiveMessage.parseFrom(input); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + public void testSizeLimit() throws Exception { + CodedInputStream input = CodedInputStream.newInstance( + TestUtil.getAllSet().toByteString().newInput()); + input.setSizeLimit(16); + + try { + TestAllTypes.parseFrom(input); + fail("Should have thrown an exception!"); + } catch (InvalidProtocolBufferException e) { + // success. + } + } + + /** + * Tests that if we read an string that contains invalid UTF-8, no exception + * is thrown. Instead, the invalid bytes are replaced with the Unicode + * "replacement character" U+FFFD. + */ + public void testReadInvalidUtf8() throws Exception { + ByteString.Output rawOutput = ByteString.newOutput(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + + int tag = WireFormat.makeTag(1, WireFormat.WIRETYPE_LENGTH_DELIMITED); + output.writeRawVarint32(tag); + output.writeRawVarint32(1); + output.writeRawBytes(new byte[] { (byte)0x80 }); + output.flush(); + + CodedInputStream input = rawOutput.toByteString().newCodedInput(); + assertEquals(tag, input.readTag()); + String text = input.readString(); + assertEquals(0xfffd, text.charAt(0)); + } +} diff --git a/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java new file mode 100644 index 00000000..7ee75534 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/CodedOutputStreamTest.java @@ -0,0 +1,280 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; + +import junit.framework.TestCase; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Unit test for {@link CodedOutputStream}. + * + * @author kenton@google.com Kenton Varda + */ +public class CodedOutputStreamTest extends TestCase { + /** + * Helper to construct a byte array from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private byte[] bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return bytes; + } + + /** Arrays.asList() does not work with arrays of primitives. :( */ + private List toList(byte[] bytes) { + List result = new ArrayList(); + for (byte b : bytes) { + result.add(b); + } + return result; + } + + private void assertEqualBytes(byte[] a, byte[] b) { + assertEquals(toList(a), toList(b)); + } + + /** + * Writes the given value using writeRawVarint32() and writeRawVarint64() and + * checks that the result matches the given bytes. + */ + private void assertWriteVarint(byte[] data, long value) throws Exception { + // Only do 32-bit write if the value fits in 32 bits. + if ((value >>> 32) == 0) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawVarint32((int) value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Also try computing size. + assertEquals(data.length, + CodedOutputStream.computeRawVarint32Size((int) value)); + } + + { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawVarint64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Also try computing size. + assertEquals(data.length, + CodedOutputStream.computeRawVarint64Size(value)); + } + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + // Only do 32-bit write if the value fits in 32 bits. + if ((value >>> 32) == 0) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawVarint32((int) value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + + { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawVarint64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + } + + /** Tests writeRawVarint32() and writeRawVarint64(). */ + public void testWriteVarint() throws Exception { + assertWriteVarint(bytes(0x00), 0); + assertWriteVarint(bytes(0x01), 1); + assertWriteVarint(bytes(0x7f), 127); + // 14882 + assertWriteVarint(bytes(0xa2, 0x74), (0x22 << 0) | (0x74 << 7)); + // 2961488830 + assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x0b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x0bL << 28)); + + // 64-bit + // 7256456126 + assertWriteVarint(bytes(0xbe, 0xf7, 0x92, 0x84, 0x1b), + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (0x1bL << 28)); + // 41256202580718336 + assertWriteVarint( + bytes(0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49), + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (0x43L << 28) | (0x49L << 35) | (0x24L << 42) | (0x49L << 49)); + // 11964378330978735131 + assertWriteVarint( + bytes(0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01), + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (0x3bL << 28) | (0x56L << 35) | (0x00L << 42) | + (0x05L << 49) | (0x26L << 56) | (0x01L << 63)); + } + + /** + * Parses the given bytes using writeRawLittleEndian32() and checks + * that the result matches the given value. + */ + private void assertWriteLittleEndian32(byte[] data, int value) + throws Exception { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawLittleEndian32(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + rawOutput = new ByteArrayOutputStream(); + output = CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawLittleEndian32(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + + /** + * Parses the given bytes using writeRawLittleEndian64() and checks + * that the result matches the given value. + */ + private void assertWriteLittleEndian64(byte[] data, long value) + throws Exception { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = CodedOutputStream.newInstance(rawOutput); + output.writeRawLittleEndian64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + + // Try different block sizes. + for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { + rawOutput = new ByteArrayOutputStream(); + output = CodedOutputStream.newInstance(rawOutput, blockSize); + output.writeRawLittleEndian64(value); + output.flush(); + assertEqualBytes(data, rawOutput.toByteArray()); + } + } + + /** Tests writeRawLittleEndian32() and writeRawLittleEndian64(). */ + public void testWriteLittleEndian() throws Exception { + assertWriteLittleEndian32(bytes(0x78, 0x56, 0x34, 0x12), 0x12345678); + assertWriteLittleEndian32(bytes(0xf0, 0xde, 0xbc, 0x9a), 0x9abcdef0); + + assertWriteLittleEndian64( + bytes(0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12), + 0x123456789abcdef0L); + assertWriteLittleEndian64( + bytes(0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a), + 0x9abcdef012345678L); + } + + /** Test encodeZigZag32() and encodeZigZag64(). */ + public void testEncodeZigZag() throws Exception { + assertEquals(0, CodedOutputStream.encodeZigZag32( 0)); + assertEquals(1, CodedOutputStream.encodeZigZag32(-1)); + assertEquals(2, CodedOutputStream.encodeZigZag32( 1)); + assertEquals(3, CodedOutputStream.encodeZigZag32(-2)); + assertEquals(0x7FFFFFFE, CodedOutputStream.encodeZigZag32(0x3FFFFFFF)); + assertEquals(0x7FFFFFFF, CodedOutputStream.encodeZigZag32(0xC0000000)); + assertEquals(0xFFFFFFFE, CodedOutputStream.encodeZigZag32(0x7FFFFFFF)); + assertEquals(0xFFFFFFFF, CodedOutputStream.encodeZigZag32(0x80000000)); + + assertEquals(0, CodedOutputStream.encodeZigZag64( 0)); + assertEquals(1, CodedOutputStream.encodeZigZag64(-1)); + assertEquals(2, CodedOutputStream.encodeZigZag64( 1)); + assertEquals(3, CodedOutputStream.encodeZigZag64(-2)); + assertEquals(0x000000007FFFFFFEL, + CodedOutputStream.encodeZigZag64(0x000000003FFFFFFFL)); + assertEquals(0x000000007FFFFFFFL, + CodedOutputStream.encodeZigZag64(0xFFFFFFFFC0000000L)); + assertEquals(0x00000000FFFFFFFEL, + CodedOutputStream.encodeZigZag64(0x000000007FFFFFFFL)); + assertEquals(0x00000000FFFFFFFFL, + CodedOutputStream.encodeZigZag64(0xFFFFFFFF80000000L)); + assertEquals(0xFFFFFFFFFFFFFFFEL, + CodedOutputStream.encodeZigZag64(0x7FFFFFFFFFFFFFFFL)); + assertEquals(0xFFFFFFFFFFFFFFFFL, + CodedOutputStream.encodeZigZag64(0x8000000000000000L)); + + // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) + // were chosen semi-randomly via keyboard bashing. + assertEquals(0, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(0))); + assertEquals(1, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(1))); + assertEquals(-1, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-1))); + assertEquals(14927, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(14927))); + assertEquals(-3612, + CodedOutputStream.encodeZigZag32(CodedInputStream.decodeZigZag32(-3612))); + + assertEquals(0, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(0))); + assertEquals(1, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(1))); + assertEquals(-1, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-1))); + assertEquals(14927, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(14927))); + assertEquals(-3612, + CodedOutputStream.encodeZigZag64(CodedInputStream.decodeZigZag64(-3612))); + + assertEquals(856912304801416L, + CodedOutputStream.encodeZigZag64( + CodedInputStream.decodeZigZag64( + 856912304801416L))); + assertEquals(-75123905439571256L, + CodedOutputStream.encodeZigZag64( + CodedInputStream.decodeZigZag64( + -75123905439571256L))); + } + + /** Tests writing a whole message with every field type. */ + public void testWriteWholeMessage() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + byte[] rawBytes = message.toByteArray(); + assertEqualBytes(TestUtil.getGoldenMessage().toByteArray(), rawBytes); + + // Try different block sizes. + for (int blockSize = 1; blockSize < 256; blockSize *= 2) { + ByteArrayOutputStream rawOutput = new ByteArrayOutputStream(); + CodedOutputStream output = + CodedOutputStream.newInstance(rawOutput, blockSize); + message.writeTo(output); + output.flush(); + assertEqualBytes(rawBytes, rawOutput.toByteArray()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/DescriptorsTest.java b/java/src/test/java/com/google/protobuf/DescriptorsTest.java new file mode 100644 index 00000000..98d6d01a --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DescriptorsTest.java @@ -0,0 +1,313 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.Descriptor; +import com.google.protobuf.Descriptors.FieldDescriptor; +import com.google.protobuf.Descriptors.EnumDescriptor; +import com.google.protobuf.Descriptors.EnumValueDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.protobuf.Descriptors.MethodDescriptor; + +import com.google.protobuf.test.UnittestImport; +import com.google.protobuf.test.UnittestImport.ImportEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestService; + + +import junit.framework.TestCase; + +import java.util.Arrays; +import java.util.Collections; + +/** + * Unit test for {@link Descriptors}. + * + * @author kenton@google.com Kenton Varda + */ +public class DescriptorsTest extends TestCase { + public void testFileDescriptor() throws Exception { + FileDescriptor file = UnittestProto.getDescriptor(); + + assertEquals("google/protobuf/unittest.proto", file.getName()); + assertEquals("protobuf_unittest", file.getPackage()); + + assertEquals("UnittestProto", file.getOptions().getJavaOuterClassname()); + assertEquals("google/protobuf/unittest.proto", + file.toProto().getName()); + + assertEquals(Arrays.asList(UnittestImport.getDescriptor()), + file.getDependencies()); + + Descriptor messageType = TestAllTypes.getDescriptor(); + assertEquals(messageType, file.getMessageTypes().get(0)); + assertEquals(messageType, file.findMessageTypeByName("TestAllTypes")); + assertNull(file.findMessageTypeByName("NoSuchType")); + assertNull(file.findMessageTypeByName("protobuf_unittest.TestAllTypes")); + for (int i = 0; i < file.getMessageTypes().size(); i++) { + assertEquals(i, file.getMessageTypes().get(i).getIndex()); + } + + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + assertEquals(enumType, file.getEnumTypes().get(0)); + assertEquals(enumType, file.findEnumTypeByName("ForeignEnum")); + assertNull(file.findEnumTypeByName("NoSuchType")); + assertNull(file.findEnumTypeByName("protobuf_unittest.ForeignEnum")); + assertEquals(Arrays.asList(ImportEnum.getDescriptor()), + UnittestImport.getDescriptor().getEnumTypes()); + for (int i = 0; i < file.getEnumTypes().size(); i++) { + assertEquals(i, file.getEnumTypes().get(i).getIndex()); + } + + ServiceDescriptor service = TestService.getDescriptor(); + assertEquals(service, file.getServices().get(0)); + assertEquals(service, file.findServiceByName("TestService")); + assertNull(file.findServiceByName("NoSuchType")); + assertNull(file.findServiceByName("protobuf_unittest.TestService")); + assertEquals(Collections.emptyList(), + UnittestImport.getDescriptor().getServices()); + for (int i = 0; i < file.getServices().size(); i++) { + assertEquals(i, file.getServices().get(i).getIndex()); + } + + FieldDescriptor extension = + UnittestProto.optionalInt32Extension.getDescriptor(); + assertEquals(extension, file.getExtensions().get(0)); + assertEquals(extension, + file.findExtensionByName("optional_int32_extension")); + assertNull(file.findExtensionByName("no_such_ext")); + assertNull(file.findExtensionByName( + "protobuf_unittest.optional_int32_extension")); + assertEquals(Collections.emptyList(), + UnittestImport.getDescriptor().getExtensions()); + for (int i = 0; i < file.getExtensions().size(); i++) { + assertEquals(i, file.getExtensions().get(i).getIndex()); + } + } + + public void testDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + Descriptor nestedType = TestAllTypes.NestedMessage.getDescriptor(); + + assertEquals("TestAllTypes", messageType.getName()); + assertEquals("protobuf_unittest.TestAllTypes", messageType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), messageType.getFile()); + assertNull(messageType.getContainingType()); + assertEquals(DescriptorProtos.MessageOptions.getDefaultInstance(), + messageType.getOptions()); + assertEquals("TestAllTypes", messageType.toProto().getName()); + + assertEquals("NestedMessage", nestedType.getName()); + assertEquals("protobuf_unittest.TestAllTypes.NestedMessage", + nestedType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); + assertEquals(messageType, nestedType.getContainingType()); + + FieldDescriptor field = messageType.getFields().get(0); + assertEquals("optional_int32", field.getName()); + assertEquals(field, messageType.findFieldByName("optional_int32")); + assertNull(messageType.findFieldByName("no_such_field")); + assertEquals(field, messageType.findFieldByNumber(1)); + assertNull(messageType.findFieldByNumber(571283)); + for (int i = 0; i < messageType.getFields().size(); i++) { + assertEquals(i, messageType.getFields().get(i).getIndex()); + } + + assertEquals(nestedType, messageType.getNestedTypes().get(0)); + assertEquals(nestedType, messageType.findNestedTypeByName("NestedMessage")); + assertNull(messageType.findNestedTypeByName("NoSuchType")); + for (int i = 0; i < messageType.getNestedTypes().size(); i++) { + assertEquals(i, messageType.getNestedTypes().get(i).getIndex()); + } + + EnumDescriptor enumType = TestAllTypes.NestedEnum.getDescriptor(); + assertEquals(enumType, messageType.getEnumTypes().get(0)); + assertEquals(enumType, messageType.findEnumTypeByName("NestedEnum")); + assertNull(messageType.findEnumTypeByName("NoSuchType")); + for (int i = 0; i < messageType.getEnumTypes().size(); i++) { + assertEquals(i, messageType.getEnumTypes().get(i).getIndex()); + } + } + + public void testFieldDescriptor() throws Exception { + Descriptor messageType = TestAllTypes.getDescriptor(); + FieldDescriptor primitiveField = + messageType.findFieldByName("optional_int32"); + FieldDescriptor enumField = + messageType.findFieldByName("optional_nested_enum"); + FieldDescriptor messageField = + messageType.findFieldByName("optional_foreign_message"); + FieldDescriptor cordField = + messageType.findFieldByName("optional_cord"); + FieldDescriptor extension = + UnittestProto.optionalInt32Extension.getDescriptor(); + FieldDescriptor nestedExtension = TestRequired.single.getDescriptor(); + + assertEquals("optional_int32", primitiveField.getName()); + assertEquals("protobuf_unittest.TestAllTypes.optional_int32", + primitiveField.getFullName()); + assertEquals(1, primitiveField.getNumber()); + assertEquals(messageType, primitiveField.getContainingType()); + assertEquals(UnittestProto.getDescriptor(), primitiveField.getFile()); + assertEquals(FieldDescriptor.Type.INT32, primitiveField.getType()); + assertEquals(FieldDescriptor.JavaType.INT, primitiveField.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), + primitiveField.getOptions()); + assertFalse(primitiveField.isExtension()); + assertEquals("optional_int32", primitiveField.toProto().getName()); + + assertEquals("optional_nested_enum", enumField.getName()); + assertEquals(FieldDescriptor.Type.ENUM, enumField.getType()); + assertEquals(FieldDescriptor.JavaType.ENUM, enumField.getJavaType()); + assertEquals(TestAllTypes.NestedEnum.getDescriptor(), + enumField.getEnumType()); + + assertEquals("optional_foreign_message", messageField.getName()); + assertEquals(FieldDescriptor.Type.MESSAGE, messageField.getType()); + assertEquals(FieldDescriptor.JavaType.MESSAGE, messageField.getJavaType()); + assertEquals(ForeignMessage.getDescriptor(), messageField.getMessageType()); + + assertEquals("optional_cord", cordField.getName()); + assertEquals(FieldDescriptor.Type.STRING, cordField.getType()); + assertEquals(FieldDescriptor.JavaType.STRING, cordField.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.CType.CORD, + cordField.getOptions().getCtype()); + + assertEquals("optional_int32_extension", extension.getName()); + assertEquals("protobuf_unittest.optional_int32_extension", + extension.getFullName()); + assertEquals(1, extension.getNumber()); + assertEquals(TestAllExtensions.getDescriptor(), + extension.getContainingType()); + assertEquals(UnittestProto.getDescriptor(), extension.getFile()); + assertEquals(FieldDescriptor.Type.INT32, extension.getType()); + assertEquals(FieldDescriptor.JavaType.INT, extension.getJavaType()); + assertEquals(DescriptorProtos.FieldOptions.getDefaultInstance(), + extension.getOptions()); + assertTrue(extension.isExtension()); + assertEquals(null, extension.getExtensionScope()); + assertEquals("optional_int32_extension", extension.toProto().getName()); + + assertEquals("single", nestedExtension.getName()); + assertEquals("protobuf_unittest.TestRequired.single", + nestedExtension.getFullName()); + assertEquals(TestRequired.getDescriptor(), + nestedExtension.getExtensionScope()); + } + + public void testFieldDescriptorLabel() throws Exception { + FieldDescriptor requiredField = + TestRequired.getDescriptor().findFieldByName("a"); + FieldDescriptor optionalField = + TestAllTypes.getDescriptor().findFieldByName("optional_int32"); + FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_int32"); + + assertTrue(requiredField.isRequired()); + assertFalse(requiredField.isRepeated()); + assertFalse(optionalField.isRequired()); + assertFalse(optionalField.isRepeated()); + assertFalse(repeatedField.isRequired()); + assertTrue(repeatedField.isRepeated()); + } + + public void testFieldDescriptorDefault() throws Exception { + Descriptor d = TestAllTypes.getDescriptor(); + assertFalse(d.findFieldByName("optional_int32").hasDefaultValue()); + assertEquals(0, d.findFieldByName("optional_int32").getDefaultValue()); + assertTrue(d.findFieldByName("default_int32").hasDefaultValue()); + assertEquals(41, d.findFieldByName("default_int32").getDefaultValue()); + + d = TestExtremeDefaultValues.getDescriptor(); + assertEquals( + ByteString.copyFrom( + "\0\001\007\b\f\n\r\t\013\\\'\"\u00fe".getBytes("ISO-8859-1")), + d.findFieldByName("escaped_bytes").getDefaultValue()); + assertEquals(-1, d.findFieldByName("large_uint32").getDefaultValue()); + assertEquals(-1L, d.findFieldByName("large_uint64").getDefaultValue()); + } + + public void testEnumDescriptor() throws Exception { + EnumDescriptor enumType = ForeignEnum.getDescriptor(); + EnumDescriptor nestedType = TestAllTypes.NestedEnum.getDescriptor(); + + assertEquals("ForeignEnum", enumType.getName()); + assertEquals("protobuf_unittest.ForeignEnum", enumType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), enumType.getFile()); + assertNull(enumType.getContainingType()); + assertEquals(DescriptorProtos.EnumOptions.getDefaultInstance(), + enumType.getOptions()); + + assertEquals("NestedEnum", nestedType.getName()); + assertEquals("protobuf_unittest.TestAllTypes.NestedEnum", + nestedType.getFullName()); + assertEquals(UnittestProto.getDescriptor(), nestedType.getFile()); + assertEquals(TestAllTypes.getDescriptor(), nestedType.getContainingType()); + + EnumValueDescriptor value = ForeignEnum.FOREIGN_FOO.getValueDescriptor(); + assertEquals(value, enumType.getValues().get(0)); + assertEquals("FOREIGN_FOO", value.getName()); + assertEquals(4, value.getNumber()); + assertEquals(value, enumType.findValueByName("FOREIGN_FOO")); + assertEquals(value, enumType.findValueByNumber(4)); + assertNull(enumType.findValueByName("NO_SUCH_VALUE")); + for (int i = 0; i < enumType.getValues().size(); i++) { + assertEquals(i, enumType.getValues().get(i).getIndex()); + } + } + + public void testServiceDescriptor() throws Exception { + ServiceDescriptor service = TestService.getDescriptor(); + + assertEquals("TestService", service.getName()); + assertEquals("protobuf_unittest.TestService", service.getFullName()); + assertEquals(UnittestProto.getDescriptor(), service.getFile()); + + assertEquals(2, service.getMethods().size()); + + MethodDescriptor fooMethod = service.getMethods().get(0); + assertEquals("Foo", fooMethod.getName()); + assertEquals(UnittestProto.FooRequest.getDescriptor(), + fooMethod.getInputType()); + assertEquals(UnittestProto.FooResponse.getDescriptor(), + fooMethod.getOutputType()); + assertEquals(fooMethod, service.findMethodByName("Foo")); + + MethodDescriptor barMethod = service.getMethods().get(1); + assertEquals("Bar", barMethod.getName()); + assertEquals(UnittestProto.BarRequest.getDescriptor(), + barMethod.getInputType()); + assertEquals(UnittestProto.BarResponse.getDescriptor(), + barMethod.getOutputType()); + assertEquals(barMethod, service.findMethodByName("Bar")); + + assertNull(service.findMethodByName("NoSuchMethod")); + + for (int i = 0; i < service.getMethods().size(); i++) { + assertEquals(i, service.getMethods().get(i).getIndex()); + } + } + +} diff --git a/java/src/test/java/com/google/protobuf/DynamicMessageTest.java b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java new file mode 100644 index 00000000..7a458981 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/DynamicMessageTest.java @@ -0,0 +1,120 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; + +import junit.framework.TestCase; + +/** + * Unit test for {@link DynamicMessage}. See also {@link MessageTest}, which + * tests some {@link DynamicMessage} functionality. + * + * @author kenton@google.com Kenton Varda + */ +public class DynamicMessageTest extends TestCase { + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testDynamicMessageAccessors() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + reflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testDynamicMessageExtensionAccessors() throws Exception { + // We don't need to extensively test DynamicMessage's handling of + // extensions because, frankly, it doesn't do anything special with them. + // It treats them just like any other fields. + Message.Builder builder = + DynamicMessage.newBuilder(TestAllExtensions.getDescriptor()); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + extensionsReflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testDynamicMessageRepeatedSetters() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + reflectionTester.modifyRepeatedFieldsViaReflection(builder); + Message message = builder.build(); + reflectionTester.assertRepeatedFieldsModifiedViaReflection(message); + } + + public void testDynamicMessageDefaults() throws Exception { + reflectionTester.assertClearViaReflection( + DynamicMessage.getDefaultInstance(TestAllTypes.getDescriptor())); + reflectionTester.assertClearViaReflection( + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()).build()); + } + + public void testDynamicMessageSerializedSize() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + Message.Builder dynamicBuilder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(dynamicBuilder); + Message dynamicMessage = dynamicBuilder.build(); + + assertEquals(message.getSerializedSize(), + dynamicMessage.getSerializedSize()); + } + + public void testDynamicMessageSerialization() throws Exception { + Message.Builder builder = + DynamicMessage.newBuilder(TestAllTypes.getDescriptor()); + reflectionTester.setAllFieldsViaReflection(builder); + Message message = builder.build(); + + ByteString rawBytes = message.toByteString(); + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + + // In fact, the serialized forms should be exactly the same, byte-for-byte. + assertEquals(TestUtil.getAllSet().toByteString(), rawBytes); + } + + public void testDynamicMessageParsing() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + ByteString rawBytes = message.toByteString(); + + Message message2 = + DynamicMessage.parseFrom(TestAllTypes.getDescriptor(), rawBytes); + reflectionTester.assertAllFieldsSetViaReflection(message2); + } + + public void testDynamicMessageCopy() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + + DynamicMessage copy = DynamicMessage.newBuilder(message).build(); + reflectionTester.assertAllFieldsSetViaReflection(copy); + } +} diff --git a/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java new file mode 100644 index 00000000..30d73d28 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -0,0 +1,246 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; +import protobuf_unittest.MultipleFilesTestProto; +import protobuf_unittest.MessageWithNoOuter; +import protobuf_unittest.EnumWithNoOuter; +import protobuf_unittest.ServiceWithNoOuter; + +import junit.framework.TestCase; +import java.util.Arrays; + +/** + * Unit test for generated messages and generated code. See also + * {@link MessageTest}, which tests some generated message functionality. + * + * @author kenton@google.com Kenton Varda + */ +public class GeneratedMessageTest extends TestCase { + TestUtil.ReflectionTester reflectionTester = + new TestUtil.ReflectionTester(TestAllTypes.getDescriptor(), null); + + public void testDefaultInstance() throws Exception { + assertSame(TestAllTypes.getDefaultInstance(), + TestAllTypes.getDefaultInstance().getDefaultInstanceForType()); + assertSame(TestAllTypes.getDefaultInstance(), + TestAllTypes.newBuilder().getDefaultInstanceForType()); + } + + public void testAccessors() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + TestUtil.assertAllFieldsSet(message); + } + + public void testRepeatedSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestUtil.modifyRepeatedFields(builder); + TestAllTypes message = builder.build(); + TestUtil.assertRepeatedFieldsModified(message); + } + + public void testRepeatedAppend() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + + builder.addAllRepeatedInt32(Arrays.asList(1, 2, 3, 4)); + builder.addAllRepeatedForeignEnum(Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + + ForeignMessage foreignMessage = + ForeignMessage.newBuilder().setC(12).build(); + builder.addAllRepeatedForeignMessage(Arrays.asList(foreignMessage)); + + TestAllTypes message = builder.build(); + assertEquals(message.getRepeatedInt32List(), Arrays.asList(1, 2, 3, 4)); + assertEquals(message.getRepeatedForeignEnumList(), + Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + assertEquals(1, message.getRepeatedForeignMessageCount()); + assertEquals(12, message.getRepeatedForeignMessage(0).getC()); + } + + public void testSettingForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder() + // Pass builder for foreign message instance. + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(123)) + .build(); + TestAllTypes expectedMessage = TestAllTypes.newBuilder() + // Create expected version passing foreign message instance explicitly. + .setOptionalForeignMessage( + ForeignMessage.newBuilder().setC(123).build()) + .build(); + // TODO(ngd): Upgrade to using real #equals method once implemented + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testSettingRepeatedForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = TestAllTypes.newBuilder() + // Pass builder for foreign message instance. + .addRepeatedForeignMessage(ForeignMessage.newBuilder().setC(456)) + .build(); + TestAllTypes expectedMessage = TestAllTypes.newBuilder() + // Create expected version passing foreign message instance explicitly. + .addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(456).build()) + .build(); + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testDefaults() throws Exception { + TestUtil.assertClear(TestAllTypes.getDefaultInstance()); + TestUtil.assertClear(TestAllTypes.newBuilder().build()); + + assertEquals("\u1234", + TestExtremeDefaultValues.getDefaultInstance().getUtf8String()); + } + + public void testReflectionGetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder.build(); + reflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testReflectionSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + reflectionTester.setAllFieldsViaReflection(builder); + TestAllTypes message = builder.build(); + TestUtil.assertAllFieldsSet(message); + } + + public void testReflectionRepeatedSetters() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + reflectionTester.setAllFieldsViaReflection(builder); + reflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestAllTypes message = builder.build(); + TestUtil.assertRepeatedFieldsModified(message); + } + + public void testReflectionDefaults() throws Exception { + reflectionTester.assertClearViaReflection( + TestAllTypes.getDefaultInstance()); + reflectionTester.assertClearViaReflection( + TestAllTypes.newBuilder().build()); + } + + // ================================================================= + // Extensions. + + TestUtil.ReflectionTester extensionsReflectionTester = + new TestUtil.ReflectionTester(TestAllExtensions.getDescriptor(), + TestUtil.getExtensionRegistry()); + + public void testExtensionAccessors() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertAllExtensionsSet(message); + } + + public void testExtensionRepeatedSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestUtil.modifyRepeatedExtensions(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertRepeatedExtensionsModified(message); + } + + public void testExtensionDefaults() throws Exception { + TestUtil.assertExtensionsClear(TestAllExtensions.getDefaultInstance()); + TestUtil.assertExtensionsClear(TestAllExtensions.newBuilder().build()); + } + + public void testExtensionReflectionGetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TestUtil.setAllExtensions(builder); + TestAllExtensions message = builder.build(); + extensionsReflectionTester.assertAllFieldsSetViaReflection(message); + } + + public void testExtensionReflectionSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertAllExtensionsSet(message); + } + + public void testExtensionReflectionRepeatedSetters() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + extensionsReflectionTester.setAllFieldsViaReflection(builder); + extensionsReflectionTester.modifyRepeatedFieldsViaReflection(builder); + TestAllExtensions message = builder.build(); + TestUtil.assertRepeatedExtensionsModified(message); + } + + public void testExtensionReflectionDefaults() throws Exception { + extensionsReflectionTester.assertClearViaReflection( + TestAllExtensions.getDefaultInstance()); + extensionsReflectionTester.assertClearViaReflection( + TestAllExtensions.newBuilder().build()); + } + + public void testClearExtension() throws Exception { + // clearExtension() is not actually used in TestUtil, so try it manually. + assertFalse( + TestAllExtensions.newBuilder() + .setExtension(UnittestProto.optionalInt32Extension, 1) + .clearExtension(UnittestProto.optionalInt32Extension) + .hasExtension(UnittestProto.optionalInt32Extension)); + assertEquals(0, + TestAllExtensions.newBuilder() + .addExtension(UnittestProto.repeatedInt32Extension, 1) + .clearExtension(UnittestProto.repeatedInt32Extension) + .getExtensionCount(UnittestProto.repeatedInt32Extension)); + } + + // ================================================================= + // multiple_files_test + + public void testMultipleFilesOption() throws Exception { + // We mostly just want to check that things compile. + MessageWithNoOuter message = + MessageWithNoOuter.newBuilder() + .setNested(MessageWithNoOuter.NestedMessage.newBuilder().setI(1)) + .addForeign(TestAllTypes.newBuilder().setOptionalInt32(1)) + .setNestedEnum(MessageWithNoOuter.NestedEnum.BAZ) + .setForeignEnum(EnumWithNoOuter.BAR) + .build(); + assertEquals(message, MessageWithNoOuter.parseFrom(message.toByteString())); + + assertEquals(MultipleFilesTestProto.getDescriptor(), + MessageWithNoOuter.getDescriptor().getFile()); + + Descriptors.FieldDescriptor field = + MessageWithNoOuter.getDescriptor().findFieldByName("foreign_enum"); + assertEquals(EnumWithNoOuter.BAR.getValueDescriptor(), + message.getField(field)); + + assertEquals(MultipleFilesTestProto.getDescriptor(), + ServiceWithNoOuter.getDescriptor().getFile()); + + assertFalse( + TestAllExtensions.getDefaultInstance().hasExtension( + MultipleFilesTestProto.extensionWithOuter)); + } +} diff --git a/java/src/test/java/com/google/protobuf/MessageTest.java b/java/src/test/java/com/google/protobuf/MessageTest.java new file mode 100644 index 00000000..3dece1ff --- /dev/null +++ b/java/src/test/java/com/google/protobuf/MessageTest.java @@ -0,0 +1,299 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestProto.ForeignMessage; + +import junit.framework.TestCase; + +/** + * Misc. unit tests for message operations that apply to both generated + * and dynamic messages. + * + * @author kenton@google.com Kenton Varda + */ +public class MessageTest extends TestCase { + // ================================================================= + // Message-merging tests. + + static final TestAllTypes MERGE_SOURCE = + TestAllTypes.newBuilder() + .setOptionalInt32(1) + .setOptionalString("foo") + .setOptionalForeignMessage(ForeignMessage.getDefaultInstance()) + .addRepeatedString("bar") + .build(); + + static final TestAllTypes MERGE_DEST = + TestAllTypes.newBuilder() + .setOptionalInt64(2) + .setOptionalString("baz") + .setOptionalForeignMessage(ForeignMessage.newBuilder().setC(3).build()) + .addRepeatedString("qux") + .build(); + + static final String MERGE_RESULT_TEXT = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_string: \"foo\"\n" + + "optional_foreign_message {\n" + + " c: 3\n" + + "}\n" + + "repeated_string: \"qux\"\n" + + "repeated_string: \"bar\"\n"; + + public void testMergeFrom() throws Exception { + TestAllTypes result = + TestAllTypes.newBuilder(MERGE_DEST) + .mergeFrom(MERGE_SOURCE).build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + /** + * Test merging a DynamicMessage into a GeneratedMessage. As long as they + * have the same descriptor, this should work, but it is an entirely different + * code path. + */ + public void testMergeFromDynamic() throws Exception { + TestAllTypes result = + TestAllTypes.newBuilder(MERGE_DEST) + .mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build()) + .build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + /** Test merging two DynamicMessages. */ + public void testDynamicMergeFrom() throws Exception { + DynamicMessage result = + DynamicMessage.newBuilder(MERGE_DEST) + .mergeFrom(DynamicMessage.newBuilder(MERGE_SOURCE).build()) + .build(); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + // ================================================================= + // Required-field-related tests. + + private static final TestRequired TEST_REQUIRED_UNINITIALIZED = + TestRequired.getDefaultInstance(); + private static final TestRequired TEST_REQUIRED_INITIALIZED = + TestRequired.newBuilder().setA(1).setB(2).setC(3).build(); + + public void testRequired() throws Exception { + TestRequired.Builder builder = TestRequired.newBuilder(); + + assertFalse(builder.isInitialized()); + builder.setA(1); + assertFalse(builder.isInitialized()); + builder.setB(1); + assertFalse(builder.isInitialized()); + builder.setC(1); + assertTrue(builder.isInitialized()); + } + + public void testRequiredForeign() throws Exception { + TestRequiredForeign.Builder builder = TestRequiredForeign.newBuilder(); + + assertTrue(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testRequiredExtension() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + + assertTrue(builder.isInitialized()); + + builder.setExtension(TestRequired.single, TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setExtension(TestRequired.single, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addExtension(TestRequired.multi, TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setExtension(TestRequired.multi, 0, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testRequiredDynamic() throws Exception { + Descriptors.Descriptor descriptor = TestRequired.getDescriptor(); + DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); + + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("a"), 1); + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("b"), 1); + assertFalse(builder.isInitialized()); + builder.setField(descriptor.findFieldByName("c"), 1); + assertTrue(builder.isInitialized()); + } + + public void testRequiredDynamicForeign() throws Exception { + Descriptors.Descriptor descriptor = TestRequiredForeign.getDescriptor(); + DynamicMessage.Builder builder = DynamicMessage.newBuilder(descriptor); + + assertTrue(builder.isInitialized()); + + builder.setField(descriptor.findFieldByName("optional_message"), + TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setField(descriptor.findFieldByName("optional_message"), + TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addRepeatedField(descriptor.findFieldByName("repeated_message"), + TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setRepeatedField(descriptor.findFieldByName("repeated_message"), 0, + TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + public void testUninitializedException() throws Exception { + try { + TestRequired.newBuilder().build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testBuildPartial() throws Exception { + // We're mostly testing that no exception is thrown. + TestRequired message = TestRequired.newBuilder().buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testNestedUninitializedException() throws Exception { + try { + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals( + "Message missing required fields: " + + "optional_message.a, " + + "optional_message.b, " + + "optional_message.c, " + + "repeated_message[0].a, " + + "repeated_message[0].b, " + + "repeated_message[0].c, " + + "repeated_message[1].a, " + + "repeated_message[1].b, " + + "repeated_message[1].c", + e.getMessage()); + } + } + + public void testBuildNestedPartial() throws Exception { + // We're mostly testing that no exception is thrown. + TestRequiredForeign message = + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testParseUnititialized() throws Exception { + try { + TestRequired.parseFrom(ByteString.EMPTY); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testParseNestedUnititialized() throws Exception { + ByteString data = + TestRequiredForeign.newBuilder() + .setOptionalMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED) + .buildPartial().toByteString(); + + try { + TestRequiredForeign.parseFrom(data); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals( + "Message missing required fields: " + + "optional_message.a, " + + "optional_message.b, " + + "optional_message.c, " + + "repeated_message[0].a, " + + "repeated_message[0].b, " + + "repeated_message[0].c, " + + "repeated_message[1].a, " + + "repeated_message[1].b, " + + "repeated_message[1].c", + e.getMessage()); + } + } + + public void testDynamicUninitializedException() throws Exception { + try { + DynamicMessage.newBuilder(TestRequired.getDescriptor()).build(); + fail("Should have thrown an exception."); + } catch (UninitializedMessageException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } + + public void testDynamicBuildPartial() throws Exception { + // We're mostly testing that no exception is thrown. + DynamicMessage message = + DynamicMessage.newBuilder(TestRequired.getDescriptor()) + .buildPartial(); + assertFalse(message.isInitialized()); + } + + public void testDynamicParseUnititialized() throws Exception { + try { + Descriptors.Descriptor descriptor = TestRequired.getDescriptor(); + DynamicMessage.parseFrom(descriptor, ByteString.EMPTY); + fail("Should have thrown an exception."); + } catch (InvalidProtocolBufferException e) { + assertEquals("Message missing required fields: a, b, c", e.getMessage()); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/ServiceTest.java b/java/src/test/java/com/google/protobuf/ServiceTest.java new file mode 100644 index 00000000..2f83837b --- /dev/null +++ b/java/src/test/java/com/google/protobuf/ServiceTest.java @@ -0,0 +1,164 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestService; +import protobuf_unittest.UnittestProto.FooRequest; +import protobuf_unittest.UnittestProto.FooResponse; +import protobuf_unittest.UnittestProto.BarRequest; +import protobuf_unittest.UnittestProto.BarResponse; + +import org.easymock.classextension.EasyMock; +import org.easymock.classextension.IMocksControl; +import org.easymock.IArgumentMatcher; + +import junit.framework.TestCase; + +/** + * Tests services and stubs. + * + * @author kenton@google.com Kenton Varda + */ +public class ServiceTest extends TestCase { + private IMocksControl control; + private RpcController mockController; + + private final Descriptors.MethodDescriptor fooDescriptor = + TestService.getDescriptor().getMethods().get(0); + private final Descriptors.MethodDescriptor barDescriptor = + TestService.getDescriptor().getMethods().get(1); + + protected void setUp() throws Exception { + super.setUp(); + control = EasyMock.createStrictControl(); + mockController = control.createMock(RpcController.class); + } + + // ================================================================= + + /** Tests Service.callMethod(). */ + public void testCallMethod() throws Exception { + FooRequest fooRequest = FooRequest.newBuilder().build(); + BarRequest barRequest = BarRequest.newBuilder().build(); + MockCallback fooCallback = new MockCallback(); + MockCallback barCallback = new MockCallback(); + TestService mockService = control.createMock(TestService.class); + + mockService.foo(EasyMock.same(mockController), EasyMock.same(fooRequest), + this.wrapsCallback(fooCallback)); + mockService.bar(EasyMock.same(mockController), EasyMock.same(barRequest), + this.wrapsCallback(barCallback)); + control.replay(); + + mockService.callMethod(fooDescriptor, mockController, + fooRequest, fooCallback); + mockService.callMethod(barDescriptor, mockController, + barRequest, barCallback); + control.verify(); + } + + /** Tests Service.get{Request,Response}Prototype(). */ + public void testGetPrototype() throws Exception { + TestService mockService = control.createMock(TestService.class); + + assertSame(mockService.getRequestPrototype(fooDescriptor), + FooRequest.getDefaultInstance()); + assertSame(mockService.getResponsePrototype(fooDescriptor), + FooResponse.getDefaultInstance()); + assertSame(mockService.getRequestPrototype(barDescriptor), + BarRequest.getDefaultInstance()); + assertSame(mockService.getResponsePrototype(barDescriptor), + BarResponse.getDefaultInstance()); + } + + /** Tests generated stubs. */ + public void testStub() throws Exception { + FooRequest fooRequest = FooRequest.newBuilder().build(); + BarRequest barRequest = BarRequest.newBuilder().build(); + MockCallback fooCallback = new MockCallback(); + MockCallback barCallback = new MockCallback(); + RpcChannel mockChannel = control.createMock(RpcChannel.class); + TestService stub = TestService.newStub(mockChannel); + + mockChannel.callMethod( + EasyMock.same(fooDescriptor), + EasyMock.same(mockController), + EasyMock.same(fooRequest), + EasyMock.same(FooResponse.getDefaultInstance()), + this.wrapsCallback(fooCallback)); + mockChannel.callMethod( + EasyMock.same(barDescriptor), + EasyMock.same(mockController), + EasyMock.same(barRequest), + EasyMock.same(BarResponse.getDefaultInstance()), + this.wrapsCallback(barCallback)); + control.replay(); + + stub.foo(mockController, fooRequest, fooCallback); + stub.bar(mockController, barRequest, barCallback); + control.verify(); + } + + // ================================================================= + + /** + * wrapsCallback() is an EasyMock argument predicate. wrapsCallback(c) + * matches a callback if calling that callback causes c to be called. + * In other words, c wraps the given callback. + */ + private RpcCallback wrapsCallback( + MockCallback callback) { + EasyMock.reportMatcher(new WrapsCallback(callback)); + return null; + } + + /** The parameter to wrapsCallback() must be a MockCallback. */ + private static class MockCallback + implements RpcCallback { + private boolean called = false; + + public boolean isCalled() { return called; } + + public void reset() { called = false; } + public void run(Type message) { called = true; } + } + + /** Implementation of the wrapsCallback() argument matcher. */ + private static class WrapsCallback implements IArgumentMatcher { + private MockCallback callback; + + public WrapsCallback(MockCallback callback) { + this.callback = callback; + } + + @SuppressWarnings("unchecked") + public boolean matches(Object actual) { + if (!(actual instanceof RpcCallback)) { + return false; + } + RpcCallback actualCallback = (RpcCallback)actual; + + callback.reset(); + actualCallback.run(null); + return callback.isCalled(); + } + + public void appendTo(StringBuffer buffer) { + buffer.append("wrapsCallback(mockCallback)"); + } + } +} diff --git a/java/src/test/java/com/google/protobuf/TestUtil.java b/java/src/test/java/com/google/protobuf/TestUtil.java new file mode 100644 index 00000000..af493ad1 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/TestUtil.java @@ -0,0 +1,2402 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Note: This file contains many lines over 80 characters. It even contains +// many lines over 100 characters, which fails a presubmit test. However, +// given the extremely repetitive nature of the file, I (kenton) feel that +// having similar components of each statement line up is more important than +// avoiding horizontal scrolling. So, I am bypassing the presubmit check. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.ForeignEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; +import com.google.protobuf.test.UnittestImport.ImportEnum; + +import junit.framework.Assert; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Contains methods for setting all fields of {@code TestAllTypes} to + * some vaules as well as checking that all the fields are set to those values. + * These are useful for testing various protocol message features, e.g. + * set all fields of a message, serialize it, parse it, and check that all + * fields are set. + * + * @author kenton@google.com Kenton Varda + */ +class TestUtil { + private TestUtil() {} + + /** Helper to convert a String to ByteString. */ + private static ByteString toBytes(String str) { + try { + return ByteString.copyFrom(str.getBytes("UTF-8")); + } catch(java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Get a {@code TestAllTypes} with all fields set as they would be by + * {@link #setAllFields(TestAllTypes.Builder)}. + */ + public static TestAllTypes getAllSet() { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + setAllFields(builder); + return builder.build(); + } + + /** + * Get a {@code TestAllExtensions} with all fields set as they would be by + * {@link #setAllExtensions(TestAllExtensions.Builder)}. + */ + public static TestAllExtensions getAllExtensionsSet() { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + setAllExtensions(builder); + return builder.build(); + } + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllFieldsSet()}. + */ + public static void setAllFields(TestAllTypes.Builder message) { + message.setOptionalInt32 (101); + message.setOptionalInt64 (102); + message.setOptionalUint32 (103); + message.setOptionalUint64 (104); + message.setOptionalSint32 (105); + message.setOptionalSint64 (106); + message.setOptionalFixed32 (107); + message.setOptionalFixed64 (108); + message.setOptionalSfixed32(109); + message.setOptionalSfixed64(110); + message.setOptionalFloat (111); + message.setOptionalDouble (112); + message.setOptionalBool (true); + message.setOptionalString ("115"); + message.setOptionalBytes (toBytes("116")); + + message.setOptionalGroup( + TestAllTypes.OptionalGroup.newBuilder().setA(117).build()); + message.setOptionalNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(118).build()); + message.setOptionalForeignMessage( + ForeignMessage.newBuilder().setC(119).build()); + message.setOptionalImportMessage( + ImportMessage.newBuilder().setD(120).build()); + + message.setOptionalNestedEnum (TestAllTypes.NestedEnum.BAZ); + message.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ); + message.setOptionalImportEnum (ImportEnum.IMPORT_BAZ); + + message.setOptionalStringPiece("124"); + message.setOptionalCord("125"); + + // ----------------------------------------------------------------- + + message.addRepeatedInt32 (201); + message.addRepeatedInt64 (202); + message.addRepeatedUint32 (203); + message.addRepeatedUint64 (204); + message.addRepeatedSint32 (205); + message.addRepeatedSint64 (206); + message.addRepeatedFixed32 (207); + message.addRepeatedFixed64 (208); + message.addRepeatedSfixed32(209); + message.addRepeatedSfixed64(210); + message.addRepeatedFloat (211); + message.addRepeatedDouble (212); + message.addRepeatedBool (true); + message.addRepeatedString ("215"); + message.addRepeatedBytes (toBytes("216")); + + message.addRepeatedGroup( + TestAllTypes.RepeatedGroup.newBuilder().setA(217).build()); + message.addRepeatedNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(218).build()); + message.addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(219).build()); + message.addRepeatedImportMessage( + ImportMessage.newBuilder().setD(220).build()); + + message.addRepeatedNestedEnum (TestAllTypes.NestedEnum.BAR); + message.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAR); + message.addRepeatedImportEnum (ImportEnum.IMPORT_BAR); + + message.addRepeatedStringPiece("224"); + message.addRepeatedCord("225"); + + // Add a second one of each field. + message.addRepeatedInt32 (301); + message.addRepeatedInt64 (302); + message.addRepeatedUint32 (303); + message.addRepeatedUint64 (304); + message.addRepeatedSint32 (305); + message.addRepeatedSint64 (306); + message.addRepeatedFixed32 (307); + message.addRepeatedFixed64 (308); + message.addRepeatedSfixed32(309); + message.addRepeatedSfixed64(310); + message.addRepeatedFloat (311); + message.addRepeatedDouble (312); + message.addRepeatedBool (false); + message.addRepeatedString ("315"); + message.addRepeatedBytes (toBytes("316")); + + message.addRepeatedGroup( + TestAllTypes.RepeatedGroup.newBuilder().setA(317).build()); + message.addRepeatedNestedMessage( + TestAllTypes.NestedMessage.newBuilder().setBb(318).build()); + message.addRepeatedForeignMessage( + ForeignMessage.newBuilder().setC(319).build()); + message.addRepeatedImportMessage( + ImportMessage.newBuilder().setD(320).build()); + + message.addRepeatedNestedEnum (TestAllTypes.NestedEnum.BAZ); + message.addRepeatedForeignEnum(ForeignEnum.FOREIGN_BAZ); + message.addRepeatedImportEnum (ImportEnum.IMPORT_BAZ); + + message.addRepeatedStringPiece("324"); + message.addRepeatedCord("325"); + + // ----------------------------------------------------------------- + + message.setDefaultInt32 (401); + message.setDefaultInt64 (402); + message.setDefaultUint32 (403); + message.setDefaultUint64 (404); + message.setDefaultSint32 (405); + message.setDefaultSint64 (406); + message.setDefaultFixed32 (407); + message.setDefaultFixed64 (408); + message.setDefaultSfixed32(409); + message.setDefaultSfixed64(410); + message.setDefaultFloat (411); + message.setDefaultDouble (412); + message.setDefaultBool (false); + message.setDefaultString ("415"); + message.setDefaultBytes (toBytes("416")); + + message.setDefaultNestedEnum (TestAllTypes.NestedEnum.FOO); + message.setDefaultForeignEnum(ForeignEnum.FOREIGN_FOO); + message.setDefaultImportEnum (ImportEnum.IMPORT_FOO); + + message.setDefaultStringPiece("424"); + message.setDefaultCord("425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated fields of {@code message} to contain the values + * expected by {@code assertRepeatedFieldsModified()}. + */ + public static void modifyRepeatedFields(TestAllTypes.Builder message) { + message.setRepeatedInt32 (1, 501); + message.setRepeatedInt64 (1, 502); + message.setRepeatedUint32 (1, 503); + message.setRepeatedUint64 (1, 504); + message.setRepeatedSint32 (1, 505); + message.setRepeatedSint64 (1, 506); + message.setRepeatedFixed32 (1, 507); + message.setRepeatedFixed64 (1, 508); + message.setRepeatedSfixed32(1, 509); + message.setRepeatedSfixed64(1, 510); + message.setRepeatedFloat (1, 511); + message.setRepeatedDouble (1, 512); + message.setRepeatedBool (1, true); + message.setRepeatedString (1, "515"); + message.setRepeatedBytes (1, toBytes("516")); + + message.setRepeatedGroup(1, + TestAllTypes.RepeatedGroup.newBuilder().setA(517).build()); + message.setRepeatedNestedMessage(1, + TestAllTypes.NestedMessage.newBuilder().setBb(518).build()); + message.setRepeatedForeignMessage(1, + ForeignMessage.newBuilder().setC(519).build()); + message.setRepeatedImportMessage(1, + ImportMessage.newBuilder().setD(520).build()); + + message.setRepeatedNestedEnum (1, TestAllTypes.NestedEnum.FOO); + message.setRepeatedForeignEnum(1, ForeignEnum.FOREIGN_FOO); + message.setRepeatedImportEnum (1, ImportEnum.IMPORT_FOO); + + message.setRepeatedStringPiece(1, "524"); + message.setRepeatedCord(1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields}. + */ + public static void assertAllFieldsSet(TestAllTypes message) { + Assert.assertTrue(message.hasOptionalInt32 ()); + Assert.assertTrue(message.hasOptionalInt64 ()); + Assert.assertTrue(message.hasOptionalUint32 ()); + Assert.assertTrue(message.hasOptionalUint64 ()); + Assert.assertTrue(message.hasOptionalSint32 ()); + Assert.assertTrue(message.hasOptionalSint64 ()); + Assert.assertTrue(message.hasOptionalFixed32 ()); + Assert.assertTrue(message.hasOptionalFixed64 ()); + Assert.assertTrue(message.hasOptionalSfixed32()); + Assert.assertTrue(message.hasOptionalSfixed64()); + Assert.assertTrue(message.hasOptionalFloat ()); + Assert.assertTrue(message.hasOptionalDouble ()); + Assert.assertTrue(message.hasOptionalBool ()); + Assert.assertTrue(message.hasOptionalString ()); + Assert.assertTrue(message.hasOptionalBytes ()); + + Assert.assertTrue(message.hasOptionalGroup ()); + Assert.assertTrue(message.hasOptionalNestedMessage ()); + Assert.assertTrue(message.hasOptionalForeignMessage()); + Assert.assertTrue(message.hasOptionalImportMessage ()); + + Assert.assertTrue(message.getOptionalGroup ().hasA()); + Assert.assertTrue(message.getOptionalNestedMessage ().hasBb()); + Assert.assertTrue(message.getOptionalForeignMessage().hasC()); + Assert.assertTrue(message.getOptionalImportMessage ().hasD()); + + Assert.assertTrue(message.hasOptionalNestedEnum ()); + Assert.assertTrue(message.hasOptionalForeignEnum()); + Assert.assertTrue(message.hasOptionalImportEnum ()); + + Assert.assertTrue(message.hasOptionalStringPiece()); + Assert.assertTrue(message.hasOptionalCord()); + + Assert.assertEquals(101 , message.getOptionalInt32 ()); + Assert.assertEquals(102 , message.getOptionalInt64 ()); + Assert.assertEquals(103 , message.getOptionalUint32 ()); + Assert.assertEquals(104 , message.getOptionalUint64 ()); + Assert.assertEquals(105 , message.getOptionalSint32 ()); + Assert.assertEquals(106 , message.getOptionalSint64 ()); + Assert.assertEquals(107 , message.getOptionalFixed32 ()); + Assert.assertEquals(108 , message.getOptionalFixed64 ()); + Assert.assertEquals(109 , message.getOptionalSfixed32()); + Assert.assertEquals(110 , message.getOptionalSfixed64()); + Assert.assertEquals(111 , message.getOptionalFloat (), 0.0); + Assert.assertEquals(112 , message.getOptionalDouble (), 0.0); + Assert.assertEquals(true , message.getOptionalBool ()); + Assert.assertEquals("115", message.getOptionalString ()); + Assert.assertEquals(toBytes("116"), message.getOptionalBytes()); + + Assert.assertEquals(117, message.getOptionalGroup ().getA()); + Assert.assertEquals(118, message.getOptionalNestedMessage ().getBb()); + Assert.assertEquals(119, message.getOptionalForeignMessage().getC()); + Assert.assertEquals(120, message.getOptionalImportMessage ().getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAZ, message.getOptionalNestedEnum()); + Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getOptionalForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_BAZ, message.getOptionalImportEnum()); + + Assert.assertEquals("124", message.getOptionalStringPiece()); + Assert.assertEquals("125", message.getOptionalCord()); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getRepeatedInt32Count ()); + Assert.assertEquals(2, message.getRepeatedInt64Count ()); + Assert.assertEquals(2, message.getRepeatedUint32Count ()); + Assert.assertEquals(2, message.getRepeatedUint64Count ()); + Assert.assertEquals(2, message.getRepeatedSint32Count ()); + Assert.assertEquals(2, message.getRepeatedSint64Count ()); + Assert.assertEquals(2, message.getRepeatedFixed32Count ()); + Assert.assertEquals(2, message.getRepeatedFixed64Count ()); + Assert.assertEquals(2, message.getRepeatedSfixed32Count()); + Assert.assertEquals(2, message.getRepeatedSfixed64Count()); + Assert.assertEquals(2, message.getRepeatedFloatCount ()); + Assert.assertEquals(2, message.getRepeatedDoubleCount ()); + Assert.assertEquals(2, message.getRepeatedBoolCount ()); + Assert.assertEquals(2, message.getRepeatedStringCount ()); + Assert.assertEquals(2, message.getRepeatedBytesCount ()); + + Assert.assertEquals(2, message.getRepeatedGroupCount ()); + Assert.assertEquals(2, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(2, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(2, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(2, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(2, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(2, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(2, message.getRepeatedStringPieceCount()); + Assert.assertEquals(2, message.getRepeatedCordCount()); + + Assert.assertEquals(201 , message.getRepeatedInt32 (0)); + Assert.assertEquals(202 , message.getRepeatedInt64 (0)); + Assert.assertEquals(203 , message.getRepeatedUint32 (0)); + Assert.assertEquals(204 , message.getRepeatedUint64 (0)); + Assert.assertEquals(205 , message.getRepeatedSint32 (0)); + Assert.assertEquals(206 , message.getRepeatedSint64 (0)); + Assert.assertEquals(207 , message.getRepeatedFixed32 (0)); + Assert.assertEquals(208 , message.getRepeatedFixed64 (0)); + Assert.assertEquals(209 , message.getRepeatedSfixed32(0)); + Assert.assertEquals(210 , message.getRepeatedSfixed64(0)); + Assert.assertEquals(211 , message.getRepeatedFloat (0), 0.0); + Assert.assertEquals(212 , message.getRepeatedDouble (0), 0.0); + Assert.assertEquals(true , message.getRepeatedBool (0)); + Assert.assertEquals("215", message.getRepeatedString (0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedBytes(0)); + + Assert.assertEquals(217, message.getRepeatedGroup (0).getA()); + Assert.assertEquals(218, message.getRepeatedNestedMessage (0).getBb()); + Assert.assertEquals(219, message.getRepeatedForeignMessage(0).getC()); + Assert.assertEquals(220, message.getRepeatedImportMessage (0).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnum (0)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnum(0)); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnum(0)); + + Assert.assertEquals("224", message.getRepeatedStringPiece(0)); + Assert.assertEquals("225", message.getRepeatedCord(0)); + + Assert.assertEquals(301 , message.getRepeatedInt32 (1)); + Assert.assertEquals(302 , message.getRepeatedInt64 (1)); + Assert.assertEquals(303 , message.getRepeatedUint32 (1)); + Assert.assertEquals(304 , message.getRepeatedUint64 (1)); + Assert.assertEquals(305 , message.getRepeatedSint32 (1)); + Assert.assertEquals(306 , message.getRepeatedSint64 (1)); + Assert.assertEquals(307 , message.getRepeatedFixed32 (1)); + Assert.assertEquals(308 , message.getRepeatedFixed64 (1)); + Assert.assertEquals(309 , message.getRepeatedSfixed32(1)); + Assert.assertEquals(310 , message.getRepeatedSfixed64(1)); + Assert.assertEquals(311 , message.getRepeatedFloat (1), 0.0); + Assert.assertEquals(312 , message.getRepeatedDouble (1), 0.0); + Assert.assertEquals(false, message.getRepeatedBool (1)); + Assert.assertEquals("315", message.getRepeatedString (1)); + Assert.assertEquals(toBytes("316"), message.getRepeatedBytes(1)); + + Assert.assertEquals(317, message.getRepeatedGroup (1).getA()); + Assert.assertEquals(318, message.getRepeatedNestedMessage (1).getBb()); + Assert.assertEquals(319, message.getRepeatedForeignMessage(1).getC()); + Assert.assertEquals(320, message.getRepeatedImportMessage (1).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAZ, message.getRepeatedNestedEnum (1)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAZ, message.getRepeatedForeignEnum(1)); + Assert.assertEquals(ImportEnum.IMPORT_BAZ, message.getRepeatedImportEnum(1)); + + Assert.assertEquals("324", message.getRepeatedStringPiece(1)); + Assert.assertEquals("325", message.getRepeatedCord(1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasDefaultInt32 ()); + Assert.assertTrue(message.hasDefaultInt64 ()); + Assert.assertTrue(message.hasDefaultUint32 ()); + Assert.assertTrue(message.hasDefaultUint64 ()); + Assert.assertTrue(message.hasDefaultSint32 ()); + Assert.assertTrue(message.hasDefaultSint64 ()); + Assert.assertTrue(message.hasDefaultFixed32 ()); + Assert.assertTrue(message.hasDefaultFixed64 ()); + Assert.assertTrue(message.hasDefaultSfixed32()); + Assert.assertTrue(message.hasDefaultSfixed64()); + Assert.assertTrue(message.hasDefaultFloat ()); + Assert.assertTrue(message.hasDefaultDouble ()); + Assert.assertTrue(message.hasDefaultBool ()); + Assert.assertTrue(message.hasDefaultString ()); + Assert.assertTrue(message.hasDefaultBytes ()); + + Assert.assertTrue(message.hasDefaultNestedEnum ()); + Assert.assertTrue(message.hasDefaultForeignEnum()); + Assert.assertTrue(message.hasDefaultImportEnum ()); + + Assert.assertTrue(message.hasDefaultStringPiece()); + Assert.assertTrue(message.hasDefaultCord()); + + Assert.assertEquals(401 , message.getDefaultInt32 ()); + Assert.assertEquals(402 , message.getDefaultInt64 ()); + Assert.assertEquals(403 , message.getDefaultUint32 ()); + Assert.assertEquals(404 , message.getDefaultUint64 ()); + Assert.assertEquals(405 , message.getDefaultSint32 ()); + Assert.assertEquals(406 , message.getDefaultSint64 ()); + Assert.assertEquals(407 , message.getDefaultFixed32 ()); + Assert.assertEquals(408 , message.getDefaultFixed64 ()); + Assert.assertEquals(409 , message.getDefaultSfixed32()); + Assert.assertEquals(410 , message.getDefaultSfixed64()); + Assert.assertEquals(411 , message.getDefaultFloat (), 0.0); + Assert.assertEquals(412 , message.getDefaultDouble (), 0.0); + Assert.assertEquals(false, message.getDefaultBool ()); + Assert.assertEquals("415", message.getDefaultString ()); + Assert.assertEquals(toBytes("416"), message.getDefaultBytes()); + + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getDefaultNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getDefaultForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getDefaultImportEnum()); + + Assert.assertEquals("424", message.getDefaultStringPiece()); + Assert.assertEquals("425", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are cleared, and that getting the fields returns their + * default values. + */ + public static void assertClear(TestAllTypes message) { + // hasBlah() should initially be false for all optional fields. + Assert.assertFalse(message.hasOptionalInt32 ()); + Assert.assertFalse(message.hasOptionalInt64 ()); + Assert.assertFalse(message.hasOptionalUint32 ()); + Assert.assertFalse(message.hasOptionalUint64 ()); + Assert.assertFalse(message.hasOptionalSint32 ()); + Assert.assertFalse(message.hasOptionalSint64 ()); + Assert.assertFalse(message.hasOptionalFixed32 ()); + Assert.assertFalse(message.hasOptionalFixed64 ()); + Assert.assertFalse(message.hasOptionalSfixed32()); + Assert.assertFalse(message.hasOptionalSfixed64()); + Assert.assertFalse(message.hasOptionalFloat ()); + Assert.assertFalse(message.hasOptionalDouble ()); + Assert.assertFalse(message.hasOptionalBool ()); + Assert.assertFalse(message.hasOptionalString ()); + Assert.assertFalse(message.hasOptionalBytes ()); + + Assert.assertFalse(message.hasOptionalGroup ()); + Assert.assertFalse(message.hasOptionalNestedMessage ()); + Assert.assertFalse(message.hasOptionalForeignMessage()); + Assert.assertFalse(message.hasOptionalImportMessage ()); + + Assert.assertFalse(message.hasOptionalNestedEnum ()); + Assert.assertFalse(message.hasOptionalForeignEnum()); + Assert.assertFalse(message.hasOptionalImportEnum ()); + + Assert.assertFalse(message.hasOptionalStringPiece()); + Assert.assertFalse(message.hasOptionalCord()); + + // Optional fields without defaults are set to zero or something like it. + Assert.assertEquals(0 , message.getOptionalInt32 ()); + Assert.assertEquals(0 , message.getOptionalInt64 ()); + Assert.assertEquals(0 , message.getOptionalUint32 ()); + Assert.assertEquals(0 , message.getOptionalUint64 ()); + Assert.assertEquals(0 , message.getOptionalSint32 ()); + Assert.assertEquals(0 , message.getOptionalSint64 ()); + Assert.assertEquals(0 , message.getOptionalFixed32 ()); + Assert.assertEquals(0 , message.getOptionalFixed64 ()); + Assert.assertEquals(0 , message.getOptionalSfixed32()); + Assert.assertEquals(0 , message.getOptionalSfixed64()); + Assert.assertEquals(0 , message.getOptionalFloat (), 0.0); + Assert.assertEquals(0 , message.getOptionalDouble (), 0.0); + Assert.assertEquals(false, message.getOptionalBool ()); + Assert.assertEquals("" , message.getOptionalString ()); + Assert.assertEquals(ByteString.EMPTY, message.getOptionalBytes()); + + // Embedded messages should also be clear. + Assert.assertFalse(message.getOptionalGroup ().hasA()); + Assert.assertFalse(message.getOptionalNestedMessage ().hasBb()); + Assert.assertFalse(message.getOptionalForeignMessage().hasC()); + Assert.assertFalse(message.getOptionalImportMessage ().hasD()); + + Assert.assertEquals(0, message.getOptionalGroup ().getA()); + Assert.assertEquals(0, message.getOptionalNestedMessage ().getBb()); + Assert.assertEquals(0, message.getOptionalForeignMessage().getC()); + Assert.assertEquals(0, message.getOptionalImportMessage ().getD()); + + // Enums without defaults are set to the first value in the enum. + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getOptionalNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getOptionalForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getOptionalImportEnum()); + + Assert.assertEquals("", message.getOptionalStringPiece()); + Assert.assertEquals("", message.getOptionalCord()); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getRepeatedInt32Count ()); + Assert.assertEquals(0, message.getRepeatedInt64Count ()); + Assert.assertEquals(0, message.getRepeatedUint32Count ()); + Assert.assertEquals(0, message.getRepeatedUint64Count ()); + Assert.assertEquals(0, message.getRepeatedSint32Count ()); + Assert.assertEquals(0, message.getRepeatedSint64Count ()); + Assert.assertEquals(0, message.getRepeatedFixed32Count ()); + Assert.assertEquals(0, message.getRepeatedFixed64Count ()); + Assert.assertEquals(0, message.getRepeatedSfixed32Count()); + Assert.assertEquals(0, message.getRepeatedSfixed64Count()); + Assert.assertEquals(0, message.getRepeatedFloatCount ()); + Assert.assertEquals(0, message.getRepeatedDoubleCount ()); + Assert.assertEquals(0, message.getRepeatedBoolCount ()); + Assert.assertEquals(0, message.getRepeatedStringCount ()); + Assert.assertEquals(0, message.getRepeatedBytesCount ()); + + Assert.assertEquals(0, message.getRepeatedGroupCount ()); + Assert.assertEquals(0, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(0, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(0, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(0, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(0, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(0, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(0, message.getRepeatedStringPieceCount()); + Assert.assertEquals(0, message.getRepeatedCordCount()); + + // hasBlah() should also be false for all default fields. + Assert.assertFalse(message.hasDefaultInt32 ()); + Assert.assertFalse(message.hasDefaultInt64 ()); + Assert.assertFalse(message.hasDefaultUint32 ()); + Assert.assertFalse(message.hasDefaultUint64 ()); + Assert.assertFalse(message.hasDefaultSint32 ()); + Assert.assertFalse(message.hasDefaultSint64 ()); + Assert.assertFalse(message.hasDefaultFixed32 ()); + Assert.assertFalse(message.hasDefaultFixed64 ()); + Assert.assertFalse(message.hasDefaultSfixed32()); + Assert.assertFalse(message.hasDefaultSfixed64()); + Assert.assertFalse(message.hasDefaultFloat ()); + Assert.assertFalse(message.hasDefaultDouble ()); + Assert.assertFalse(message.hasDefaultBool ()); + Assert.assertFalse(message.hasDefaultString ()); + Assert.assertFalse(message.hasDefaultBytes ()); + + Assert.assertFalse(message.hasDefaultNestedEnum ()); + Assert.assertFalse(message.hasDefaultForeignEnum()); + Assert.assertFalse(message.hasDefaultImportEnum ()); + + Assert.assertFalse(message.hasDefaultStringPiece()); + Assert.assertFalse(message.hasDefaultCord()); + + // Fields with defaults have their default values (duh). + Assert.assertEquals( 41 , message.getDefaultInt32 ()); + Assert.assertEquals( 42 , message.getDefaultInt64 ()); + Assert.assertEquals( 43 , message.getDefaultUint32 ()); + Assert.assertEquals( 44 , message.getDefaultUint64 ()); + Assert.assertEquals(-45 , message.getDefaultSint32 ()); + Assert.assertEquals( 46 , message.getDefaultSint64 ()); + Assert.assertEquals( 47 , message.getDefaultFixed32 ()); + Assert.assertEquals( 48 , message.getDefaultFixed64 ()); + Assert.assertEquals( 49 , message.getDefaultSfixed32()); + Assert.assertEquals(-50 , message.getDefaultSfixed64()); + Assert.assertEquals( 51.5 , message.getDefaultFloat (), 0.0); + Assert.assertEquals( 52e3 , message.getDefaultDouble (), 0.0); + Assert.assertEquals(true , message.getDefaultBool ()); + Assert.assertEquals("hello", message.getDefaultString ()); + Assert.assertEquals(toBytes("world"), message.getDefaultBytes()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getDefaultNestedEnum ()); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getDefaultForeignEnum()); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getDefaultImportEnum()); + + Assert.assertEquals("abc", message.getDefaultStringPiece()); + Assert.assertEquals("123", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields} + * followed by {@code modifyRepeatedFields}. + */ + public static void assertRepeatedFieldsModified(TestAllTypes message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getRepeatedInt32Count ()); + Assert.assertEquals(2, message.getRepeatedInt64Count ()); + Assert.assertEquals(2, message.getRepeatedUint32Count ()); + Assert.assertEquals(2, message.getRepeatedUint64Count ()); + Assert.assertEquals(2, message.getRepeatedSint32Count ()); + Assert.assertEquals(2, message.getRepeatedSint64Count ()); + Assert.assertEquals(2, message.getRepeatedFixed32Count ()); + Assert.assertEquals(2, message.getRepeatedFixed64Count ()); + Assert.assertEquals(2, message.getRepeatedSfixed32Count()); + Assert.assertEquals(2, message.getRepeatedSfixed64Count()); + Assert.assertEquals(2, message.getRepeatedFloatCount ()); + Assert.assertEquals(2, message.getRepeatedDoubleCount ()); + Assert.assertEquals(2, message.getRepeatedBoolCount ()); + Assert.assertEquals(2, message.getRepeatedStringCount ()); + Assert.assertEquals(2, message.getRepeatedBytesCount ()); + + Assert.assertEquals(2, message.getRepeatedGroupCount ()); + Assert.assertEquals(2, message.getRepeatedNestedMessageCount ()); + Assert.assertEquals(2, message.getRepeatedForeignMessageCount()); + Assert.assertEquals(2, message.getRepeatedImportMessageCount ()); + Assert.assertEquals(2, message.getRepeatedNestedEnumCount ()); + Assert.assertEquals(2, message.getRepeatedForeignEnumCount ()); + Assert.assertEquals(2, message.getRepeatedImportEnumCount ()); + + Assert.assertEquals(2, message.getRepeatedStringPieceCount()); + Assert.assertEquals(2, message.getRepeatedCordCount()); + + Assert.assertEquals(201 , message.getRepeatedInt32 (0)); + Assert.assertEquals(202L , message.getRepeatedInt64 (0)); + Assert.assertEquals(203 , message.getRepeatedUint32 (0)); + Assert.assertEquals(204L , message.getRepeatedUint64 (0)); + Assert.assertEquals(205 , message.getRepeatedSint32 (0)); + Assert.assertEquals(206L , message.getRepeatedSint64 (0)); + Assert.assertEquals(207 , message.getRepeatedFixed32 (0)); + Assert.assertEquals(208L , message.getRepeatedFixed64 (0)); + Assert.assertEquals(209 , message.getRepeatedSfixed32(0)); + Assert.assertEquals(210L , message.getRepeatedSfixed64(0)); + Assert.assertEquals(211F , message.getRepeatedFloat (0)); + Assert.assertEquals(212D , message.getRepeatedDouble (0)); + Assert.assertEquals(true , message.getRepeatedBool (0)); + Assert.assertEquals("215", message.getRepeatedString (0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedBytes(0)); + + Assert.assertEquals(217, message.getRepeatedGroup (0).getA()); + Assert.assertEquals(218, message.getRepeatedNestedMessage (0).getBb()); + Assert.assertEquals(219, message.getRepeatedForeignMessage(0).getC()); + Assert.assertEquals(220, message.getRepeatedImportMessage (0).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnum (0)); + Assert.assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnum(0)); + Assert.assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnum(0)); + + Assert.assertEquals("224", message.getRepeatedStringPiece(0)); + Assert.assertEquals("225", message.getRepeatedCord(0)); + + // Actually verify the second (modified) elements now. + Assert.assertEquals(501 , message.getRepeatedInt32 (1)); + Assert.assertEquals(502L , message.getRepeatedInt64 (1)); + Assert.assertEquals(503 , message.getRepeatedUint32 (1)); + Assert.assertEquals(504L , message.getRepeatedUint64 (1)); + Assert.assertEquals(505 , message.getRepeatedSint32 (1)); + Assert.assertEquals(506L , message.getRepeatedSint64 (1)); + Assert.assertEquals(507 , message.getRepeatedFixed32 (1)); + Assert.assertEquals(508L , message.getRepeatedFixed64 (1)); + Assert.assertEquals(509 , message.getRepeatedSfixed32(1)); + Assert.assertEquals(510L , message.getRepeatedSfixed64(1)); + Assert.assertEquals(511F , message.getRepeatedFloat (1)); + Assert.assertEquals(512D , message.getRepeatedDouble (1)); + Assert.assertEquals(true , message.getRepeatedBool (1)); + Assert.assertEquals("515", message.getRepeatedString (1)); + Assert.assertEquals(toBytes("516"), message.getRepeatedBytes(1)); + + Assert.assertEquals(517, message.getRepeatedGroup (1).getA()); + Assert.assertEquals(518, message.getRepeatedNestedMessage (1).getBb()); + Assert.assertEquals(519, message.getRepeatedForeignMessage(1).getC()); + Assert.assertEquals(520, message.getRepeatedImportMessage (1).getD()); + + Assert.assertEquals(TestAllTypes.NestedEnum.FOO, message.getRepeatedNestedEnum (1)); + Assert.assertEquals(ForeignEnum.FOREIGN_FOO, message.getRepeatedForeignEnum(1)); + Assert.assertEquals(ImportEnum.IMPORT_FOO, message.getRepeatedImportEnum(1)); + + Assert.assertEquals("524", message.getRepeatedStringPiece(1)); + Assert.assertEquals("525", message.getRepeatedCord(1)); + } + + // =================================================================== + // Like above, but for extensions + + // Java gets confused with things like assertEquals(int, Integer): it can't + // decide whether to call assertEquals(int, int) or assertEquals(Object, + // Object). So we define these methods to help it. + private static void assertEqualsExactType(int a, int b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(long a, long b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(float a, float b) { + Assert.assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(double a, double b) { + Assert.assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(boolean a, boolean b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(String a, String b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ByteString a, ByteString b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(TestAllTypes.NestedEnum a, + TestAllTypes.NestedEnum b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ForeignEnum a, ForeignEnum b) { + Assert.assertEquals(a, b); + } + private static void assertEqualsExactType(ImportEnum a, ImportEnum b) { + Assert.assertEquals(a, b); + } + + /** + * Get an unmodifiable {@link ExtensionRegistry} containing all the + * extensions of {@code TestAllExtensions}. + */ + public static ExtensionRegistry getExtensionRegistry() { + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + registerAllExtensions(registry); + return registry.getUnmodifiable(); + } + + /** + * Register all of {@code TestAllExtensions}' extensions with the + * given {@link ExtensionRegistry}. + */ + public static void registerAllExtensions(ExtensionRegistry registry) { + registry.add(UnittestProto.optionalInt32Extension ); + registry.add(UnittestProto.optionalInt64Extension ); + registry.add(UnittestProto.optionalUint32Extension ); + registry.add(UnittestProto.optionalUint64Extension ); + registry.add(UnittestProto.optionalSint32Extension ); + registry.add(UnittestProto.optionalSint64Extension ); + registry.add(UnittestProto.optionalFixed32Extension ); + registry.add(UnittestProto.optionalFixed64Extension ); + registry.add(UnittestProto.optionalSfixed32Extension ); + registry.add(UnittestProto.optionalSfixed64Extension ); + registry.add(UnittestProto.optionalFloatExtension ); + registry.add(UnittestProto.optionalDoubleExtension ); + registry.add(UnittestProto.optionalBoolExtension ); + registry.add(UnittestProto.optionalStringExtension ); + registry.add(UnittestProto.optionalBytesExtension ); + registry.add(UnittestProto.optionalGroupExtension ); + registry.add(UnittestProto.optionalNestedMessageExtension ); + registry.add(UnittestProto.optionalForeignMessageExtension); + registry.add(UnittestProto.optionalImportMessageExtension ); + registry.add(UnittestProto.optionalNestedEnumExtension ); + registry.add(UnittestProto.optionalForeignEnumExtension ); + registry.add(UnittestProto.optionalImportEnumExtension ); + registry.add(UnittestProto.optionalStringPieceExtension ); + registry.add(UnittestProto.optionalCordExtension ); + + registry.add(UnittestProto.repeatedInt32Extension ); + registry.add(UnittestProto.repeatedInt64Extension ); + registry.add(UnittestProto.repeatedUint32Extension ); + registry.add(UnittestProto.repeatedUint64Extension ); + registry.add(UnittestProto.repeatedSint32Extension ); + registry.add(UnittestProto.repeatedSint64Extension ); + registry.add(UnittestProto.repeatedFixed32Extension ); + registry.add(UnittestProto.repeatedFixed64Extension ); + registry.add(UnittestProto.repeatedSfixed32Extension ); + registry.add(UnittestProto.repeatedSfixed64Extension ); + registry.add(UnittestProto.repeatedFloatExtension ); + registry.add(UnittestProto.repeatedDoubleExtension ); + registry.add(UnittestProto.repeatedBoolExtension ); + registry.add(UnittestProto.repeatedStringExtension ); + registry.add(UnittestProto.repeatedBytesExtension ); + registry.add(UnittestProto.repeatedGroupExtension ); + registry.add(UnittestProto.repeatedNestedMessageExtension ); + registry.add(UnittestProto.repeatedForeignMessageExtension); + registry.add(UnittestProto.repeatedImportMessageExtension ); + registry.add(UnittestProto.repeatedNestedEnumExtension ); + registry.add(UnittestProto.repeatedForeignEnumExtension ); + registry.add(UnittestProto.repeatedImportEnumExtension ); + registry.add(UnittestProto.repeatedStringPieceExtension ); + registry.add(UnittestProto.repeatedCordExtension ); + + registry.add(UnittestProto.defaultInt32Extension ); + registry.add(UnittestProto.defaultInt64Extension ); + registry.add(UnittestProto.defaultUint32Extension ); + registry.add(UnittestProto.defaultUint64Extension ); + registry.add(UnittestProto.defaultSint32Extension ); + registry.add(UnittestProto.defaultSint64Extension ); + registry.add(UnittestProto.defaultFixed32Extension ); + registry.add(UnittestProto.defaultFixed64Extension ); + registry.add(UnittestProto.defaultSfixed32Extension ); + registry.add(UnittestProto.defaultSfixed64Extension ); + registry.add(UnittestProto.defaultFloatExtension ); + registry.add(UnittestProto.defaultDoubleExtension ); + registry.add(UnittestProto.defaultBoolExtension ); + registry.add(UnittestProto.defaultStringExtension ); + registry.add(UnittestProto.defaultBytesExtension ); + registry.add(UnittestProto.defaultNestedEnumExtension ); + registry.add(UnittestProto.defaultForeignEnumExtension); + registry.add(UnittestProto.defaultImportEnumExtension ); + registry.add(UnittestProto.defaultStringPieceExtension); + registry.add(UnittestProto.defaultCordExtension ); + } + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllExtensionsSet()}. + */ + public static void setAllExtensions(TestAllExtensions.Builder message) { + message.setExtension(UnittestProto.optionalInt32Extension , 101); + message.setExtension(UnittestProto.optionalInt64Extension , 102L); + message.setExtension(UnittestProto.optionalUint32Extension , 103); + message.setExtension(UnittestProto.optionalUint64Extension , 104L); + message.setExtension(UnittestProto.optionalSint32Extension , 105); + message.setExtension(UnittestProto.optionalSint64Extension , 106L); + message.setExtension(UnittestProto.optionalFixed32Extension , 107); + message.setExtension(UnittestProto.optionalFixed64Extension , 108L); + message.setExtension(UnittestProto.optionalSfixed32Extension, 109); + message.setExtension(UnittestProto.optionalSfixed64Extension, 110L); + message.setExtension(UnittestProto.optionalFloatExtension , 111F); + message.setExtension(UnittestProto.optionalDoubleExtension , 112D); + message.setExtension(UnittestProto.optionalBoolExtension , true); + message.setExtension(UnittestProto.optionalStringExtension , "115"); + message.setExtension(UnittestProto.optionalBytesExtension , toBytes("116")); + + message.setExtension(UnittestProto.optionalGroupExtension, + UnittestProto.OptionalGroup_extension.newBuilder().setA(117).build()); + message.setExtension(UnittestProto.optionalNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(118).build()); + message.setExtension(UnittestProto.optionalForeignMessageExtension, + ForeignMessage.newBuilder().setC(119).build()); + message.setExtension(UnittestProto.optionalImportMessageExtension, + ImportMessage.newBuilder().setD(120).build()); + + message.setExtension(UnittestProto.optionalNestedEnumExtension, + TestAllTypes.NestedEnum.BAZ); + message.setExtension(UnittestProto.optionalForeignEnumExtension, + ForeignEnum.FOREIGN_BAZ); + message.setExtension(UnittestProto.optionalImportEnumExtension, + ImportEnum.IMPORT_BAZ); + + message.setExtension(UnittestProto.optionalStringPieceExtension, "124"); + message.setExtension(UnittestProto.optionalCordExtension, "125"); + + // ----------------------------------------------------------------- + + message.addExtension(UnittestProto.repeatedInt32Extension , 201); + message.addExtension(UnittestProto.repeatedInt64Extension , 202L); + message.addExtension(UnittestProto.repeatedUint32Extension , 203); + message.addExtension(UnittestProto.repeatedUint64Extension , 204L); + message.addExtension(UnittestProto.repeatedSint32Extension , 205); + message.addExtension(UnittestProto.repeatedSint64Extension , 206L); + message.addExtension(UnittestProto.repeatedFixed32Extension , 207); + message.addExtension(UnittestProto.repeatedFixed64Extension , 208L); + message.addExtension(UnittestProto.repeatedSfixed32Extension, 209); + message.addExtension(UnittestProto.repeatedSfixed64Extension, 210L); + message.addExtension(UnittestProto.repeatedFloatExtension , 211F); + message.addExtension(UnittestProto.repeatedDoubleExtension , 212D); + message.addExtension(UnittestProto.repeatedBoolExtension , true); + message.addExtension(UnittestProto.repeatedStringExtension , "215"); + message.addExtension(UnittestProto.repeatedBytesExtension , toBytes("216")); + + message.addExtension(UnittestProto.repeatedGroupExtension, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(217).build()); + message.addExtension(UnittestProto.repeatedNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(218).build()); + message.addExtension(UnittestProto.repeatedForeignMessageExtension, + ForeignMessage.newBuilder().setC(219).build()); + message.addExtension(UnittestProto.repeatedImportMessageExtension, + ImportMessage.newBuilder().setD(220).build()); + + message.addExtension(UnittestProto.repeatedNestedEnumExtension, + TestAllTypes.NestedEnum.BAR); + message.addExtension(UnittestProto.repeatedForeignEnumExtension, + ForeignEnum.FOREIGN_BAR); + message.addExtension(UnittestProto.repeatedImportEnumExtension, + ImportEnum.IMPORT_BAR); + + message.addExtension(UnittestProto.repeatedStringPieceExtension, "224"); + message.addExtension(UnittestProto.repeatedCordExtension, "225"); + + // Add a second one of each field. + message.addExtension(UnittestProto.repeatedInt32Extension , 301); + message.addExtension(UnittestProto.repeatedInt64Extension , 302L); + message.addExtension(UnittestProto.repeatedUint32Extension , 303); + message.addExtension(UnittestProto.repeatedUint64Extension , 304L); + message.addExtension(UnittestProto.repeatedSint32Extension , 305); + message.addExtension(UnittestProto.repeatedSint64Extension , 306L); + message.addExtension(UnittestProto.repeatedFixed32Extension , 307); + message.addExtension(UnittestProto.repeatedFixed64Extension , 308L); + message.addExtension(UnittestProto.repeatedSfixed32Extension, 309); + message.addExtension(UnittestProto.repeatedSfixed64Extension, 310L); + message.addExtension(UnittestProto.repeatedFloatExtension , 311F); + message.addExtension(UnittestProto.repeatedDoubleExtension , 312D); + message.addExtension(UnittestProto.repeatedBoolExtension , false); + message.addExtension(UnittestProto.repeatedStringExtension , "315"); + message.addExtension(UnittestProto.repeatedBytesExtension , toBytes("316")); + + message.addExtension(UnittestProto.repeatedGroupExtension, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(317).build()); + message.addExtension(UnittestProto.repeatedNestedMessageExtension, + TestAllTypes.NestedMessage.newBuilder().setBb(318).build()); + message.addExtension(UnittestProto.repeatedForeignMessageExtension, + ForeignMessage.newBuilder().setC(319).build()); + message.addExtension(UnittestProto.repeatedImportMessageExtension, + ImportMessage.newBuilder().setD(320).build()); + + message.addExtension(UnittestProto.repeatedNestedEnumExtension, + TestAllTypes.NestedEnum.BAZ); + message.addExtension(UnittestProto.repeatedForeignEnumExtension, + ForeignEnum.FOREIGN_BAZ); + message.addExtension(UnittestProto.repeatedImportEnumExtension, + ImportEnum.IMPORT_BAZ); + + message.addExtension(UnittestProto.repeatedStringPieceExtension, "324"); + message.addExtension(UnittestProto.repeatedCordExtension, "325"); + + // ----------------------------------------------------------------- + + message.setExtension(UnittestProto.defaultInt32Extension , 401); + message.setExtension(UnittestProto.defaultInt64Extension , 402L); + message.setExtension(UnittestProto.defaultUint32Extension , 403); + message.setExtension(UnittestProto.defaultUint64Extension , 404L); + message.setExtension(UnittestProto.defaultSint32Extension , 405); + message.setExtension(UnittestProto.defaultSint64Extension , 406L); + message.setExtension(UnittestProto.defaultFixed32Extension , 407); + message.setExtension(UnittestProto.defaultFixed64Extension , 408L); + message.setExtension(UnittestProto.defaultSfixed32Extension, 409); + message.setExtension(UnittestProto.defaultSfixed64Extension, 410L); + message.setExtension(UnittestProto.defaultFloatExtension , 411F); + message.setExtension(UnittestProto.defaultDoubleExtension , 412D); + message.setExtension(UnittestProto.defaultBoolExtension , false); + message.setExtension(UnittestProto.defaultStringExtension , "415"); + message.setExtension(UnittestProto.defaultBytesExtension , toBytes("416")); + + message.setExtension(UnittestProto.defaultNestedEnumExtension, + TestAllTypes.NestedEnum.FOO); + message.setExtension(UnittestProto.defaultForeignEnumExtension, + ForeignEnum.FOREIGN_FOO); + message.setExtension(UnittestProto.defaultImportEnumExtension, + ImportEnum.IMPORT_FOO); + + message.setExtension(UnittestProto.defaultStringPieceExtension, "424"); + message.setExtension(UnittestProto.defaultCordExtension, "425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated extensions of {@code message} to contain the values + * expected by {@code assertRepeatedExtensionsModified()}. + */ + public static void modifyRepeatedExtensions( + TestAllExtensions.Builder message) { + message.setExtension(UnittestProto.repeatedInt32Extension , 1, 501); + message.setExtension(UnittestProto.repeatedInt64Extension , 1, 502L); + message.setExtension(UnittestProto.repeatedUint32Extension , 1, 503); + message.setExtension(UnittestProto.repeatedUint64Extension , 1, 504L); + message.setExtension(UnittestProto.repeatedSint32Extension , 1, 505); + message.setExtension(UnittestProto.repeatedSint64Extension , 1, 506L); + message.setExtension(UnittestProto.repeatedFixed32Extension , 1, 507); + message.setExtension(UnittestProto.repeatedFixed64Extension , 1, 508L); + message.setExtension(UnittestProto.repeatedSfixed32Extension, 1, 509); + message.setExtension(UnittestProto.repeatedSfixed64Extension, 1, 510L); + message.setExtension(UnittestProto.repeatedFloatExtension , 1, 511F); + message.setExtension(UnittestProto.repeatedDoubleExtension , 1, 512D); + message.setExtension(UnittestProto.repeatedBoolExtension , 1, true); + message.setExtension(UnittestProto.repeatedStringExtension , 1, "515"); + message.setExtension(UnittestProto.repeatedBytesExtension , 1, toBytes("516")); + + message.setExtension(UnittestProto.repeatedGroupExtension, 1, + UnittestProto.RepeatedGroup_extension.newBuilder().setA(517).build()); + message.setExtension(UnittestProto.repeatedNestedMessageExtension, 1, + TestAllTypes.NestedMessage.newBuilder().setBb(518).build()); + message.setExtension(UnittestProto.repeatedForeignMessageExtension, 1, + ForeignMessage.newBuilder().setC(519).build()); + message.setExtension(UnittestProto.repeatedImportMessageExtension, 1, + ImportMessage.newBuilder().setD(520).build()); + + message.setExtension(UnittestProto.repeatedNestedEnumExtension , 1, + TestAllTypes.NestedEnum.FOO); + message.setExtension(UnittestProto.repeatedForeignEnumExtension, 1, + ForeignEnum.FOREIGN_FOO); + message.setExtension(UnittestProto.repeatedImportEnumExtension , 1, + ImportEnum.IMPORT_FOO); + + message.setExtension(UnittestProto.repeatedStringPieceExtension, 1, "524"); + message.setExtension(UnittestProto.repeatedCordExtension, 1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are set to the values assigned by {@code setAllExtensions}. + */ + public static void assertAllExtensionsSet(TestAllExtensions message) { + Assert.assertTrue(message.hasExtension(UnittestProto.optionalInt32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalInt64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalUint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalUint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFixed32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFixed64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSfixed32Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalSfixed64Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalFloatExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalDoubleExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalBoolExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalStringExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalBytesExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalGroupExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalNestedMessageExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalForeignMessageExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalImportMessageExtension )); + + Assert.assertTrue(message.getExtension(UnittestProto.optionalGroupExtension ).hasA()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalNestedMessageExtension ).hasBb()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalForeignMessageExtension).hasC()); + Assert.assertTrue(message.getExtension(UnittestProto.optionalImportMessageExtension ).hasD()); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalNestedEnumExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalForeignEnumExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalImportEnumExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.optionalStringPieceExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.optionalCordExtension)); + + assertEqualsExactType(101 , message.getExtension(UnittestProto.optionalInt32Extension )); + assertEqualsExactType(102L , message.getExtension(UnittestProto.optionalInt64Extension )); + assertEqualsExactType(103 , message.getExtension(UnittestProto.optionalUint32Extension )); + assertEqualsExactType(104L , message.getExtension(UnittestProto.optionalUint64Extension )); + assertEqualsExactType(105 , message.getExtension(UnittestProto.optionalSint32Extension )); + assertEqualsExactType(106L , message.getExtension(UnittestProto.optionalSint64Extension )); + assertEqualsExactType(107 , message.getExtension(UnittestProto.optionalFixed32Extension )); + assertEqualsExactType(108L , message.getExtension(UnittestProto.optionalFixed64Extension )); + assertEqualsExactType(109 , message.getExtension(UnittestProto.optionalSfixed32Extension)); + assertEqualsExactType(110L , message.getExtension(UnittestProto.optionalSfixed64Extension)); + assertEqualsExactType(111F , message.getExtension(UnittestProto.optionalFloatExtension )); + assertEqualsExactType(112D , message.getExtension(UnittestProto.optionalDoubleExtension )); + assertEqualsExactType(true , message.getExtension(UnittestProto.optionalBoolExtension )); + assertEqualsExactType("115", message.getExtension(UnittestProto.optionalStringExtension )); + assertEqualsExactType(toBytes("116"), message.getExtension(UnittestProto.optionalBytesExtension)); + + assertEqualsExactType(117, message.getExtension(UnittestProto.optionalGroupExtension ).getA()); + assertEqualsExactType(118, message.getExtension(UnittestProto.optionalNestedMessageExtension ).getBb()); + assertEqualsExactType(119, message.getExtension(UnittestProto.optionalForeignMessageExtension).getC()); + assertEqualsExactType(120, message.getExtension(UnittestProto.optionalImportMessageExtension ).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAZ, + message.getExtension(UnittestProto.optionalNestedEnumExtension)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAZ, + message.getExtension(UnittestProto.optionalForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_BAZ, + message.getExtension(UnittestProto.optionalImportEnumExtension)); + + assertEqualsExactType("124", message.getExtension(UnittestProto.optionalStringPieceExtension)); + assertEqualsExactType("125", message.getExtension(UnittestProto.optionalCordExtension)); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + assertEqualsExactType(201 , message.getExtension(UnittestProto.repeatedInt32Extension , 0)); + assertEqualsExactType(202L , message.getExtension(UnittestProto.repeatedInt64Extension , 0)); + assertEqualsExactType(203 , message.getExtension(UnittestProto.repeatedUint32Extension , 0)); + assertEqualsExactType(204L , message.getExtension(UnittestProto.repeatedUint64Extension , 0)); + assertEqualsExactType(205 , message.getExtension(UnittestProto.repeatedSint32Extension , 0)); + assertEqualsExactType(206L , message.getExtension(UnittestProto.repeatedSint64Extension , 0)); + assertEqualsExactType(207 , message.getExtension(UnittestProto.repeatedFixed32Extension , 0)); + assertEqualsExactType(208L , message.getExtension(UnittestProto.repeatedFixed64Extension , 0)); + assertEqualsExactType(209 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 0)); + assertEqualsExactType(210L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 0)); + assertEqualsExactType(211F , message.getExtension(UnittestProto.repeatedFloatExtension , 0)); + assertEqualsExactType(212D , message.getExtension(UnittestProto.repeatedDoubleExtension , 0)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 0)); + assertEqualsExactType("215", message.getExtension(UnittestProto.repeatedStringExtension , 0)); + assertEqualsExactType(toBytes("216"), message.getExtension(UnittestProto.repeatedBytesExtension, 0)); + + assertEqualsExactType(217, message.getExtension(UnittestProto.repeatedGroupExtension , 0).getA()); + assertEqualsExactType(218, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 0).getBb()); + assertEqualsExactType(219, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 0).getC()); + assertEqualsExactType(220, message.getExtension(UnittestProto.repeatedImportMessageExtension , 0).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 0)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 0)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 0)); + + assertEqualsExactType("224", message.getExtension(UnittestProto.repeatedStringPieceExtension, 0)); + assertEqualsExactType("225", message.getExtension(UnittestProto.repeatedCordExtension, 0)); + + assertEqualsExactType(301 , message.getExtension(UnittestProto.repeatedInt32Extension , 1)); + assertEqualsExactType(302L , message.getExtension(UnittestProto.repeatedInt64Extension , 1)); + assertEqualsExactType(303 , message.getExtension(UnittestProto.repeatedUint32Extension , 1)); + assertEqualsExactType(304L , message.getExtension(UnittestProto.repeatedUint64Extension , 1)); + assertEqualsExactType(305 , message.getExtension(UnittestProto.repeatedSint32Extension , 1)); + assertEqualsExactType(306L , message.getExtension(UnittestProto.repeatedSint64Extension , 1)); + assertEqualsExactType(307 , message.getExtension(UnittestProto.repeatedFixed32Extension , 1)); + assertEqualsExactType(308L , message.getExtension(UnittestProto.repeatedFixed64Extension , 1)); + assertEqualsExactType(309 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 1)); + assertEqualsExactType(310L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 1)); + assertEqualsExactType(311F , message.getExtension(UnittestProto.repeatedFloatExtension , 1)); + assertEqualsExactType(312D , message.getExtension(UnittestProto.repeatedDoubleExtension , 1)); + assertEqualsExactType(false, message.getExtension(UnittestProto.repeatedBoolExtension , 1)); + assertEqualsExactType("315", message.getExtension(UnittestProto.repeatedStringExtension , 1)); + assertEqualsExactType(toBytes("316"), message.getExtension(UnittestProto.repeatedBytesExtension, 1)); + + assertEqualsExactType(317, message.getExtension(UnittestProto.repeatedGroupExtension , 1).getA()); + assertEqualsExactType(318, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 1).getBb()); + assertEqualsExactType(319, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 1).getC()); + assertEqualsExactType(320, message.getExtension(UnittestProto.repeatedImportMessageExtension , 1).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAZ, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 1)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAZ, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 1)); + assertEqualsExactType(ImportEnum.IMPORT_BAZ, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 1)); + + assertEqualsExactType("324", message.getExtension(UnittestProto.repeatedStringPieceExtension, 1)); + assertEqualsExactType("325", message.getExtension(UnittestProto.repeatedCordExtension, 1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultInt32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultInt64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultUint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultUint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSint32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSint64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFixed32Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFixed64Extension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSfixed32Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultSfixed64Extension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultFloatExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultDoubleExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultBoolExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultStringExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultBytesExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultNestedEnumExtension )); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultForeignEnumExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultImportEnumExtension )); + + Assert.assertTrue(message.hasExtension(UnittestProto.defaultStringPieceExtension)); + Assert.assertTrue(message.hasExtension(UnittestProto.defaultCordExtension)); + + assertEqualsExactType(401 , message.getExtension(UnittestProto.defaultInt32Extension )); + assertEqualsExactType(402L , message.getExtension(UnittestProto.defaultInt64Extension )); + assertEqualsExactType(403 , message.getExtension(UnittestProto.defaultUint32Extension )); + assertEqualsExactType(404L , message.getExtension(UnittestProto.defaultUint64Extension )); + assertEqualsExactType(405 , message.getExtension(UnittestProto.defaultSint32Extension )); + assertEqualsExactType(406L , message.getExtension(UnittestProto.defaultSint64Extension )); + assertEqualsExactType(407 , message.getExtension(UnittestProto.defaultFixed32Extension )); + assertEqualsExactType(408L , message.getExtension(UnittestProto.defaultFixed64Extension )); + assertEqualsExactType(409 , message.getExtension(UnittestProto.defaultSfixed32Extension)); + assertEqualsExactType(410L , message.getExtension(UnittestProto.defaultSfixed64Extension)); + assertEqualsExactType(411F , message.getExtension(UnittestProto.defaultFloatExtension )); + assertEqualsExactType(412D , message.getExtension(UnittestProto.defaultDoubleExtension )); + assertEqualsExactType(false, message.getExtension(UnittestProto.defaultBoolExtension )); + assertEqualsExactType("415", message.getExtension(UnittestProto.defaultStringExtension )); + assertEqualsExactType(toBytes("416"), message.getExtension(UnittestProto.defaultBytesExtension)); + + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.defaultNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.defaultForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.defaultImportEnumExtension)); + + assertEqualsExactType("424", message.getExtension(UnittestProto.defaultStringPieceExtension)); + assertEqualsExactType("425", message.getExtension(UnittestProto.defaultCordExtension)); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are cleared, and that getting the extensions returns their + * default values. + */ + public static void assertExtensionsClear(TestAllExtensions message) { + // hasBlah() should initially be false for all optional fields. + Assert.assertFalse(message.hasExtension(UnittestProto.optionalInt32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalInt64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalUint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalUint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFixed32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFixed64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSfixed32Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalSfixed64Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalFloatExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalDoubleExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalBoolExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalStringExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalBytesExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalGroupExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalNestedMessageExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalForeignMessageExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalImportMessageExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalNestedEnumExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalForeignEnumExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalImportEnumExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.optionalStringPieceExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.optionalCordExtension)); + + // Optional fields without defaults are set to zero or something like it. + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalInt32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalInt64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalUint32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalUint64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalSint32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalSint64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalFixed32Extension )); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalFixed64Extension )); + assertEqualsExactType(0 , message.getExtension(UnittestProto.optionalSfixed32Extension)); + assertEqualsExactType(0L , message.getExtension(UnittestProto.optionalSfixed64Extension)); + assertEqualsExactType(0F , message.getExtension(UnittestProto.optionalFloatExtension )); + assertEqualsExactType(0D , message.getExtension(UnittestProto.optionalDoubleExtension )); + assertEqualsExactType(false, message.getExtension(UnittestProto.optionalBoolExtension )); + assertEqualsExactType("" , message.getExtension(UnittestProto.optionalStringExtension )); + assertEqualsExactType(ByteString.EMPTY, message.getExtension(UnittestProto.optionalBytesExtension)); + + // Embedded messages should also be clear. + Assert.assertFalse(message.getExtension(UnittestProto.optionalGroupExtension ).hasA()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalNestedMessageExtension ).hasBb()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalForeignMessageExtension).hasC()); + Assert.assertFalse(message.getExtension(UnittestProto.optionalImportMessageExtension ).hasD()); + + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalGroupExtension ).getA()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalNestedMessageExtension ).getBb()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalForeignMessageExtension).getC()); + assertEqualsExactType(0, message.getExtension(UnittestProto.optionalImportMessageExtension ).getD()); + + // Enums without defaults are set to the first value in the enum. + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.optionalNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.optionalForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.optionalImportEnumExtension)); + + assertEqualsExactType("", message.getExtension(UnittestProto.optionalStringPieceExtension)); + assertEqualsExactType("", message.getExtension(UnittestProto.optionalCordExtension)); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(0, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + // hasBlah() should also be false for all default fields. + Assert.assertFalse(message.hasExtension(UnittestProto.defaultInt32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultInt64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultUint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultUint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSint32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSint64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFixed32Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFixed64Extension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSfixed32Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultSfixed64Extension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultFloatExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultDoubleExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultBoolExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultStringExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultBytesExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.defaultNestedEnumExtension )); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultForeignEnumExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultImportEnumExtension )); + + Assert.assertFalse(message.hasExtension(UnittestProto.defaultStringPieceExtension)); + Assert.assertFalse(message.hasExtension(UnittestProto.defaultCordExtension)); + + // Fields with defaults have their default values (duh). + assertEqualsExactType( 41 , message.getExtension(UnittestProto.defaultInt32Extension )); + assertEqualsExactType( 42L , message.getExtension(UnittestProto.defaultInt64Extension )); + assertEqualsExactType( 43 , message.getExtension(UnittestProto.defaultUint32Extension )); + assertEqualsExactType( 44L , message.getExtension(UnittestProto.defaultUint64Extension )); + assertEqualsExactType(-45 , message.getExtension(UnittestProto.defaultSint32Extension )); + assertEqualsExactType( 46L , message.getExtension(UnittestProto.defaultSint64Extension )); + assertEqualsExactType( 47 , message.getExtension(UnittestProto.defaultFixed32Extension )); + assertEqualsExactType( 48L , message.getExtension(UnittestProto.defaultFixed64Extension )); + assertEqualsExactType( 49 , message.getExtension(UnittestProto.defaultSfixed32Extension)); + assertEqualsExactType(-50L , message.getExtension(UnittestProto.defaultSfixed64Extension)); + assertEqualsExactType( 51.5F , message.getExtension(UnittestProto.defaultFloatExtension )); + assertEqualsExactType( 52e3D , message.getExtension(UnittestProto.defaultDoubleExtension )); + assertEqualsExactType(true , message.getExtension(UnittestProto.defaultBoolExtension )); + assertEqualsExactType("hello", message.getExtension(UnittestProto.defaultStringExtension )); + assertEqualsExactType(toBytes("world"), message.getExtension(UnittestProto.defaultBytesExtension)); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.defaultNestedEnumExtension )); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.defaultForeignEnumExtension)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.defaultImportEnumExtension)); + + assertEqualsExactType("abc", message.getExtension(UnittestProto.defaultStringPieceExtension)); + assertEqualsExactType("123", message.getExtension(UnittestProto.defaultCordExtension)); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all extensions of + * {@code message} are set to the values assigned by {@code setAllExtensions} + * followed by {@code modifyRepeatedExtensions}. + */ + public static void assertRepeatedExtensionsModified( + TestAllExtensions message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedInt64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedUint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSint64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed32Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFixed64Extension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed32Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedSfixed64Extension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedFloatExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedDoubleExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBoolExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedBytesExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedGroupExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignMessageExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportMessageExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedNestedEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedForeignEnumExtension )); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedImportEnumExtension )); + + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedStringPieceExtension)); + Assert.assertEquals(2, message.getExtensionCount(UnittestProto.repeatedCordExtension)); + + assertEqualsExactType(201 , message.getExtension(UnittestProto.repeatedInt32Extension , 0)); + assertEqualsExactType(202L , message.getExtension(UnittestProto.repeatedInt64Extension , 0)); + assertEqualsExactType(203 , message.getExtension(UnittestProto.repeatedUint32Extension , 0)); + assertEqualsExactType(204L , message.getExtension(UnittestProto.repeatedUint64Extension , 0)); + assertEqualsExactType(205 , message.getExtension(UnittestProto.repeatedSint32Extension , 0)); + assertEqualsExactType(206L , message.getExtension(UnittestProto.repeatedSint64Extension , 0)); + assertEqualsExactType(207 , message.getExtension(UnittestProto.repeatedFixed32Extension , 0)); + assertEqualsExactType(208L , message.getExtension(UnittestProto.repeatedFixed64Extension , 0)); + assertEqualsExactType(209 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 0)); + assertEqualsExactType(210L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 0)); + assertEqualsExactType(211F , message.getExtension(UnittestProto.repeatedFloatExtension , 0)); + assertEqualsExactType(212D , message.getExtension(UnittestProto.repeatedDoubleExtension , 0)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 0)); + assertEqualsExactType("215", message.getExtension(UnittestProto.repeatedStringExtension , 0)); + assertEqualsExactType(toBytes("216"), message.getExtension(UnittestProto.repeatedBytesExtension, 0)); + + assertEqualsExactType(217, message.getExtension(UnittestProto.repeatedGroupExtension , 0).getA()); + assertEqualsExactType(218, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 0).getBb()); + assertEqualsExactType(219, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 0).getC()); + assertEqualsExactType(220, message.getExtension(UnittestProto.repeatedImportMessageExtension , 0).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 0)); + assertEqualsExactType(ForeignEnum.FOREIGN_BAR, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 0)); + assertEqualsExactType(ImportEnum.IMPORT_BAR, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 0)); + + assertEqualsExactType("224", message.getExtension(UnittestProto.repeatedStringPieceExtension, 0)); + assertEqualsExactType("225", message.getExtension(UnittestProto.repeatedCordExtension, 0)); + + // Actually verify the second (modified) elements now. + assertEqualsExactType(501 , message.getExtension(UnittestProto.repeatedInt32Extension , 1)); + assertEqualsExactType(502L , message.getExtension(UnittestProto.repeatedInt64Extension , 1)); + assertEqualsExactType(503 , message.getExtension(UnittestProto.repeatedUint32Extension , 1)); + assertEqualsExactType(504L , message.getExtension(UnittestProto.repeatedUint64Extension , 1)); + assertEqualsExactType(505 , message.getExtension(UnittestProto.repeatedSint32Extension , 1)); + assertEqualsExactType(506L , message.getExtension(UnittestProto.repeatedSint64Extension , 1)); + assertEqualsExactType(507 , message.getExtension(UnittestProto.repeatedFixed32Extension , 1)); + assertEqualsExactType(508L , message.getExtension(UnittestProto.repeatedFixed64Extension , 1)); + assertEqualsExactType(509 , message.getExtension(UnittestProto.repeatedSfixed32Extension, 1)); + assertEqualsExactType(510L , message.getExtension(UnittestProto.repeatedSfixed64Extension, 1)); + assertEqualsExactType(511F , message.getExtension(UnittestProto.repeatedFloatExtension , 1)); + assertEqualsExactType(512D , message.getExtension(UnittestProto.repeatedDoubleExtension , 1)); + assertEqualsExactType(true , message.getExtension(UnittestProto.repeatedBoolExtension , 1)); + assertEqualsExactType("515", message.getExtension(UnittestProto.repeatedStringExtension , 1)); + assertEqualsExactType(toBytes("516"), message.getExtension(UnittestProto.repeatedBytesExtension, 1)); + + assertEqualsExactType(517, message.getExtension(UnittestProto.repeatedGroupExtension , 1).getA()); + assertEqualsExactType(518, message.getExtension(UnittestProto.repeatedNestedMessageExtension , 1).getBb()); + assertEqualsExactType(519, message.getExtension(UnittestProto.repeatedForeignMessageExtension, 1).getC()); + assertEqualsExactType(520, message.getExtension(UnittestProto.repeatedImportMessageExtension , 1).getD()); + + assertEqualsExactType(TestAllTypes.NestedEnum.FOO, + message.getExtension(UnittestProto.repeatedNestedEnumExtension, 1)); + assertEqualsExactType(ForeignEnum.FOREIGN_FOO, + message.getExtension(UnittestProto.repeatedForeignEnumExtension, 1)); + assertEqualsExactType(ImportEnum.IMPORT_FOO, + message.getExtension(UnittestProto.repeatedImportEnumExtension, 1)); + + assertEqualsExactType("524", message.getExtension(UnittestProto.repeatedStringPieceExtension, 1)); + assertEqualsExactType("525", message.getExtension(UnittestProto.repeatedCordExtension, 1)); + } + + // =================================================================== + + /** + * Performs the same things that the methods of {@code TestUtil} do, but + * via the reflection interface. This is its own class because it needs + * to know what descriptor to use. + */ + public static class ReflectionTester { + private final Descriptors.Descriptor baseDescriptor; + private final ExtensionRegistry extensionRegistry; + + private final Descriptors.FileDescriptor file; + private final Descriptors.FileDescriptor importFile; + + private final Descriptors.Descriptor optionalGroup; + private final Descriptors.Descriptor repeatedGroup; + private final Descriptors.Descriptor nestedMessage; + private final Descriptors.Descriptor foreignMessage; + private final Descriptors.Descriptor importMessage; + + private final Descriptors.FieldDescriptor groupA; + private final Descriptors.FieldDescriptor repeatedGroupA; + private final Descriptors.FieldDescriptor nestedB; + private final Descriptors.FieldDescriptor foreignC; + private final Descriptors.FieldDescriptor importD; + + private final Descriptors.EnumDescriptor nestedEnum; + private final Descriptors.EnumDescriptor foreignEnum; + private final Descriptors.EnumDescriptor importEnum; + + private final Descriptors.EnumValueDescriptor nestedFoo; + private final Descriptors.EnumValueDescriptor nestedBar; + private final Descriptors.EnumValueDescriptor nestedBaz; + private final Descriptors.EnumValueDescriptor foreignFoo; + private final Descriptors.EnumValueDescriptor foreignBar; + private final Descriptors.EnumValueDescriptor foreignBaz; + private final Descriptors.EnumValueDescriptor importFoo; + private final Descriptors.EnumValueDescriptor importBar; + private final Descriptors.EnumValueDescriptor importBaz; + + /** + * Construct a {@code ReflectionTester} that will expect messages using + * the given descriptor. + * + * Normally {@code baseDescriptor} should be a descriptor for the type + * {@code TestAllTypes}, defined in + * {@code google/protobuf/unittest.proto}. However, if + * {@code extensionRegistry} is non-null, then {@code baseDescriptor} should + * be for {@code TestAllExtensions} instead, and instead of reading and + * writing normal fields, the tester will read and write extensions. + * All of {@code TestAllExtensions}' extensions must be registered in the + * registry. + */ + public ReflectionTester(Descriptors.Descriptor baseDescriptor, + ExtensionRegistry extensionRegistry) { + this.baseDescriptor = baseDescriptor; + this.extensionRegistry = extensionRegistry; + + this.file = baseDescriptor.getFile(); + Assert.assertEquals(1, file.getDependencies().size()); + this.importFile = file.getDependencies().get(0); + + Descriptors.Descriptor testAllTypes; + if (extensionRegistry == null) { + testAllTypes = baseDescriptor; + } else { + testAllTypes = file.findMessageTypeByName("TestAllTypes"); + Assert.assertNotNull(testAllTypes); + } + + if (extensionRegistry == null) { + this.optionalGroup = + baseDescriptor.findNestedTypeByName("OptionalGroup"); + this.repeatedGroup = + baseDescriptor.findNestedTypeByName("RepeatedGroup"); + } else { + this.optionalGroup = + file.findMessageTypeByName("OptionalGroup_extension"); + this.repeatedGroup = + file.findMessageTypeByName("RepeatedGroup_extension"); + } + this.nestedMessage = testAllTypes.findNestedTypeByName("NestedMessage"); + this.foreignMessage = file.findMessageTypeByName("ForeignMessage"); + this.importMessage = importFile.findMessageTypeByName("ImportMessage"); + + this.nestedEnum = testAllTypes.findEnumTypeByName("NestedEnum"); + this.foreignEnum = file.findEnumTypeByName("ForeignEnum"); + this.importEnum = importFile.findEnumTypeByName("ImportEnum"); + + Assert.assertNotNull(optionalGroup ); + Assert.assertNotNull(repeatedGroup ); + Assert.assertNotNull(nestedMessage ); + Assert.assertNotNull(foreignMessage); + Assert.assertNotNull(importMessage ); + Assert.assertNotNull(nestedEnum ); + Assert.assertNotNull(foreignEnum ); + Assert.assertNotNull(importEnum ); + + this.nestedB = nestedMessage .findFieldByName("bb"); + this.foreignC = foreignMessage.findFieldByName("c"); + this.importD = importMessage .findFieldByName("d"); + this.nestedFoo = nestedEnum.findValueByName("FOO"); + this.nestedBar = nestedEnum.findValueByName("BAR"); + this.nestedBaz = nestedEnum.findValueByName("BAZ"); + this.foreignFoo = foreignEnum.findValueByName("FOREIGN_FOO"); + this.foreignBar = foreignEnum.findValueByName("FOREIGN_BAR"); + this.foreignBaz = foreignEnum.findValueByName("FOREIGN_BAZ"); + this.importFoo = importEnum.findValueByName("IMPORT_FOO"); + this.importBar = importEnum.findValueByName("IMPORT_BAR"); + this.importBaz = importEnum.findValueByName("IMPORT_BAZ"); + + this.groupA = optionalGroup.findFieldByName("a"); + this.repeatedGroupA = repeatedGroup.findFieldByName("a"); + + Assert.assertNotNull(groupA ); + Assert.assertNotNull(repeatedGroupA); + Assert.assertNotNull(nestedB ); + Assert.assertNotNull(foreignC ); + Assert.assertNotNull(importD ); + Assert.assertNotNull(nestedFoo ); + Assert.assertNotNull(nestedBar ); + Assert.assertNotNull(nestedBaz ); + Assert.assertNotNull(foreignFoo ); + Assert.assertNotNull(foreignBar ); + Assert.assertNotNull(foreignBaz ); + Assert.assertNotNull(importFoo ); + Assert.assertNotNull(importBar ); + Assert.assertNotNull(importBaz ); + } + + /** + * Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. + */ + private Descriptors.FieldDescriptor f(String name) { + Descriptors.FieldDescriptor result; + if (extensionRegistry == null) { + result = baseDescriptor.findFieldByName(name); + } else { + result = file.findExtensionByName(name + "_extension"); + } + Assert.assertNotNull(result); + return result; + } + + /** + * Calls {@code parent.newBuilderForField()} or uses the + * {@code ExtensionRegistry} to find an appropriate builder, depending + * on what type is being tested. + */ + private Message.Builder newBuilderForField( + Message.Builder parent, Descriptors.FieldDescriptor field) { + if (extensionRegistry == null) { + return parent.newBuilderForField(field); + } else { + ExtensionRegistry.ExtensionInfo extension = + extensionRegistry.findExtensionByNumber(field.getContainingType(), + field.getNumber()); + Assert.assertNotNull(extension); + Assert.assertNotNull(extension.defaultInstance); + return extension.defaultInstance.newBuilderForType(); + } + } + + // ------------------------------------------------------------------- + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllFieldsSet()}, using the {@link Message.Builder} + * reflection interface. + */ + void setAllFieldsViaReflection(Message.Builder message) { + message.setField(f("optional_int32" ), 101 ); + message.setField(f("optional_int64" ), 102L); + message.setField(f("optional_uint32" ), 103 ); + message.setField(f("optional_uint64" ), 104L); + message.setField(f("optional_sint32" ), 105 ); + message.setField(f("optional_sint64" ), 106L); + message.setField(f("optional_fixed32" ), 107 ); + message.setField(f("optional_fixed64" ), 108L); + message.setField(f("optional_sfixed32"), 109 ); + message.setField(f("optional_sfixed64"), 110L); + message.setField(f("optional_float" ), 111F); + message.setField(f("optional_double" ), 112D); + message.setField(f("optional_bool" ), true); + message.setField(f("optional_string" ), "115"); + message.setField(f("optional_bytes" ), toBytes("116")); + + message.setField(f("optionalgroup"), + newBuilderForField(message, f("optionalgroup")) + .setField(groupA, 117).build()); + message.setField(f("optional_nested_message"), + newBuilderForField(message, f("optional_nested_message")) + .setField(nestedB, 118).build()); + message.setField(f("optional_foreign_message"), + newBuilderForField(message, f("optional_foreign_message")) + .setField(foreignC, 119).build()); + message.setField(f("optional_import_message"), + newBuilderForField(message, f("optional_import_message")) + .setField(importD, 120).build()); + + message.setField(f("optional_nested_enum" ), nestedBaz); + message.setField(f("optional_foreign_enum"), foreignBaz); + message.setField(f("optional_import_enum" ), importBaz); + + message.setField(f("optional_string_piece" ), "124"); + message.setField(f("optional_cord" ), "125"); + + // ----------------------------------------------------------------- + + message.addRepeatedField(f("repeated_int32" ), 201 ); + message.addRepeatedField(f("repeated_int64" ), 202L); + message.addRepeatedField(f("repeated_uint32" ), 203 ); + message.addRepeatedField(f("repeated_uint64" ), 204L); + message.addRepeatedField(f("repeated_sint32" ), 205 ); + message.addRepeatedField(f("repeated_sint64" ), 206L); + message.addRepeatedField(f("repeated_fixed32" ), 207 ); + message.addRepeatedField(f("repeated_fixed64" ), 208L); + message.addRepeatedField(f("repeated_sfixed32"), 209 ); + message.addRepeatedField(f("repeated_sfixed64"), 210L); + message.addRepeatedField(f("repeated_float" ), 211F); + message.addRepeatedField(f("repeated_double" ), 212D); + message.addRepeatedField(f("repeated_bool" ), true); + message.addRepeatedField(f("repeated_string" ), "215"); + message.addRepeatedField(f("repeated_bytes" ), toBytes("216")); + + message.addRepeatedField(f("repeatedgroup"), + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 217).build()); + message.addRepeatedField(f("repeated_nested_message"), + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 218).build()); + message.addRepeatedField(f("repeated_foreign_message"), + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 219).build()); + message.addRepeatedField(f("repeated_import_message"), + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 220).build()); + + message.addRepeatedField(f("repeated_nested_enum" ), nestedBar); + message.addRepeatedField(f("repeated_foreign_enum"), foreignBar); + message.addRepeatedField(f("repeated_import_enum" ), importBar); + + message.addRepeatedField(f("repeated_string_piece" ), "224"); + message.addRepeatedField(f("repeated_cord" ), "225"); + + // Add a second one of each field. + message.addRepeatedField(f("repeated_int32" ), 301 ); + message.addRepeatedField(f("repeated_int64" ), 302L); + message.addRepeatedField(f("repeated_uint32" ), 303 ); + message.addRepeatedField(f("repeated_uint64" ), 304L); + message.addRepeatedField(f("repeated_sint32" ), 305 ); + message.addRepeatedField(f("repeated_sint64" ), 306L); + message.addRepeatedField(f("repeated_fixed32" ), 307 ); + message.addRepeatedField(f("repeated_fixed64" ), 308L); + message.addRepeatedField(f("repeated_sfixed32"), 309 ); + message.addRepeatedField(f("repeated_sfixed64"), 310L); + message.addRepeatedField(f("repeated_float" ), 311F); + message.addRepeatedField(f("repeated_double" ), 312D); + message.addRepeatedField(f("repeated_bool" ), false); + message.addRepeatedField(f("repeated_string" ), "315"); + message.addRepeatedField(f("repeated_bytes" ), toBytes("316")); + + message.addRepeatedField(f("repeatedgroup"), + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 317).build()); + message.addRepeatedField(f("repeated_nested_message"), + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 318).build()); + message.addRepeatedField(f("repeated_foreign_message"), + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 319).build()); + message.addRepeatedField(f("repeated_import_message"), + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 320).build()); + + message.addRepeatedField(f("repeated_nested_enum" ), nestedBaz); + message.addRepeatedField(f("repeated_foreign_enum"), foreignBaz); + message.addRepeatedField(f("repeated_import_enum" ), importBaz); + + message.addRepeatedField(f("repeated_string_piece" ), "324"); + message.addRepeatedField(f("repeated_cord" ), "325"); + + // ----------------------------------------------------------------- + + message.setField(f("default_int32" ), 401 ); + message.setField(f("default_int64" ), 402L); + message.setField(f("default_uint32" ), 403 ); + message.setField(f("default_uint64" ), 404L); + message.setField(f("default_sint32" ), 405 ); + message.setField(f("default_sint64" ), 406L); + message.setField(f("default_fixed32" ), 407 ); + message.setField(f("default_fixed64" ), 408L); + message.setField(f("default_sfixed32"), 409 ); + message.setField(f("default_sfixed64"), 410L); + message.setField(f("default_float" ), 411F); + message.setField(f("default_double" ), 412D); + message.setField(f("default_bool" ), false); + message.setField(f("default_string" ), "415"); + message.setField(f("default_bytes" ), toBytes("416")); + + message.setField(f("default_nested_enum" ), nestedFoo); + message.setField(f("default_foreign_enum"), foreignFoo); + message.setField(f("default_import_enum" ), importFoo); + + message.setField(f("default_string_piece" ), "424"); + message.setField(f("default_cord" ), "425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated fields of {@code message} to contain the values + * expected by {@code assertRepeatedFieldsModified()}, using the + * {@link Message.Builder} reflection interface. + */ + void modifyRepeatedFieldsViaReflection(Message.Builder message) { + message.setRepeatedField(f("repeated_int32" ), 1, 501 ); + message.setRepeatedField(f("repeated_int64" ), 1, 502L); + message.setRepeatedField(f("repeated_uint32" ), 1, 503 ); + message.setRepeatedField(f("repeated_uint64" ), 1, 504L); + message.setRepeatedField(f("repeated_sint32" ), 1, 505 ); + message.setRepeatedField(f("repeated_sint64" ), 1, 506L); + message.setRepeatedField(f("repeated_fixed32" ), 1, 507 ); + message.setRepeatedField(f("repeated_fixed64" ), 1, 508L); + message.setRepeatedField(f("repeated_sfixed32"), 1, 509 ); + message.setRepeatedField(f("repeated_sfixed64"), 1, 510L); + message.setRepeatedField(f("repeated_float" ), 1, 511F); + message.setRepeatedField(f("repeated_double" ), 1, 512D); + message.setRepeatedField(f("repeated_bool" ), 1, true); + message.setRepeatedField(f("repeated_string" ), 1, "515"); + message.setRepeatedField(f("repeated_bytes" ), 1, toBytes("516")); + + message.setRepeatedField(f("repeatedgroup"), 1, + newBuilderForField(message, f("repeatedgroup")) + .setField(repeatedGroupA, 517).build()); + message.setRepeatedField(f("repeated_nested_message"), 1, + newBuilderForField(message, f("repeated_nested_message")) + .setField(nestedB, 518).build()); + message.setRepeatedField(f("repeated_foreign_message"), 1, + newBuilderForField(message, f("repeated_foreign_message")) + .setField(foreignC, 519).build()); + message.setRepeatedField(f("repeated_import_message"), 1, + newBuilderForField(message, f("repeated_import_message")) + .setField(importD, 520).build()); + + message.setRepeatedField(f("repeated_nested_enum" ), 1, nestedFoo); + message.setRepeatedField(f("repeated_foreign_enum"), 1, foreignFoo); + message.setRepeatedField(f("repeated_import_enum" ), 1, importFoo); + + message.setRepeatedField(f("repeated_string_piece"), 1, "524"); + message.setRepeatedField(f("repeated_cord"), 1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields}, + * using the {@link Message} reflection interface. + */ + public void assertAllFieldsSetViaReflection(Message message) { + Assert.assertTrue(message.hasField(f("optional_int32" ))); + Assert.assertTrue(message.hasField(f("optional_int64" ))); + Assert.assertTrue(message.hasField(f("optional_uint32" ))); + Assert.assertTrue(message.hasField(f("optional_uint64" ))); + Assert.assertTrue(message.hasField(f("optional_sint32" ))); + Assert.assertTrue(message.hasField(f("optional_sint64" ))); + Assert.assertTrue(message.hasField(f("optional_fixed32" ))); + Assert.assertTrue(message.hasField(f("optional_fixed64" ))); + Assert.assertTrue(message.hasField(f("optional_sfixed32"))); + Assert.assertTrue(message.hasField(f("optional_sfixed64"))); + Assert.assertTrue(message.hasField(f("optional_float" ))); + Assert.assertTrue(message.hasField(f("optional_double" ))); + Assert.assertTrue(message.hasField(f("optional_bool" ))); + Assert.assertTrue(message.hasField(f("optional_string" ))); + Assert.assertTrue(message.hasField(f("optional_bytes" ))); + + Assert.assertTrue(message.hasField(f("optionalgroup" ))); + Assert.assertTrue(message.hasField(f("optional_nested_message" ))); + Assert.assertTrue(message.hasField(f("optional_foreign_message"))); + Assert.assertTrue(message.hasField(f("optional_import_message" ))); + + Assert.assertTrue( + ((Message)message.getField(f("optionalgroup"))).hasField(groupA)); + Assert.assertTrue( + ((Message)message.getField(f("optional_nested_message"))) + .hasField(nestedB)); + Assert.assertTrue( + ((Message)message.getField(f("optional_foreign_message"))) + .hasField(foreignC)); + Assert.assertTrue( + ((Message)message.getField(f("optional_import_message"))) + .hasField(importD)); + + Assert.assertTrue(message.hasField(f("optional_nested_enum" ))); + Assert.assertTrue(message.hasField(f("optional_foreign_enum"))); + Assert.assertTrue(message.hasField(f("optional_import_enum" ))); + + Assert.assertTrue(message.hasField(f("optional_string_piece"))); + Assert.assertTrue(message.hasField(f("optional_cord"))); + + Assert.assertEquals(101 , message.getField(f("optional_int32" ))); + Assert.assertEquals(102L , message.getField(f("optional_int64" ))); + Assert.assertEquals(103 , message.getField(f("optional_uint32" ))); + Assert.assertEquals(104L , message.getField(f("optional_uint64" ))); + Assert.assertEquals(105 , message.getField(f("optional_sint32" ))); + Assert.assertEquals(106L , message.getField(f("optional_sint64" ))); + Assert.assertEquals(107 , message.getField(f("optional_fixed32" ))); + Assert.assertEquals(108L , message.getField(f("optional_fixed64" ))); + Assert.assertEquals(109 , message.getField(f("optional_sfixed32"))); + Assert.assertEquals(110L , message.getField(f("optional_sfixed64"))); + Assert.assertEquals(111F , message.getField(f("optional_float" ))); + Assert.assertEquals(112D , message.getField(f("optional_double" ))); + Assert.assertEquals(true , message.getField(f("optional_bool" ))); + Assert.assertEquals("115", message.getField(f("optional_string" ))); + Assert.assertEquals(toBytes("116"), message.getField(f("optional_bytes"))); + + Assert.assertEquals(117, + ((Message)message.getField(f("optionalgroup"))).getField(groupA)); + Assert.assertEquals(118, + ((Message)message.getField(f("optional_nested_message"))) + .getField(nestedB)); + Assert.assertEquals(119, + ((Message)message.getField(f("optional_foreign_message"))) + .getField(foreignC)); + Assert.assertEquals(120, + ((Message)message.getField(f("optional_import_message"))) + .getField(importD)); + + Assert.assertEquals( nestedBaz, message.getField(f("optional_nested_enum" ))); + Assert.assertEquals(foreignBaz, message.getField(f("optional_foreign_enum"))); + Assert.assertEquals( importBaz, message.getField(f("optional_import_enum" ))); + + Assert.assertEquals("124", message.getField(f("optional_string_piece"))); + Assert.assertEquals("125", message.getField(f("optional_cord"))); + + // ----------------------------------------------------------------- + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_cord"))); + + Assert.assertEquals(201 , message.getRepeatedField(f("repeated_int32" ), 0)); + Assert.assertEquals(202L , message.getRepeatedField(f("repeated_int64" ), 0)); + Assert.assertEquals(203 , message.getRepeatedField(f("repeated_uint32" ), 0)); + Assert.assertEquals(204L , message.getRepeatedField(f("repeated_uint64" ), 0)); + Assert.assertEquals(205 , message.getRepeatedField(f("repeated_sint32" ), 0)); + Assert.assertEquals(206L , message.getRepeatedField(f("repeated_sint64" ), 0)); + Assert.assertEquals(207 , message.getRepeatedField(f("repeated_fixed32" ), 0)); + Assert.assertEquals(208L , message.getRepeatedField(f("repeated_fixed64" ), 0)); + Assert.assertEquals(209 , message.getRepeatedField(f("repeated_sfixed32"), 0)); + Assert.assertEquals(210L , message.getRepeatedField(f("repeated_sfixed64"), 0)); + Assert.assertEquals(211F , message.getRepeatedField(f("repeated_float" ), 0)); + Assert.assertEquals(212D , message.getRepeatedField(f("repeated_double" ), 0)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 0)); + Assert.assertEquals("215", message.getRepeatedField(f("repeated_string" ), 0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedField(f("repeated_bytes"), 0)); + + Assert.assertEquals(217, + ((Message)message.getRepeatedField(f("repeatedgroup"), 0)) + .getField(repeatedGroupA)); + Assert.assertEquals(218, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 0)) + .getField(nestedB)); + Assert.assertEquals(219, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 0)) + .getField(foreignC)); + Assert.assertEquals(220, + ((Message)message.getRepeatedField(f("repeated_import_message"), 0)) + .getField(importD)); + + Assert.assertEquals( nestedBar, message.getRepeatedField(f("repeated_nested_enum" ),0)); + Assert.assertEquals(foreignBar, message.getRepeatedField(f("repeated_foreign_enum"),0)); + Assert.assertEquals( importBar, message.getRepeatedField(f("repeated_import_enum" ),0)); + + Assert.assertEquals("224", message.getRepeatedField(f("repeated_string_piece"), 0)); + Assert.assertEquals("225", message.getRepeatedField(f("repeated_cord"), 0)); + + Assert.assertEquals(301 , message.getRepeatedField(f("repeated_int32" ), 1)); + Assert.assertEquals(302L , message.getRepeatedField(f("repeated_int64" ), 1)); + Assert.assertEquals(303 , message.getRepeatedField(f("repeated_uint32" ), 1)); + Assert.assertEquals(304L , message.getRepeatedField(f("repeated_uint64" ), 1)); + Assert.assertEquals(305 , message.getRepeatedField(f("repeated_sint32" ), 1)); + Assert.assertEquals(306L , message.getRepeatedField(f("repeated_sint64" ), 1)); + Assert.assertEquals(307 , message.getRepeatedField(f("repeated_fixed32" ), 1)); + Assert.assertEquals(308L , message.getRepeatedField(f("repeated_fixed64" ), 1)); + Assert.assertEquals(309 , message.getRepeatedField(f("repeated_sfixed32"), 1)); + Assert.assertEquals(310L , message.getRepeatedField(f("repeated_sfixed64"), 1)); + Assert.assertEquals(311F , message.getRepeatedField(f("repeated_float" ), 1)); + Assert.assertEquals(312D , message.getRepeatedField(f("repeated_double" ), 1)); + Assert.assertEquals(false, message.getRepeatedField(f("repeated_bool" ), 1)); + Assert.assertEquals("315", message.getRepeatedField(f("repeated_string" ), 1)); + Assert.assertEquals(toBytes("316"), message.getRepeatedField(f("repeated_bytes"), 1)); + + Assert.assertEquals(317, + ((Message)message.getRepeatedField(f("repeatedgroup"), 1)) + .getField(repeatedGroupA)); + Assert.assertEquals(318, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 1)) + .getField(nestedB)); + Assert.assertEquals(319, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 1)) + .getField(foreignC)); + Assert.assertEquals(320, + ((Message)message.getRepeatedField(f("repeated_import_message"), 1)) + .getField(importD)); + + Assert.assertEquals( nestedBaz, message.getRepeatedField(f("repeated_nested_enum" ),1)); + Assert.assertEquals(foreignBaz, message.getRepeatedField(f("repeated_foreign_enum"),1)); + Assert.assertEquals( importBaz, message.getRepeatedField(f("repeated_import_enum" ),1)); + + Assert.assertEquals("324", message.getRepeatedField(f("repeated_string_piece"), 1)); + Assert.assertEquals("325", message.getRepeatedField(f("repeated_cord"), 1)); + + // ----------------------------------------------------------------- + + Assert.assertTrue(message.hasField(f("default_int32" ))); + Assert.assertTrue(message.hasField(f("default_int64" ))); + Assert.assertTrue(message.hasField(f("default_uint32" ))); + Assert.assertTrue(message.hasField(f("default_uint64" ))); + Assert.assertTrue(message.hasField(f("default_sint32" ))); + Assert.assertTrue(message.hasField(f("default_sint64" ))); + Assert.assertTrue(message.hasField(f("default_fixed32" ))); + Assert.assertTrue(message.hasField(f("default_fixed64" ))); + Assert.assertTrue(message.hasField(f("default_sfixed32"))); + Assert.assertTrue(message.hasField(f("default_sfixed64"))); + Assert.assertTrue(message.hasField(f("default_float" ))); + Assert.assertTrue(message.hasField(f("default_double" ))); + Assert.assertTrue(message.hasField(f("default_bool" ))); + Assert.assertTrue(message.hasField(f("default_string" ))); + Assert.assertTrue(message.hasField(f("default_bytes" ))); + + Assert.assertTrue(message.hasField(f("default_nested_enum" ))); + Assert.assertTrue(message.hasField(f("default_foreign_enum"))); + Assert.assertTrue(message.hasField(f("default_import_enum" ))); + + Assert.assertTrue(message.hasField(f("default_string_piece"))); + Assert.assertTrue(message.hasField(f("default_cord"))); + + Assert.assertEquals(401 , message.getField(f("default_int32" ))); + Assert.assertEquals(402L , message.getField(f("default_int64" ))); + Assert.assertEquals(403 , message.getField(f("default_uint32" ))); + Assert.assertEquals(404L , message.getField(f("default_uint64" ))); + Assert.assertEquals(405 , message.getField(f("default_sint32" ))); + Assert.assertEquals(406L , message.getField(f("default_sint64" ))); + Assert.assertEquals(407 , message.getField(f("default_fixed32" ))); + Assert.assertEquals(408L , message.getField(f("default_fixed64" ))); + Assert.assertEquals(409 , message.getField(f("default_sfixed32"))); + Assert.assertEquals(410L , message.getField(f("default_sfixed64"))); + Assert.assertEquals(411F , message.getField(f("default_float" ))); + Assert.assertEquals(412D , message.getField(f("default_double" ))); + Assert.assertEquals(false, message.getField(f("default_bool" ))); + Assert.assertEquals("415", message.getField(f("default_string" ))); + Assert.assertEquals(toBytes("416"), message.getField(f("default_bytes"))); + + Assert.assertEquals( nestedFoo, message.getField(f("default_nested_enum" ))); + Assert.assertEquals(foreignFoo, message.getField(f("default_foreign_enum"))); + Assert.assertEquals( importFoo, message.getField(f("default_import_enum" ))); + + Assert.assertEquals("424", message.getField(f("default_string_piece"))); + Assert.assertEquals("425", message.getField(f("default_cord"))); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are cleared, and that getting the fields returns their + * default values, using the {@link Message} reflection interface. + */ + public void assertClearViaReflection(Message message) { + // has_blah() should initially be false for all optional fields. + Assert.assertFalse(message.hasField(f("optional_int32" ))); + Assert.assertFalse(message.hasField(f("optional_int64" ))); + Assert.assertFalse(message.hasField(f("optional_uint32" ))); + Assert.assertFalse(message.hasField(f("optional_uint64" ))); + Assert.assertFalse(message.hasField(f("optional_sint32" ))); + Assert.assertFalse(message.hasField(f("optional_sint64" ))); + Assert.assertFalse(message.hasField(f("optional_fixed32" ))); + Assert.assertFalse(message.hasField(f("optional_fixed64" ))); + Assert.assertFalse(message.hasField(f("optional_sfixed32"))); + Assert.assertFalse(message.hasField(f("optional_sfixed64"))); + Assert.assertFalse(message.hasField(f("optional_float" ))); + Assert.assertFalse(message.hasField(f("optional_double" ))); + Assert.assertFalse(message.hasField(f("optional_bool" ))); + Assert.assertFalse(message.hasField(f("optional_string" ))); + Assert.assertFalse(message.hasField(f("optional_bytes" ))); + + Assert.assertFalse(message.hasField(f("optionalgroup" ))); + Assert.assertFalse(message.hasField(f("optional_nested_message" ))); + Assert.assertFalse(message.hasField(f("optional_foreign_message"))); + Assert.assertFalse(message.hasField(f("optional_import_message" ))); + + Assert.assertFalse(message.hasField(f("optional_nested_enum" ))); + Assert.assertFalse(message.hasField(f("optional_foreign_enum"))); + Assert.assertFalse(message.hasField(f("optional_import_enum" ))); + + Assert.assertFalse(message.hasField(f("optional_string_piece"))); + Assert.assertFalse(message.hasField(f("optional_cord"))); + + // Optional fields without defaults are set to zero or something like it. + Assert.assertEquals(0 , message.getField(f("optional_int32" ))); + Assert.assertEquals(0L , message.getField(f("optional_int64" ))); + Assert.assertEquals(0 , message.getField(f("optional_uint32" ))); + Assert.assertEquals(0L , message.getField(f("optional_uint64" ))); + Assert.assertEquals(0 , message.getField(f("optional_sint32" ))); + Assert.assertEquals(0L , message.getField(f("optional_sint64" ))); + Assert.assertEquals(0 , message.getField(f("optional_fixed32" ))); + Assert.assertEquals(0L , message.getField(f("optional_fixed64" ))); + Assert.assertEquals(0 , message.getField(f("optional_sfixed32"))); + Assert.assertEquals(0L , message.getField(f("optional_sfixed64"))); + Assert.assertEquals(0F , message.getField(f("optional_float" ))); + Assert.assertEquals(0D , message.getField(f("optional_double" ))); + Assert.assertEquals(false, message.getField(f("optional_bool" ))); + Assert.assertEquals("" , message.getField(f("optional_string" ))); + Assert.assertEquals(ByteString.EMPTY, message.getField(f("optional_bytes"))); + + // Embedded messages should also be clear. + Assert.assertFalse( + ((Message)message.getField(f("optionalgroup"))).hasField(groupA)); + Assert.assertFalse( + ((Message)message.getField(f("optional_nested_message"))) + .hasField(nestedB)); + Assert.assertFalse( + ((Message)message.getField(f("optional_foreign_message"))) + .hasField(foreignC)); + Assert.assertFalse( + ((Message)message.getField(f("optional_import_message"))) + .hasField(importD)); + + Assert.assertEquals(0, + ((Message)message.getField(f("optionalgroup"))).getField(groupA)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_nested_message"))) + .getField(nestedB)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_foreign_message"))) + .getField(foreignC)); + Assert.assertEquals(0, + ((Message)message.getField(f("optional_import_message"))) + .getField(importD)); + + // Enums without defaults are set to the first value in the enum. + Assert.assertEquals( nestedFoo, message.getField(f("optional_nested_enum" ))); + Assert.assertEquals(foreignFoo, message.getField(f("optional_foreign_enum"))); + Assert.assertEquals( importFoo, message.getField(f("optional_import_enum" ))); + + Assert.assertEquals("", message.getField(f("optional_string_piece"))); + Assert.assertEquals("", message.getField(f("optional_cord"))); + + // Repeated fields are empty. + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(0, message.getRepeatedFieldCount(f("repeated_cord"))); + + // has_blah() should also be false for all default fields. + Assert.assertFalse(message.hasField(f("default_int32" ))); + Assert.assertFalse(message.hasField(f("default_int64" ))); + Assert.assertFalse(message.hasField(f("default_uint32" ))); + Assert.assertFalse(message.hasField(f("default_uint64" ))); + Assert.assertFalse(message.hasField(f("default_sint32" ))); + Assert.assertFalse(message.hasField(f("default_sint64" ))); + Assert.assertFalse(message.hasField(f("default_fixed32" ))); + Assert.assertFalse(message.hasField(f("default_fixed64" ))); + Assert.assertFalse(message.hasField(f("default_sfixed32"))); + Assert.assertFalse(message.hasField(f("default_sfixed64"))); + Assert.assertFalse(message.hasField(f("default_float" ))); + Assert.assertFalse(message.hasField(f("default_double" ))); + Assert.assertFalse(message.hasField(f("default_bool" ))); + Assert.assertFalse(message.hasField(f("default_string" ))); + Assert.assertFalse(message.hasField(f("default_bytes" ))); + + Assert.assertFalse(message.hasField(f("default_nested_enum" ))); + Assert.assertFalse(message.hasField(f("default_foreign_enum"))); + Assert.assertFalse(message.hasField(f("default_import_enum" ))); + + Assert.assertFalse(message.hasField(f("default_string_piece" ))); + Assert.assertFalse(message.hasField(f("default_cord" ))); + + // Fields with defaults have their default values (duh). + Assert.assertEquals( 41 , message.getField(f("default_int32" ))); + Assert.assertEquals( 42L , message.getField(f("default_int64" ))); + Assert.assertEquals( 43 , message.getField(f("default_uint32" ))); + Assert.assertEquals( 44L , message.getField(f("default_uint64" ))); + Assert.assertEquals(-45 , message.getField(f("default_sint32" ))); + Assert.assertEquals( 46L , message.getField(f("default_sint64" ))); + Assert.assertEquals( 47 , message.getField(f("default_fixed32" ))); + Assert.assertEquals( 48L , message.getField(f("default_fixed64" ))); + Assert.assertEquals( 49 , message.getField(f("default_sfixed32"))); + Assert.assertEquals(-50L , message.getField(f("default_sfixed64"))); + Assert.assertEquals( 51.5F , message.getField(f("default_float" ))); + Assert.assertEquals( 52e3D , message.getField(f("default_double" ))); + Assert.assertEquals(true , message.getField(f("default_bool" ))); + Assert.assertEquals("hello", message.getField(f("default_string" ))); + Assert.assertEquals(toBytes("world"), message.getField(f("default_bytes"))); + + Assert.assertEquals( nestedBar, message.getField(f("default_nested_enum" ))); + Assert.assertEquals(foreignBar, message.getField(f("default_foreign_enum"))); + Assert.assertEquals( importBar, message.getField(f("default_import_enum" ))); + + Assert.assertEquals("abc", message.getField(f("default_string_piece"))); + Assert.assertEquals("123", message.getField(f("default_cord"))); + } + + // --------------------------------------------------------------- + + public void assertRepeatedFieldsModifiedViaReflection(Message message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_int64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_uint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sint64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed32" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_fixed64" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed32"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_sfixed64"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_float" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_double" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bool" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_bytes" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeatedgroup" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_message"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_message" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_nested_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_foreign_enum" ))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_import_enum" ))); + + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_string_piece"))); + Assert.assertEquals(2, message.getRepeatedFieldCount(f("repeated_cord"))); + + Assert.assertEquals(201 , message.getRepeatedField(f("repeated_int32" ), 0)); + Assert.assertEquals(202L , message.getRepeatedField(f("repeated_int64" ), 0)); + Assert.assertEquals(203 , message.getRepeatedField(f("repeated_uint32" ), 0)); + Assert.assertEquals(204L , message.getRepeatedField(f("repeated_uint64" ), 0)); + Assert.assertEquals(205 , message.getRepeatedField(f("repeated_sint32" ), 0)); + Assert.assertEquals(206L , message.getRepeatedField(f("repeated_sint64" ), 0)); + Assert.assertEquals(207 , message.getRepeatedField(f("repeated_fixed32" ), 0)); + Assert.assertEquals(208L , message.getRepeatedField(f("repeated_fixed64" ), 0)); + Assert.assertEquals(209 , message.getRepeatedField(f("repeated_sfixed32"), 0)); + Assert.assertEquals(210L , message.getRepeatedField(f("repeated_sfixed64"), 0)); + Assert.assertEquals(211F , message.getRepeatedField(f("repeated_float" ), 0)); + Assert.assertEquals(212D , message.getRepeatedField(f("repeated_double" ), 0)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 0)); + Assert.assertEquals("215", message.getRepeatedField(f("repeated_string" ), 0)); + Assert.assertEquals(toBytes("216"), message.getRepeatedField(f("repeated_bytes"), 0)); + + Assert.assertEquals(217, + ((Message)message.getRepeatedField(f("repeatedgroup"), 0)) + .getField(repeatedGroupA)); + Assert.assertEquals(218, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 0)) + .getField(nestedB)); + Assert.assertEquals(219, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 0)) + .getField(foreignC)); + Assert.assertEquals(220, + ((Message)message.getRepeatedField(f("repeated_import_message"), 0)) + .getField(importD)); + + Assert.assertEquals( nestedBar, message.getRepeatedField(f("repeated_nested_enum" ),0)); + Assert.assertEquals(foreignBar, message.getRepeatedField(f("repeated_foreign_enum"),0)); + Assert.assertEquals( importBar, message.getRepeatedField(f("repeated_import_enum" ),0)); + + Assert.assertEquals("224", message.getRepeatedField(f("repeated_string_piece"), 0)); + Assert.assertEquals("225", message.getRepeatedField(f("repeated_cord"), 0)); + + Assert.assertEquals(501 , message.getRepeatedField(f("repeated_int32" ), 1)); + Assert.assertEquals(502L , message.getRepeatedField(f("repeated_int64" ), 1)); + Assert.assertEquals(503 , message.getRepeatedField(f("repeated_uint32" ), 1)); + Assert.assertEquals(504L , message.getRepeatedField(f("repeated_uint64" ), 1)); + Assert.assertEquals(505 , message.getRepeatedField(f("repeated_sint32" ), 1)); + Assert.assertEquals(506L , message.getRepeatedField(f("repeated_sint64" ), 1)); + Assert.assertEquals(507 , message.getRepeatedField(f("repeated_fixed32" ), 1)); + Assert.assertEquals(508L , message.getRepeatedField(f("repeated_fixed64" ), 1)); + Assert.assertEquals(509 , message.getRepeatedField(f("repeated_sfixed32"), 1)); + Assert.assertEquals(510L , message.getRepeatedField(f("repeated_sfixed64"), 1)); + Assert.assertEquals(511F , message.getRepeatedField(f("repeated_float" ), 1)); + Assert.assertEquals(512D , message.getRepeatedField(f("repeated_double" ), 1)); + Assert.assertEquals(true , message.getRepeatedField(f("repeated_bool" ), 1)); + Assert.assertEquals("515", message.getRepeatedField(f("repeated_string" ), 1)); + Assert.assertEquals(toBytes("516"), message.getRepeatedField(f("repeated_bytes"), 1)); + + Assert.assertEquals(517, + ((Message)message.getRepeatedField(f("repeatedgroup"), 1)) + .getField(repeatedGroupA)); + Assert.assertEquals(518, + ((Message)message.getRepeatedField(f("repeated_nested_message"), 1)) + .getField(nestedB)); + Assert.assertEquals(519, + ((Message)message.getRepeatedField(f("repeated_foreign_message"), 1)) + .getField(foreignC)); + Assert.assertEquals(520, + ((Message)message.getRepeatedField(f("repeated_import_message"), 1)) + .getField(importD)); + + Assert.assertEquals( nestedFoo, message.getRepeatedField(f("repeated_nested_enum" ),1)); + Assert.assertEquals(foreignFoo, message.getRepeatedField(f("repeated_foreign_enum"),1)); + Assert.assertEquals( importFoo, message.getRepeatedField(f("repeated_import_enum" ),1)); + + Assert.assertEquals("524", message.getRepeatedField(f("repeated_string_piece"), 1)); + Assert.assertEquals("525", message.getRepeatedField(f("repeated_cord"), 1)); + } + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static String readTextFromFile(String filePath) { + return readBytesFromFile(filePath).toStringUtf8(); + } + + private static File getTestDataDir() { + // Search each parent directory looking for "src/google/protobuf". + File ancestor = new File("."); + try { + ancestor = ancestor.getCanonicalFile(); + } catch (IOException e) { + throw new RuntimeException( + "Couldn't get canonical name of working directory.", e); + } + while (ancestor != null && ancestor.exists()) { + if (new File(ancestor, "src/google/protobuf").exists()) { + return new File(ancestor, "src/google/protobuf/testdata"); + } + ancestor = ancestor.getParentFile(); + } + + throw new RuntimeException( + "Could not find golden files. This test must be run from within the " + + "protobuf source package so that it can read test data files from the " + + "C++ source tree."); + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static ByteString readBytesFromFile(String filename) { + File fullPath = new File(getTestDataDir(), filename); + try { + RandomAccessFile file = new RandomAccessFile(fullPath, "r"); + byte[] content = new byte[(int) file.length()]; + file.readFully(content); + return ByteString.copyFrom(content); + } catch (IOException e) { + // Throw a RuntimeException here so that we can call this function from + // static initializers. + throw new IllegalArgumentException( + "Couldn't read file: " + fullPath.getPath(), e); + } + } + + /** + * Get the bytes of the "golden message". This is a serialized TestAllTypes + * with all fields set as they would be by + * {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file + * on disk rather than generated dynamically. The file is actually generated + * by C++ code, so testing against it verifies compatibility with C++. + */ + public static ByteString getGoldenMessage() { + if (goldenMessage == null) { + goldenMessage = readBytesFromFile("golden_message"); + } + return goldenMessage; + } + private static ByteString goldenMessage = null; +} diff --git a/java/src/test/java/com/google/protobuf/TextFormatTest.java b/java/src/test/java/com/google/protobuf/TextFormatTest.java new file mode 100644 index 00000000..8b7af107 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/TextFormatTest.java @@ -0,0 +1,575 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestEmptyMessage; +import protobuf_unittest.UnittestMset.TestMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; + +import junit.framework.TestCase; + +import java.io.StringReader; + +/** + * Test case for {@link TextFormat}. + * + * TODO(wenboz): ExtensionTest and rest of text_format_unittest.cc. + * + * @author wenboz@google.com (Wenbo Zhu) + */ +public class TextFormatTest extends TestCase { + + // A basic string with different escapable characters for testing. + private final static String kEscapeTestString = + "\"A string with ' characters \n and \r newlines and \t tabs and \001 " + + "slashes \\"; + + // A representation of the above string with all the characters escaped. + private final static String kEscapeTestStringEscaped = + "\"\\\"A string with \\' characters \\n and \\r newlines " + + "and \\t tabs and \\001 slashes \\\\\""; + + private static String allFieldsSetText = TestUtil.readTextFromFile( + "text_format_unittest_data.txt"); + private static String allExtensionsSetText = TestUtil.readTextFromFile( + "text_format_unittest_extensions_data.txt"); + + private String exoticText = + "repeated_int32: -1\n" + + "repeated_int32: -2147483648\n" + + "repeated_int64: -1\n" + + "repeated_int64: -9223372036854775808\n" + + "repeated_uint32: 4294967295\n" + + "repeated_uint32: 2147483648\n" + + "repeated_uint64: 18446744073709551615\n" + + "repeated_uint64: 9223372036854775808\n" + + "repeated_double: 123.0\n" + + "repeated_double: 123.5\n" + + "repeated_double: 0.125\n" + + "repeated_double: 1.23E17\n" + + "repeated_double: 1.235E22\n" + + "repeated_double: 1.235E-18\n" + + "repeated_double: 123.456789\n" + + "repeated_double: Infinity\n" + + "repeated_double: -Infinity\n" + + "repeated_double: NaN\n" + + "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"" + + "\\341\\210\\264\"\n" + + "repeated_bytes: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\\376\"\n"; + + private String messageSetText = + "[protobuf_unittest.TestMessageSetExtension1] {\n" + + " i: 123\n" + + "}\n" + + "[protobuf_unittest.TestMessageSetExtension2] {\n" + + " str: \"foo\"\n" + + "}\n"; + + /** Print TestAllTypes and compare with golden file. */ + public void testPrintMessage() throws Exception { + String javaText = TextFormat.printToString(TestUtil.getAllSet()); + + // Java likes to add a trailing ".0" to floats and doubles. C printf + // (with %g format) does not. Our golden files are used for both + // C++ and Java TextFormat classes, so we need to conform. + javaText = javaText.replace(".0\n", "\n"); + + assertEquals(allFieldsSetText, javaText); + } + + /** Print TestAllExtensions and compare with golden file. */ + public void testPrintExtensions() throws Exception { + String javaText = TextFormat.printToString(TestUtil.getAllExtensionsSet()); + + // Java likes to add a trailing ".0" to floats and doubles. C printf + // (with %g format) does not. Our golden files are used for both + // C++ and Java TextFormat classes, so we need to conform. + javaText = javaText.replace(".0\n", "\n"); + + assertEquals(allExtensionsSetText, javaText); + } + + public void testPrintUnknownFields() throws Exception { + // Test printing of unknown fields in a message. + + TestEmptyMessage message = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(5, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addFixed32(2) + .addFixed64(3) + .addLengthDelimited(ByteString.copyFromUtf8("4")) + .addGroup( + UnknownFieldSet.newBuilder() + .addField(10, + UnknownFieldSet.Field.newBuilder() + .addVarint(5) + .build()) + .build()) + .build()) + .addField(8, + UnknownFieldSet.Field.newBuilder() + .addVarint(1) + .addVarint(2) + .addVarint(3) + .build()) + .addField(15, + UnknownFieldSet.Field.newBuilder() + .addVarint(0xABCDEF1234567890L) + .addFixed32(0xABCD1234) + .addFixed64(0xABCDEF1234567890L) + .build()) + .build()) + .build(); + + assertEquals( + "5: 1\n" + + "5: 0x00000002\n" + + "5: 0x0000000000000003\n" + + "5: \"4\"\n" + + "5 {\n" + + " 10: 5\n" + + "}\n" + + "8: 1\n" + + "8: 2\n" + + "8: 3\n" + + "15: 12379813812177893520\n" + + "15: 0xabcd1234\n" + + "15: 0xabcdef1234567890\n", + TextFormat.printToString(message)); + } + + /** + * Helper to construct a ByteString from a String containing only 8-bit + * characters. The characters are converted directly to bytes, *not* + * encoded using UTF-8. + */ + private ByteString bytes(String str) throws Exception { + return ByteString.copyFrom(str.getBytes("ISO-8859-1")); + } + + /** + * Helper to construct a ByteString from a bunch of bytes. The inputs are + * actually ints so that I can use hex notation and not get stupid errors + * about precision. + */ + private ByteString bytes(int... bytesAsInts) { + byte[] bytes = new byte[bytesAsInts.length]; + for (int i = 0; i < bytesAsInts.length; i++) { + bytes[i] = (byte) bytesAsInts[i]; + } + return ByteString.copyFrom(bytes); + } + + public void testPrintExotic() throws Exception { + Message message = TestAllTypes.newBuilder() + // Signed vs. unsigned numbers. + .addRepeatedInt32 (-1) + .addRepeatedUint32(-1) + .addRepeatedInt64 (-1) + .addRepeatedUint64(-1) + + .addRepeatedInt32 (1 << 31) + .addRepeatedUint32(1 << 31) + .addRepeatedInt64 (1l << 63) + .addRepeatedUint64(1l << 63) + + // Floats of various precisions and exponents. + .addRepeatedDouble(123) + .addRepeatedDouble(123.5) + .addRepeatedDouble(0.125) + .addRepeatedDouble(123e15) + .addRepeatedDouble(123.5e20) + .addRepeatedDouble(123.5e-20) + .addRepeatedDouble(123.456789) + .addRepeatedDouble(Double.POSITIVE_INFINITY) + .addRepeatedDouble(Double.NEGATIVE_INFINITY) + .addRepeatedDouble(Double.NaN) + + // Strings and bytes that needing escaping. + .addRepeatedString("\0\001\007\b\f\n\r\t\013\\\'\"\u1234") + .addRepeatedBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\"\u00fe")) + .build(); + + assertEquals(exoticText, message.toString()); + } + + public void testPrintMessageSet() throws Exception { + TestMessageSet messageSet = + TestMessageSet.newBuilder() + .setExtension( + TestMessageSetExtension1.messageSetExtension, + TestMessageSetExtension1.newBuilder().setI(123).build()) + .setExtension( + TestMessageSetExtension2.messageSetExtension, + TestMessageSetExtension2.newBuilder().setStr("foo").build()) + .build(); + + assertEquals(messageSetText, messageSet.toString()); + } + + // ================================================================= + + public void testParse() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(allFieldsSetText, builder); + TestUtil.assertAllFieldsSet(builder.build()); + } + + public void testParseReader() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(new StringReader(allFieldsSetText), builder); + TestUtil.assertAllFieldsSet(builder.build()); + } + + public void testParseExtensions() throws Exception { + TestAllExtensions.Builder builder = TestAllExtensions.newBuilder(); + TextFormat.merge(allExtensionsSetText, + TestUtil.getExtensionRegistry(), + builder); + TestUtil.assertAllExtensionsSet(builder.build()); + } + + public void testParseCompatibility() throws Exception { + String original = "repeated_float: inf\n" + + "repeated_float: -inf\n" + + "repeated_float: nan\n" + + "repeated_float: inff\n" + + "repeated_float: -inff\n" + + "repeated_float: nanf\n" + + "repeated_float: 1.0f\n" + + "repeated_float: infinityf\n" + + "repeated_float: -Infinityf\n" + + "repeated_double: infinity\n" + + "repeated_double: -infinity\n" + + "repeated_double: nan\n"; + String canonical = "repeated_float: Infinity\n" + + "repeated_float: -Infinity\n" + + "repeated_float: NaN\n" + + "repeated_float: Infinity\n" + + "repeated_float: -Infinity\n" + + "repeated_float: NaN\n" + + "repeated_float: 1.0\n" + + "repeated_float: Infinity\n" + + "repeated_float: -Infinity\n" + + "repeated_double: Infinity\n" + + "repeated_double: -Infinity\n" + + "repeated_double: NaN\n"; + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(original, builder); + assertEquals(canonical, builder.build().toString()); + } + + public void testParseExotic() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge(exoticText, builder); + + // Too lazy to check things individually. Don't try to debug this + // if testPrintExotic() is failing. + assertEquals(exoticText, builder.build().toString()); + } + + public void testParseMessageSet() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); + + TestMessageSet.Builder builder = TestMessageSet.newBuilder(); + TextFormat.merge(messageSetText, extensionRegistry, builder); + TestMessageSet messageSet = builder.build(); + + assertTrue(messageSet.hasExtension( + TestMessageSetExtension1.messageSetExtension)); + assertEquals(123, messageSet.getExtension( + TestMessageSetExtension1.messageSetExtension).getI()); + assertTrue(messageSet.hasExtension( + TestMessageSetExtension2.messageSetExtension)); + assertEquals("foo", messageSet.getExtension( + TestMessageSetExtension2.messageSetExtension).getStr()); + } + + public void testParseNumericEnum() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("optional_nested_enum: 2", builder); + assertEquals(TestAllTypes.NestedEnum.BAR, builder.getOptionalNestedEnum()); + } + + public void testParseAngleBrackets() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge("OptionalGroup: < a: 1 >", builder); + assertTrue(builder.hasOptionalGroup()); + assertEquals(1, builder.getOptionalGroup().getA()); + } + + public void testParseComment() throws Exception { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + TextFormat.merge( + "# this is a comment\n" + + "optional_int32: 1 # another comment\n" + + "optional_int64: 2\n" + + "# EOF comment", builder); + assertEquals(1, builder.getOptionalInt32()); + assertEquals(2, builder.getOptionalInt64()); + } + + private void assertParseError(String error, String text) { + TestAllTypes.Builder builder = TestAllTypes.newBuilder(); + try { + TextFormat.merge(text, TestUtil.getExtensionRegistry(), builder); + fail("Expected parse exception."); + } catch (TextFormat.ParseException e) { + assertEquals(error, e.getMessage()); + } + } + + public void testParseErrors() throws Exception { + assertParseError( + "1:16: Expected \":\".", + "optional_int32 123"); + assertParseError( + "1:23: Expected identifier.", + "optional_nested_enum: ?"); + assertParseError( + "1:18: Couldn't parse integer: Number must be positive: -1", + "optional_uint32: -1"); + assertParseError( + "1:17: Couldn't parse integer: Number out of range for 32-bit signed " + + "integer: 82301481290849012385230157", + "optional_int32: 82301481290849012385230157"); + assertParseError( + "1:16: Expected \"true\" or \"false\".", + "optional_bool: maybe"); + assertParseError( + "1:18: Expected string.", + "optional_string: 123"); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe"); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe\n" + + "optional_int32: 123"); + assertParseError( + "1:18: Invalid escape sequence: '\\z'", + "optional_string: \"\\z\""); + assertParseError( + "1:18: String missing ending quote.", + "optional_string: \"ueoauaoe\n" + + "optional_int32: 123"); + assertParseError( + "1:2: Extension \"nosuchext\" not found in the ExtensionRegistry.", + "[nosuchext]: 123"); + assertParseError( + "1:20: Extension \"protobuf_unittest.optional_int32_extension\" does " + + "not extend message type \"protobuf_unittest.TestAllTypes\".", + "[protobuf_unittest.optional_int32_extension]: 123"); + assertParseError( + "1:1: Message type \"protobuf_unittest.TestAllTypes\" has no field " + + "named \"nosuchfield\".", + "nosuchfield: 123"); + assertParseError( + "1:21: Expected \">\".", + "OptionalGroup < a: 1"); + assertParseError( + "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + + "value named \"NO_SUCH_VALUE\".", + "optional_nested_enum: NO_SUCH_VALUE"); + assertParseError( + "1:23: Enum type \"protobuf_unittest.TestAllTypes.NestedEnum\" has no " + + "value with number 123.", + "optional_nested_enum: 123"); + + // Delimiters must match. + assertParseError( + "1:22: Expected identifier.", + "OptionalGroup < a: 1 }"); + assertParseError( + "1:22: Expected identifier.", + "OptionalGroup { a: 1 >"); + } + + // ================================================================= + + public void testEscape() throws Exception { + // Escape sequences. + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", + TextFormat.escapeBytes(bytes("\0\001\007\b\f\n\r\t\013\\\'\""))); + assertEquals("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"", + TextFormat.escapeText("\0\001\007\b\f\n\r\t\013\\\'\"")); + assertEquals(bytes("\0\001\007\b\f\n\r\t\013\\\'\""), + TextFormat.unescapeBytes("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); + assertEquals("\0\001\007\b\f\n\r\t\013\\\'\"", + TextFormat.unescapeText("\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"")); + + // Unicode handling. + assertEquals("\\341\\210\\264", TextFormat.escapeText("\u1234")); + assertEquals("\\341\\210\\264", + TextFormat.escapeBytes(bytes(0xe1, 0x88, 0xb4))); + assertEquals("\u1234", TextFormat.unescapeText("\\341\\210\\264")); + assertEquals(bytes(0xe1, 0x88, 0xb4), + TextFormat.unescapeBytes("\\341\\210\\264")); + assertEquals("\u1234", TextFormat.unescapeText("\\xe1\\x88\\xb4")); + assertEquals(bytes(0xe1, 0x88, 0xb4), + TextFormat.unescapeBytes("\\xe1\\x88\\xb4")); + + // Errors. + try { + TextFormat.unescapeText("\\x"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + + try { + TextFormat.unescapeText("\\z"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + + try { + TextFormat.unescapeText("\\"); + fail("Should have thrown an exception."); + } catch (TextFormat.InvalidEscapeSequence e) { + // success + } + } + + public void testParseInteger() throws Exception { + assertEquals( 0, TextFormat.parseInt32( "0")); + assertEquals( 1, TextFormat.parseInt32( "1")); + assertEquals( -1, TextFormat.parseInt32( "-1")); + assertEquals( 12345, TextFormat.parseInt32( "12345")); + assertEquals( -12345, TextFormat.parseInt32( "-12345")); + assertEquals( 2147483647, TextFormat.parseInt32( "2147483647")); + assertEquals(-2147483648, TextFormat.parseInt32("-2147483648")); + + assertEquals( 0, TextFormat.parseUInt32( "0")); + assertEquals( 1, TextFormat.parseUInt32( "1")); + assertEquals( 12345, TextFormat.parseUInt32( "12345")); + assertEquals( 2147483647, TextFormat.parseUInt32("2147483647")); + assertEquals((int) 2147483648L, TextFormat.parseUInt32("2147483648")); + assertEquals((int) 4294967295L, TextFormat.parseUInt32("4294967295")); + + assertEquals( 0L, TextFormat.parseInt64( "0")); + assertEquals( 1L, TextFormat.parseInt64( "1")); + assertEquals( -1L, TextFormat.parseInt64( "-1")); + assertEquals( 12345L, TextFormat.parseInt64( "12345")); + assertEquals( -12345L, TextFormat.parseInt64( "-12345")); + assertEquals( 2147483647L, TextFormat.parseInt64( "2147483647")); + assertEquals(-2147483648L, TextFormat.parseInt64("-2147483648")); + assertEquals( 4294967295L, TextFormat.parseInt64( "4294967295")); + assertEquals( 4294967296L, TextFormat.parseInt64( "4294967296")); + assertEquals(9223372036854775807L, + TextFormat.parseInt64("9223372036854775807")); + assertEquals(-9223372036854775808L, + TextFormat.parseInt64("-9223372036854775808")); + + assertEquals( 0L, TextFormat.parseUInt64( "0")); + assertEquals( 1L, TextFormat.parseUInt64( "1")); + assertEquals( 12345L, TextFormat.parseUInt64( "12345")); + assertEquals( 2147483647L, TextFormat.parseUInt64( "2147483647")); + assertEquals( 4294967295L, TextFormat.parseUInt64( "4294967295")); + assertEquals( 4294967296L, TextFormat.parseUInt64( "4294967296")); + assertEquals(9223372036854775807L, + TextFormat.parseUInt64("9223372036854775807")); + assertEquals(-9223372036854775808L, + TextFormat.parseUInt64("9223372036854775808")); + assertEquals(-1L, TextFormat.parseUInt64("18446744073709551615")); + + // Hex + assertEquals(0x1234abcd, TextFormat.parseInt32("0x1234abcd")); + assertEquals(-0x1234abcd, TextFormat.parseInt32("-0x1234abcd")); + assertEquals(-1, TextFormat.parseUInt64("0xffffffffffffffff")); + assertEquals(0x7fffffffffffffffL, + TextFormat.parseInt64("0x7fffffffffffffff")); + + // Octal + assertEquals(01234567, TextFormat.parseInt32("01234567")); + + // Out-of-range + try { + TextFormat.parseInt32("2147483648"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt32("-2147483649"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt32("4294967296"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt32("-1"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt64("9223372036854775808"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseInt64("-9223372036854775809"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt64("18446744073709551616"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + try { + TextFormat.parseUInt64("-1"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + + // Not a number. + try { + TextFormat.parseInt32("abcd"); + fail("Should have thrown an exception."); + } catch (NumberFormatException e) { + // success + } + } +} diff --git a/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java new file mode 100644 index 00000000..8919414e --- /dev/null +++ b/java/src/test/java/com/google/protobuf/UnknownFieldSetTest.java @@ -0,0 +1,330 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestEmptyMessage; +import protobuf_unittest.UnittestProto. + TestEmptyMessageWithExtensions; + +import junit.framework.TestCase; +import java.util.Arrays; +import java.util.Map; + +/** + * Tests related to unknown field handling. + * + * @author kenton@google.com (Kenton Varda) + */ +public class UnknownFieldSetTest extends TestCase { + public void setUp() throws Exception { + descriptor = TestAllTypes.getDescriptor(); + allFields = TestUtil.getAllSet(); + allFieldsData = allFields.toByteString(); + emptyMessage = TestEmptyMessage.parseFrom(allFieldsData); + unknownFields = emptyMessage.getUnknownFields(); + } + + UnknownFieldSet.Field getField(String name) { + Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); + assertNotNull(field); + return unknownFields.getField(field.getNumber()); + } + + // Constructs a protocol buffer which contains fields with all the same + // numbers as allFieldsData except that each field is some other wire + // type. + ByteString getBizarroData() throws Exception { + UnknownFieldSet.Builder bizarroFields = UnknownFieldSet.newBuilder(); + + UnknownFieldSet.Field varintField = + UnknownFieldSet.Field.newBuilder().addVarint(1).build(); + UnknownFieldSet.Field fixed32Field = + UnknownFieldSet.Field.newBuilder().addFixed32(1).build(); + + for (Map.Entry entry : + unknownFields.asMap().entrySet()) { + if (entry.getValue().getVarintList().isEmpty()) { + // Original field is not a varint, so use a varint. + bizarroFields.addField(entry.getKey(), varintField); + } else { + // Original field *is* a varint, so use something else. + bizarroFields.addField(entry.getKey(), fixed32Field); + } + } + + return bizarroFields.build().toByteString(); + } + + Descriptors.Descriptor descriptor; + TestAllTypes allFields; + ByteString allFieldsData; + + // An empty message that has been parsed from allFieldsData. So, it has + // unknown fields of every type. + TestEmptyMessage emptyMessage; + UnknownFieldSet unknownFields; + + // ================================================================= + + public void testVarint() throws Exception { + UnknownFieldSet.Field field = getField("optional_int32"); + assertEquals(1, field.getVarintList().size()); + assertEquals(allFields.getOptionalInt32(), + (long) field.getVarintList().get(0)); + } + + public void testFixed32() throws Exception { + UnknownFieldSet.Field field = getField("optional_fixed32"); + assertEquals(1, field.getFixed32List().size()); + assertEquals(allFields.getOptionalFixed32(), + (int) field.getFixed32List().get(0)); + } + + public void testFixed64() throws Exception { + UnknownFieldSet.Field field = getField("optional_fixed64"); + assertEquals(1, field.getFixed64List().size()); + assertEquals(allFields.getOptionalFixed64(), + (long) field.getFixed64List().get(0)); + } + + public void testLengthDelimited() throws Exception { + UnknownFieldSet.Field field = getField("optional_bytes"); + assertEquals(1, field.getLengthDelimitedList().size()); + assertEquals(allFields.getOptionalBytes(), + field.getLengthDelimitedList().get(0)); + } + + public void testGroup() throws Exception { + Descriptors.FieldDescriptor nestedFieldDescriptor = + TestAllTypes.OptionalGroup.getDescriptor().findFieldByName("a"); + assertNotNull(nestedFieldDescriptor); + + UnknownFieldSet.Field field = getField("optionalgroup"); + assertEquals(1, field.getGroupList().size()); + + UnknownFieldSet group = field.getGroupList().get(0); + assertEquals(1, group.asMap().size()); + assertTrue(group.hasField(nestedFieldDescriptor.getNumber())); + + UnknownFieldSet.Field nestedField = + group.getField(nestedFieldDescriptor.getNumber()); + assertEquals(1, nestedField.getVarintList().size()); + assertEquals(allFields.getOptionalGroup().getA(), + (long) nestedField.getVarintList().get(0)); + } + + public void testSerialize() throws Exception { + // Check that serializing the UnknownFieldSet produces the original data + // again. + ByteString data = emptyMessage.toByteString(); + assertEquals(allFieldsData, data); + } + + public void testCopyFrom() throws Exception { + TestEmptyMessage message = + TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).build(); + + assertEquals(emptyMessage.toString(), message.toString()); + } + + public void testMergeFrom() throws Exception { + TestEmptyMessage source = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(2, + UnknownFieldSet.Field.newBuilder() + .addVarint(2).build()) + .addField(3, + UnknownFieldSet.Field.newBuilder() + .addVarint(4).build()) + .build()) + .build(); + TestEmptyMessage destination = + TestEmptyMessage.newBuilder() + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(1, + UnknownFieldSet.Field.newBuilder() + .addVarint(1).build()) + .addField(3, + UnknownFieldSet.Field.newBuilder() + .addVarint(3).build()) + .build()) + .mergeFrom(source) + .build(); + + assertEquals( + "1: 1\n" + + "2: 2\n" + + "3: 3\n" + + "3: 4\n", + destination.toString()); + } + + public void testClear() throws Exception { + UnknownFieldSet fields = + UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); + assertTrue(fields.asMap().isEmpty()); + } + + public void testClearMessage() throws Exception { + TestEmptyMessage message = + TestEmptyMessage.newBuilder().mergeFrom(emptyMessage).clear().build(); + assertEquals(0, message.getSerializedSize()); + } + + public void testParseKnownAndUnknown() throws Exception { + // Test mixing known and unknown fields when parsing. + + UnknownFieldSet fields = + UnknownFieldSet.newBuilder(unknownFields) + .addField(123456, + UnknownFieldSet.Field.newBuilder().addVarint(654321).build()) + .build(); + + ByteString data = fields.toByteString(); + TestAllTypes destination = TestAllTypes.parseFrom(data); + + TestUtil.assertAllFieldsSet(destination); + assertEquals(1, destination.getUnknownFields().asMap().size()); + + UnknownFieldSet.Field field = + destination.getUnknownFields().getField(123456); + assertEquals(1, field.getVarintList().size()); + assertEquals(654321, (long) field.getVarintList().get(0)); + } + + public void testWrongTypeTreatedAsUnknown() throws Exception { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing. + + ByteString bizarroData = getBizarroData(); + TestAllTypes allTypesMessage = TestAllTypes.parseFrom(bizarroData); + TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + assertEquals(emptyMessage.toString(), allTypesMessage.toString()); + } + + public void testUnknownExtensions() throws Exception { + // Make sure fields are properly parsed to the UnknownFieldSet even when + // they are declared as extension numbers. + + TestEmptyMessageWithExtensions message = + TestEmptyMessageWithExtensions.parseFrom(allFieldsData); + + assertEquals(unknownFields.asMap().size(), + message.getUnknownFields().asMap().size()); + assertEquals(allFieldsData, message.toByteString()); + } + + public void testWrongExtensionTypeTreatedAsUnknown() throws Exception { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing extensions. + + ByteString bizarroData = getBizarroData(); + TestAllExtensions allExtensionsMessage = + TestAllExtensions.parseFrom(bizarroData); + TestEmptyMessage emptyMessage = TestEmptyMessage.parseFrom(bizarroData); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + assertEquals(emptyMessage.toString(), + allExtensionsMessage.toString()); + } + + public void testParseUnknownEnumValue() throws Exception { + Descriptors.FieldDescriptor singularField = + TestAllTypes.getDescriptor().findFieldByName("optional_nested_enum"); + Descriptors.FieldDescriptor repeatedField = + TestAllTypes.getDescriptor().findFieldByName("repeated_nested_enum"); + assertNotNull(singularField); + assertNotNull(repeatedField); + + ByteString data = + UnknownFieldSet.newBuilder() + .addField(singularField.getNumber(), + UnknownFieldSet.Field.newBuilder() + .addVarint(TestAllTypes.NestedEnum.BAR.getNumber()) + .addVarint(5) // not valid + .build()) + .addField(repeatedField.getNumber(), + UnknownFieldSet.Field.newBuilder() + .addVarint(TestAllTypes.NestedEnum.FOO.getNumber()) + .addVarint(4) // not valid + .addVarint(TestAllTypes.NestedEnum.BAZ.getNumber()) + .addVarint(6) // not valid + .build()) + .build() + .toByteString(); + + { + TestAllTypes message = TestAllTypes.parseFrom(data); + assertEquals(TestAllTypes.NestedEnum.BAR, + message.getOptionalNestedEnum()); + assertEquals( + Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), + message.getRepeatedNestedEnumList()); + assertEquals(Arrays.asList(5L), + message.getUnknownFields() + .getField(singularField.getNumber()) + .getVarintList()); + assertEquals(Arrays.asList(4L, 6L), + message.getUnknownFields() + .getField(repeatedField.getNumber()) + .getVarintList()); + } + + { + TestAllExtensions message = + TestAllExtensions.parseFrom(data, TestUtil.getExtensionRegistry()); + assertEquals(TestAllTypes.NestedEnum.BAR, + message.getExtension(UnittestProto.optionalNestedEnumExtension)); + assertEquals( + Arrays.asList(TestAllTypes.NestedEnum.FOO, TestAllTypes.NestedEnum.BAZ), + message.getExtension(UnittestProto.repeatedNestedEnumExtension)); + assertEquals(Arrays.asList(5L), + message.getUnknownFields() + .getField(singularField.getNumber()) + .getVarintList()); + assertEquals(Arrays.asList(4L, 6L), + message.getUnknownFields() + .getField(repeatedField.getNumber()) + .getVarintList()); + } + } + + public void testLargeVarint() throws Exception { + ByteString data = + UnknownFieldSet.newBuilder() + .addField(1, + UnknownFieldSet.Field.newBuilder() + .addVarint(0x7FFFFFFFFFFFFFFFL) + .build()) + .build() + .toByteString(); + UnknownFieldSet parsed = UnknownFieldSet.parseFrom(data); + UnknownFieldSet.Field field = parsed.getField(1); + assertEquals(1, field.getVarintList().size()); + assertEquals(0x7FFFFFFFFFFFFFFFL, (long)field.getVarintList().get(0)); + } +} diff --git a/java/src/test/java/com/google/protobuf/WireFormatTest.java b/java/src/test/java/com/google/protobuf/WireFormatTest.java new file mode 100644 index 00000000..84cc89f8 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/WireFormatTest.java @@ -0,0 +1,226 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +package com.google.protobuf; + +import junit.framework.TestCase; +import protobuf_unittest.UnittestProto; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestAllExtensions; +import protobuf_unittest.UnittestProto.TestFieldOrderings; +import protobuf_unittest.UnittestMset.TestMessageSet; +import protobuf_unittest.UnittestMset.RawMessageSet; +import protobuf_unittest.UnittestMset.TestMessageSetExtension1; +import protobuf_unittest.UnittestMset.TestMessageSetExtension2; + +/** + * Tests related to parsing and serialization. + * + * @author kenton@google.com (Kenton Varda) + */ +public class WireFormatTest extends TestCase { + public void testSerialization() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + ByteString rawBytes = message.toByteString(); + assertEquals(rawBytes.size(), message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + } + + public void testSerializeExtensions() throws Exception { + // TestAllTypes and TestAllExtensions should have compatible wire formats, + // so if we serealize a TestAllExtensions then parse it as TestAllTypes + // it should work. + + TestAllExtensions message = TestUtil.getAllExtensionsSet(); + ByteString rawBytes = message.toByteString(); + assertEquals(rawBytes.size(), message.getSerializedSize()); + + TestAllTypes message2 = TestAllTypes.parseFrom(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + } + + public void testParseExtensions() throws Exception { + // TestAllTypes and TestAllExtensions should have compatible wire formats, + // so if we serealize a TestAllTypes then parse it as TestAllExtensions + // it should work. + + TestAllTypes message = TestUtil.getAllSet(); + ByteString rawBytes = message.toByteString(); + + ExtensionRegistry registry = ExtensionRegistry.newInstance(); + TestUtil.registerAllExtensions(registry); + registry = registry.getUnmodifiable(); + + TestAllExtensions message2 = + TestAllExtensions.parseFrom(rawBytes, registry); + + TestUtil.assertAllExtensionsSet(message2); + } + + public void testExtensionsSerializedSize() throws Exception { + assertEquals(TestUtil.getAllSet().getSerializedSize(), + TestUtil.getAllExtensionsSet().getSerializedSize()); + } + + private void assertFieldsInOrder(ByteString data) throws Exception { + CodedInputStream input = data.newCodedInput(); + int previousTag = 0; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + assertTrue(tag > previousTag); + input.skipField(tag); + } + } + + public void testInterleavedFieldsAndExtensions() throws Exception { + // Tests that fields are written in order even when extension ranges + // are interleaved with field numbers. + ByteString data = + TestFieldOrderings.newBuilder() + .setMyInt(1) + .setMyString("foo") + .setMyFloat(1.0F) + .setExtension(UnittestProto.myExtensionInt, 23) + .setExtension(UnittestProto.myExtensionString, "bar") + .build().toByteString(); + assertFieldsInOrder(data); + + Descriptors.Descriptor descriptor = TestFieldOrderings.getDescriptor(); + ByteString dynamic_data = + DynamicMessage.newBuilder(TestFieldOrderings.getDescriptor()) + .setField(descriptor.findFieldByName("my_int"), 1L) + .setField(descriptor.findFieldByName("my_string"), "foo") + .setField(descriptor.findFieldByName("my_float"), 1.0F) + .setField(UnittestProto.myExtensionInt.getDescriptor(), 23) + .setField(UnittestProto.myExtensionString.getDescriptor(), "bar") + .build().toByteString(); + assertFieldsInOrder(dynamic_data); + } + + private static final int UNKNOWN_TYPE_ID = 1550055; + private static final int TYPE_ID_1 = + TestMessageSetExtension1.getDescriptor().getExtensions().get(0).getNumber(); + private static final int TYPE_ID_2 = + TestMessageSetExtension2.getDescriptor().getExtensions().get(0).getNumber(); + + public void testSerializeMessageSet() throws Exception { + // Set up a TestMessageSet with two known messages and an unknown one. + TestMessageSet messageSet = + TestMessageSet.newBuilder() + .setExtension( + TestMessageSetExtension1.messageSetExtension, + TestMessageSetExtension1.newBuilder().setI(123).build()) + .setExtension( + TestMessageSetExtension2.messageSetExtension, + TestMessageSetExtension2.newBuilder().setStr("foo").build()) + .setUnknownFields( + UnknownFieldSet.newBuilder() + .addField(UNKNOWN_TYPE_ID, + UnknownFieldSet.Field.newBuilder() + .addLengthDelimited(ByteString.copyFromUtf8("bar")) + .build()) + .build()) + .build(); + + ByteString data = messageSet.toByteString(); + + // Parse back using RawMessageSet and check the contents. + RawMessageSet raw = RawMessageSet.parseFrom(data); + + assertTrue(raw.getUnknownFields().asMap().isEmpty()); + + assertEquals(3, raw.getItemCount()); + assertEquals(TYPE_ID_1, raw.getItem(0).getTypeId()); + assertEquals(TYPE_ID_2, raw.getItem(1).getTypeId()); + assertEquals(UNKNOWN_TYPE_ID, raw.getItem(2).getTypeId()); + + TestMessageSetExtension1 message1 = + TestMessageSetExtension1.parseFrom( + raw.getItem(0).getMessage().toByteArray()); + assertEquals(123, message1.getI()); + + TestMessageSetExtension2 message2 = + TestMessageSetExtension2.parseFrom( + raw.getItem(1).getMessage().toByteArray()); + assertEquals("foo", message2.getStr()); + + assertEquals("bar", raw.getItem(2).getMessage().toStringUtf8()); + } + + public void testParseMessageSet() throws Exception { + ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance(); + extensionRegistry.add(TestMessageSetExtension1.messageSetExtension); + extensionRegistry.add(TestMessageSetExtension2.messageSetExtension); + + // Set up a RawMessageSet with two known messages and an unknown one. + RawMessageSet raw = + RawMessageSet.newBuilder() + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_1) + .setMessage( + TestMessageSetExtension1.newBuilder() + .setI(123) + .build().toByteString()) + .build()) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(TYPE_ID_2) + .setMessage( + TestMessageSetExtension2.newBuilder() + .setStr("foo") + .build().toByteString()) + .build()) + .addItem( + RawMessageSet.Item.newBuilder() + .setTypeId(UNKNOWN_TYPE_ID) + .setMessage(ByteString.copyFromUtf8("bar")) + .build()) + .build(); + + ByteString data = raw.toByteString(); + + // Parse as a TestMessageSet and check the contents. + TestMessageSet messageSet = + TestMessageSet.parseFrom(data, extensionRegistry); + + assertEquals(123, messageSet.getExtension( + TestMessageSetExtension1.messageSetExtension).getI()); + assertEquals("foo", messageSet.getExtension( + TestMessageSetExtension2.messageSetExtension).getStr()); + + // Check for unknown field with type LENGTH_DELIMITED, + // number UNKNOWN_TYPE_ID, and contents "bar". + UnknownFieldSet unknownFields = messageSet.getUnknownFields(); + assertEquals(1, unknownFields.asMap().size()); + assertTrue(unknownFields.hasField(UNKNOWN_TYPE_ID)); + + UnknownFieldSet.Field field = unknownFields.getField(UNKNOWN_TYPE_ID); + assertEquals(1, field.getLengthDelimitedList().size()); + assertEquals("bar", field.getLengthDelimitedList().get(0).toStringUtf8()); + } +} + diff --git a/java/src/test/java/com/google/protobuf/multiple_files_test.proto b/java/src/test/java/com/google/protobuf/multiple_files_test.proto new file mode 100644 index 00000000..1dbadfe0 --- /dev/null +++ b/java/src/test/java/com/google/protobuf/multiple_files_test.proto @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// +// A proto file which tests the java_multiple_files option. + + +import "google/protobuf/unittest.proto"; + +package protobuf_unittest; + +option java_multiple_files = true; +option java_outer_classname = "MultipleFilesTestProto"; + +message MessageWithNoOuter { + message NestedMessage { + optional int32 i = 1; + } + enum NestedEnum { + BAZ = 3; + } + optional NestedMessage nested = 1; + repeated TestAllTypes foreign = 2; + optional NestedEnum nested_enum = 3; + optional EnumWithNoOuter foreign_enum = 4; +} + +enum EnumWithNoOuter { + FOO = 1; + BAR = 2; +} + +service ServiceWithNoOuter { + rpc Foo(MessageWithNoOuter) returns(TestAllTypes); +} + +extend TestAllExtensions { + optional int32 extension_with_outer = 1234567; +} diff --git a/m4/acx_pthread.m4 b/m4/acx_pthread.m4 new file mode 100644 index 00000000..2cf20de1 --- /dev/null +++ b/m4/acx_pthread.m4 @@ -0,0 +1,363 @@ +# This was retrieved from +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?revision=1277&root=avahi +# See also (perhaps for new versions?) +# http://svn.0pointer.de/viewvc/trunk/common/acx_pthread.m4?root=avahi +# +# We've rewritten the inconsistency check code (from avahi), to work +# more broadly. In particular, it no longer assumes ld accepts -zdefs. +# This caused a restructing of the code, but the functionality has only +# changed a little. + +dnl @synopsis ACX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +dnl +dnl @summary figure out how to build C programs using POSIX threads +dnl +dnl This macro figures out how to build C programs using POSIX threads. +dnl It sets the PTHREAD_LIBS output variable to the threads library and +dnl linker flags, and the PTHREAD_CFLAGS output variable to any special +dnl C compiler flags that are needed. (The user can also force certain +dnl compiler flags/libs to be tested by setting these environment +dnl variables.) +dnl +dnl Also sets PTHREAD_CC to any special C compiler that is needed for +dnl multi-threaded programs (defaults to the value of CC otherwise). +dnl (This is necessary on AIX to use the special cc_r compiler alias.) +dnl +dnl NOTE: You are assumed to not only compile your program with these +dnl flags, but also link it with them as well. e.g. you should link +dnl with $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS +dnl $LIBS +dnl +dnl If you are only building threads programs, you may wish to use +dnl these variables in your default LIBS, CFLAGS, and CC: +dnl +dnl LIBS="$PTHREAD_LIBS $LIBS" +dnl CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +dnl CC="$PTHREAD_CC" +dnl +dnl In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute +dnl constant has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to +dnl that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +dnl +dnl ACTION-IF-FOUND is a list of shell commands to run if a threads +dnl library is found, and ACTION-IF-NOT-FOUND is a list of commands to +dnl run it if it is not found. If ACTION-IF-FOUND is not specified, the +dnl default action will define HAVE_PTHREAD. +dnl +dnl Please let the authors know if this macro fails on any platform, or +dnl if you have any other suggestions or comments. This macro was based +dnl on work by SGJ on autoconf scripts for FFTW (www.fftw.org) (with +dnl help from M. Frigo), as well as ac_pthread and hb_pthread macros +dnl posted by Alejandro Forero Cuervo to the autoconf macro repository. +dnl We are also grateful for the helpful feedback of numerous users. +dnl +dnl @category InstalledPackages +dnl @author Steven G. Johnson +dnl @version 2006-05-29 +dnl @license GPLWithACException +dnl +dnl Checks for GCC shared/pthread inconsistency based on work by +dnl Marcin Owsiany + + +AC_DEFUN([ACX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +acx_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, acx_pthread_ok=yes) + AC_MSG_RESULT($acx_pthread_ok) + if test x"$acx_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +acx_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + acx_pthread_flags="-pthreads pthread -mt -pthread $acx_pthread_flags" + ;; +esac + +if test x"$acx_pthread_ok" = xno; then +for flag in $acx_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(acx_pthread_config, pthread-config, yes, no) + if test x"$acx_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [acx_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($acx_pthread_ok) + if test "x$acx_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$acx_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi + + # The next part tries to detect GCC inconsistency with -shared on some + # architectures and systems. The problem is that in certain + # configurations, when -shared is specified, GCC "forgets" to + # internally use various flags which are still necessary. + + # + # Prepare the flags + # + save_CFLAGS="$CFLAGS" + save_LIBS="$LIBS" + save_CC="$CC" + + # Try with the flags determined by the earlier checks. + # + # -Wl,-z,defs forces link-time symbol resolution, so that the + # linking checks with -shared actually have any value + # + # FIXME: -fPIC is required for -shared on many architectures, + # so we specify it here, but the right way would probably be to + # properly detect whether it is actually required. + CFLAGS="-shared -fPIC -Wl,-z,defs $CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CC="$PTHREAD_CC" + + # In order not to create several levels of indentation, we test + # the value of "$done" until we find the cure or run out of ideas. + done="no" + + # First, make sure the CFLAGS we added are actually accepted by our + # compiler. If not (and OS X's ld, for instance, does not accept -z), + # then we can't do this test. + if test x"$done" = xno; then + AC_MSG_CHECKING([whether to check for GCC pthread/shared inconsistencies]) + AC_TRY_LINK(,, , [done=yes]) + + if test "x$done" = xyes ; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + fi + fi + + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -pthread is sufficient with -shared]) + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + fi + + # + # Linux gcc on some architectures such as mips/mipsel forgets + # about -lpthread + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lpthread fixes that]) + LIBS="-lpthread $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lpthread $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + # + # FreeBSD 4.10 gcc forgets to use -lc_r instead of -lc + # + if test x"$done" = xno; then + AC_MSG_CHECKING([whether -lc_r fixes that]) + LIBS="-lc_r $PTHREAD_LIBS $save_LIBS" + AC_TRY_LINK([#include ], + [pthread_t th; pthread_join(th, 0); + pthread_attr_init(0); pthread_cleanup_push(0, 0); + pthread_create(0,0,0,0); pthread_cleanup_pop(0); ], + [done=yes]) + + if test "x$done" = xyes; then + AC_MSG_RESULT([yes]) + PTHREAD_LIBS="-lc_r $PTHREAD_LIBS" + else + AC_MSG_RESULT([no]) + fi + fi + if test x"$done" = xno; then + # OK, we have run out of ideas + AC_MSG_WARN([Impossible to determine how to use pthreads with shared libraries]) + + # so it's not safe to assume that we may use pthreads + acx_pthread_ok=no + fi + + CFLAGS="$save_CFLAGS" + LIBS="$save_LIBS" + CC="$save_CC" +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$acx_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + acx_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl ACX_PTHREAD diff --git a/m4/stl_hash.m4 b/m4/stl_hash.m4 new file mode 100644 index 00000000..912b954e --- /dev/null +++ b/m4/stl_hash.m4 @@ -0,0 +1,43 @@ +# We check two things: where the include file is for hash_map, and +# what namespace hash_map lives in within that include file. We +# include AC_TRY_COMPILE for all the combinations we've seen in the +# wild. We define one of HAVE_HASH_MAP or HAVE_EXT_HASH_MAP depending +# on location, and HASH_NAMESPACE to be the namespace hash_map is +# defined in. +# +# Ideally we'd use AC_CACHE_CHECK, but that only lets us store one value +# at a time, and we need to store two (filename and namespace). +# prints messages itself, so we have to do the message-printing ourselves +# via AC_MSG_CHECKING + AC_MSG_RESULT. (TODO(csilvers): can we cache?) + +AC_DEFUN([AC_CXX_STL_HASH], + [AC_MSG_CHECKING(the location of hash_map) + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + ac_cv_cxx_hash_map="" + for location in ext/hash_map hash_map; do + for namespace in __gnu_cxx "" std stdext; do + if test -z "$ac_cv_cxx_hash_map"; then + AC_TRY_COMPILE([#include <$location>], + [${namespace}::hash_map t], + [ac_cv_cxx_hash_map="<$location>"; + ac_cv_cxx_hash_namespace="$namespace";]) + fi + done + done + ac_cv_cxx_hash_set=`echo "$ac_cv_cxx_hash_map" | sed s/map/set/`; + if test -n "$ac_cv_cxx_hash_map"; then + AC_DEFINE(HAVE_HASH_MAP, 1, [define if the compiler has hash_map]) + AC_DEFINE(HAVE_HASH_SET, 1, [define if the compiler has hash_set]) + AC_DEFINE_UNQUOTED(HASH_MAP_H,$ac_cv_cxx_hash_map, + [the location of ]) + AC_DEFINE_UNQUOTED(HASH_SET_H,$ac_cv_cxx_hash_set, + [the location of ]) + AC_DEFINE_UNQUOTED(HASH_NAMESPACE,$ac_cv_cxx_hash_namespace, + [the namespace of hash_map/hash_set]) + AC_MSG_RESULT([$ac_cv_cxx_hash_map]) + else + AC_MSG_RESULT() + AC_MSG_WARN([could not find an STL hash_map]) + fi +]) diff --git a/python/README.txt b/python/README.txt new file mode 100644 index 00000000..4f68d506 --- /dev/null +++ b/python/README.txt @@ -0,0 +1,65 @@ +Protocol Buffers - Google's data interchange format +Copyright 2008 Google Inc. + +This directory contains the Python Protocol Buffers runtime library. + +Normally, this directory comes as part of the protobuf package, available +from: + + http://code.google.com/p/protobuf + +The complete package includes the C++ source code, which includes the +Protocol Compiler (protoc). If you downloaded this package from PyPI +or some other Python-specific source, you may have received only the +Python part of the code. In this case, you will need to obtain the +Protocol Compiler from some other source before you can use this +package. + +Installation +============ + +1) Make sure you have Python 2.4 or newer. If in doubt, run: + + $ python -V + +2) If you do not have setuptools installed, note that it will be + downloaded and installed automatically as soon as you run setup.py. + If you would rather install it manually, you may do so by following + the instructions on this page: + + http://peak.telecommunity.com/DevCenter/EasyInstall#installation-instructions + +3) Build the C++ code, or install 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 + +4) Run the tests: + + $ python setup.py test + + If some tests fail, this library may not work correctly on your + system. Continue at your own risk. + + Please note that there is a known problem with some versions of + Python on Cygwin which causes the tests to fail after printing the + error: "sem_init: Resource temporarily unavailable". This appears + to be a bug either in Cygwin or in Python: + http://www.cygwin.com/ml/cygwin/2005-07/msg01378.html + We do not know if or when it might me fixed. We also do not know + how likely it is that this bug will affect users in practice. + +5) Install: + + $ python setup.py install + + This step may require superuser privileges. + +Usage +===== + +The complete documentation for Protocol Buffers is available via the +web at: + + http://code.google.com/apis/protocolbuffers/ diff --git a/python/ez_setup.py b/python/ez_setup.py new file mode 100755 index 00000000..989d366c --- /dev/null +++ b/python/ez_setup.py @@ -0,0 +1,277 @@ +#!python + +# This file was obtained from: +# http://peak.telecommunity.com/dist/ez_setup.py +# on 2008/7/1. + +"""Bootstrap setuptools installation + +If you want to use setuptools in your package's setup.py, just include this +file in the same directory with it, and add this to the top of your setup.py:: + + from ez_setup import use_setuptools + use_setuptools() + +If you want to require a specific version of setuptools, set a download +mirror, or use an alternate download directory, you can do so by supplying +the appropriate options to ``use_setuptools()``. + +This file can also be run as a script to install or upgrade setuptools. +""" +import sys +DEFAULT_VERSION = "0.6c8" +DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3] + +md5_data = { + 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca', + 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb', + 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b', + 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a', + 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618', + 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac', + 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5', + 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4', + 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c', + 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b', + 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27', + 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277', + 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa', + 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e', + 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e', + 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f', + 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2', + 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc', + 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167', + 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64', + 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d', + 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20', + 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab', + 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53', + 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2', + 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e', + 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372', + 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902', + 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de', + 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b', +} + +import sys, os + +def _validate_md5(egg_name, data): + if egg_name in md5_data: + from md5 import md5 + digest = md5(data).hexdigest() + if digest != md5_data[egg_name]: + print >>sys.stderr, ( + "md5 validation of %s failed! (Possible download problem?)" + % egg_name + ) + sys.exit(2) + return data + + +def use_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + download_delay=15 +): + """Automatically find/download setuptools and make it available on sys.path + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end with + a '/'). `to_dir` is the directory where setuptools will be downloaded, if + it is not already available. If `download_delay` is specified, it should + be the number of seconds that will be paused before initiating a download, + should one be required. If an older version of setuptools is installed, + this routine will print a message to ``sys.stderr`` and raise SystemExit in + an attempt to abort the calling script. + """ + was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules + def do_download(): + egg = download_setuptools(version, download_base, to_dir, download_delay) + sys.path.insert(0, egg) + import setuptools; setuptools.bootstrap_install_from = egg + try: + import pkg_resources + except ImportError: + return do_download() + try: + pkg_resources.require("setuptools>="+version); return + except pkg_resources.VersionConflict, e: + if was_imported: + print >>sys.stderr, ( + "The required version of setuptools (>=%s) is not available, and\n" + "can't be installed while this script is running. Please install\n" + " a more recent version first, using 'easy_install -U setuptools'." + "\n\n(Currently using %r)" + ) % (version, e.args[0]) + sys.exit(2) + else: + del pkg_resources, sys.modules['pkg_resources'] # reload ok + return do_download() + except pkg_resources.DistributionNotFound: + return do_download() + +def download_setuptools( + version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, + delay = 15 +): + """Download setuptools from a specified location and return its filename + + `version` should be a valid setuptools version number that is available + as an egg for download under the `download_base` URL (which should end + with a '/'). `to_dir` is the directory where the egg will be downloaded. + `delay` is the number of seconds to pause before an actual download attempt. + """ + import urllib2, shutil + egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3]) + url = download_base + egg_name + saveto = os.path.join(to_dir, egg_name) + src = dst = None + if not os.path.exists(saveto): # Avoid repeated downloads + try: + from distutils import log + if delay: + log.warn(""" +--------------------------------------------------------------------------- +This script requires setuptools version %s to run (even to display +help). I will attempt to download it for you (from +%s), but +you may need to enable firewall access for this script first. +I will start the download in %d seconds. + +(Note: if this machine does not have network access, please obtain the file + + %s + +and place it in this directory before rerunning this script.) +---------------------------------------------------------------------------""", + version, download_base, delay, url + ); from time import sleep; sleep(delay) + log.warn("Downloading %s", url) + src = urllib2.urlopen(url) + # Read/write all in one block, so we don't create a corrupt file + # if the download is interrupted. + data = _validate_md5(egg_name, src.read()) + dst = open(saveto,"wb"); dst.write(data) + finally: + if src: src.close() + if dst: dst.close() + return os.path.realpath(saveto) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +def main(argv, version=DEFAULT_VERSION): + """Install or upgrade setuptools and EasyInstall""" + try: + import setuptools + except ImportError: + egg = None + try: + egg = download_setuptools(version, delay=0) + sys.path.insert(0,egg) + from setuptools.command.easy_install import main + return main(list(argv)+[egg]) # we're done here + finally: + if egg and os.path.exists(egg): + os.unlink(egg) + else: + if setuptools.__version__ == '0.0.1': + print >>sys.stderr, ( + "You have an obsolete version of setuptools installed. Please\n" + "remove it from your system entirely before rerunning this script." + ) + sys.exit(2) + + req = "setuptools>="+version + import pkg_resources + try: + pkg_resources.require(req) + except pkg_resources.VersionConflict: + try: + from setuptools.command.easy_install import main + except ImportError: + from easy_install import main + main(list(argv)+[download_setuptools(delay=0)]) + sys.exit(0) # try to force an exit + else: + if argv: + from setuptools.command.easy_install import main + main(argv) + else: + print "Setuptools version",version,"or greater has been installed." + print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)' + +def update_md5(filenames): + """Update our built-in md5 registry""" + + import re + from md5 import md5 + + for name in filenames: + base = os.path.basename(name) + f = open(name,'rb') + md5_data[base] = md5(f.read()).hexdigest() + f.close() + + data = [" %r: %r,\n" % it for it in md5_data.items()] + data.sort() + repl = "".join(data) + + import inspect + srcfile = inspect.getsourcefile(sys.modules[__name__]) + f = open(srcfile, 'rb'); src = f.read(); f.close() + + match = re.search("\nmd5_data = {\n([^}]+)}", src) + if not match: + print >>sys.stderr, "Internal error!" + sys.exit(2) + + src = src[:match.start(1)] + repl + src[match.end(1):] + f = open(srcfile,'w') + f.write(src) + f.close() + + +if __name__=='__main__': + if len(sys.argv)>2 and sys.argv[1]=='--md5update': + update_md5(sys.argv[2:]) + else: + main(sys.argv[1:]) + + + + + diff --git a/python/google/__init__.py b/python/google/__init__.py new file mode 100755 index 00000000..de40ea7c --- /dev/null +++ b/python/google/__init__.py @@ -0,0 +1 @@ +__import__('pkg_resources').declare_namespace(__name__) diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py new file mode 100755 index 00000000..04748053 --- /dev/null +++ b/python/google/protobuf/descriptor.py @@ -0,0 +1,419 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +# TODO(robinson): We probably need to provide deep-copy methods for +# descriptor types. When a FieldDescriptor is passed into +# Descriptor.__init__(), we should make a deep copy and then set +# containing_type on it. Alternatively, we could just get +# rid of containing_type (iit's not needed for reflection.py, at least). +# +# TODO(robinson): Print method? +# +# TODO(robinson): Useful __repr__? + +"""Descriptors essentially contain exactly the information found in a .proto +file, in types that make this information accessible in Python. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +class DescriptorBase(object): + + """Descriptors base class. + + This class is the base of all descriptor classes. It provides common options + related functionaility. + """ + + def __init__(self, options, options_class_name): + """Initialize the descriptor given its options message and the name of the + class of the options message. The name of the class is required in case + the options message is None and has to be created. + """ + self._options = options + self._options_class_name = options_class_name + + def GetOptions(self): + """Retrieves descriptor options. + + This method returns the options set or creates the default options for the + descriptor. + """ + if self._options: + return self._options + from google.protobuf import descriptor_pb2 + try: + options_class = getattr(descriptor_pb2, self._options_class_name) + except AttributeError: + raise RuntimeError('Unknown options class name %s!' % + (self._options_class_name)) + self._options = options_class() + return self._options + + +class Descriptor(DescriptorBase): + + """Descriptor for a protocol message type. + + A Descriptor instance has the following attributes: + + name: (str) Name of this protocol message type. + full_name: (str) Fully-qualified name of this protocol message type, + which will include protocol "package" name and the name of any + enclosing types. + + filename: (str) Name of the .proto file containing this message. + + containing_type: (Descriptor) Reference to the descriptor of the + type containing us, or None if we have no containing type. + + fields: (list of FieldDescriptors) Field descriptors for all + fields in this type. + fields_by_number: (dict int -> FieldDescriptor) Same FieldDescriptor + objects as in |fields|, but indexed by "number" attribute in each + FieldDescriptor. + fields_by_name: (dict str -> FieldDescriptor) Same FieldDescriptor + objects as in |fields|, but indexed by "name" attribute in each + FieldDescriptor. + + nested_types: (list of Descriptors) Descriptor references + for all protocol message types nested within this one. + nested_types_by_name: (dict str -> Descriptor) Same Descriptor + objects as in |nested_types|, but indexed by "name" attribute + in each Descriptor. + + enum_types: (list of EnumDescriptors) EnumDescriptor references + for all enums contained within this type. + enum_types_by_name: (dict str ->EnumDescriptor) Same EnumDescriptor + objects as in |enum_types|, but indexed by "name" attribute + in each EnumDescriptor. + enum_values_by_name: (dict str -> EnumValueDescriptor) Dict mapping + from enum value name to EnumValueDescriptor for that value. + + extensions: (list of FieldDescriptor) All extensions defined directly + within this message type (NOT within a nested type). + extensions_by_name: (dict, string -> FieldDescriptor) Same FieldDescriptor + objects as |extensions|, but indexed by "name" attribute of each + FieldDescriptor. + + options: (descriptor_pb2.MessageOptions) Protocol message options or None + to use default message options. + """ + + def __init__(self, name, full_name, filename, containing_type, + fields, nested_types, enum_types, extensions, options=None): + """Arguments to __init__() are as described in the description + of Descriptor fields above. + """ + super(Descriptor, self).__init__(options, 'MessageOptions') + self.name = name + self.full_name = full_name + self.filename = filename + self.containing_type = containing_type + + # We have fields in addition to fields_by_name and fields_by_number, + # so that: + # 1. Clients can index fields by "order in which they're listed." + # 2. Clients can easily iterate over all fields with the terse + # syntax: for f in descriptor.fields: ... + self.fields = fields + for field in self.fields: + field.containing_type = self + self.fields_by_number = dict((f.number, f) for f in fields) + self.fields_by_name = dict((f.name, f) for f in fields) + + self.nested_types = nested_types + self.nested_types_by_name = dict((t.name, t) for t in nested_types) + + self.enum_types = enum_types + for enum_type in self.enum_types: + enum_type.containing_type = self + self.enum_types_by_name = dict((t.name, t) for t in enum_types) + self.enum_values_by_name = dict( + (v.name, v) for t in enum_types for v in t.values) + + self.extensions = extensions + for extension in self.extensions: + extension.extension_scope = self + self.extensions_by_name = dict((f.name, f) for f in extensions) + + +# TODO(robinson): We should have aggressive checking here, +# for example: +# * If you specify a repeated field, you should not be allowed +# to specify a default value. +# * [Other examples here as needed]. +# +# TODO(robinson): for this and other *Descriptor classes, we +# might also want to lock things down aggressively (e.g., +# prevent clients from setting the attributes). Having +# stronger invariants here in general will reduce the number +# of runtime checks we must do in reflection.py... +class FieldDescriptor(DescriptorBase): + + """Descriptor for a single field in a .proto file. + + A FieldDescriptor instance has the following attriubtes: + + name: (str) Name of this field, exactly as it appears in .proto. + full_name: (str) Name of this field, including containing scope. This is + particularly relevant for extensions. + index: (int) Dense, 0-indexed index giving the order that this + field textually appears within its message in the .proto file. + number: (int) Tag number declared for this field in the .proto file. + + type: (One of the TYPE_* constants below) Declared type. + cpp_type: (One of the CPPTYPE_* constants below) C++ type used to + represent this field. + + label: (One of the LABEL_* constants below) Tells whether this + field is optional, required, or repeated. + default_value: (Varies) Default value of this field. Only + meaningful for non-repeated scalar fields. Repeated fields + should always set this to [], and non-repeated composite + fields should always set this to None. + + containing_type: (Descriptor) Descriptor of the protocol message + type that contains this field. Set by the Descriptor constructor + if we're passed into one. + Somewhat confusingly, for extension fields, this is the + descriptor of the EXTENDED message, not the descriptor + of the message containing this field. (See is_extension and + extension_scope below). + message_type: (Descriptor) If a composite field, a descriptor + of the message type contained in this field. Otherwise, this is None. + enum_type: (EnumDescriptor) If this field contains an enum, a + descriptor of that enum. Otherwise, this is None. + + is_extension: True iff this describes an extension field. + extension_scope: (Descriptor) Only meaningful if is_extension is True. + Gives the message that immediately contains this extension field. + Will be None iff we're a top-level (file-level) extension field. + + options: (descriptor_pb2.FieldOptions) Protocol message field options or + None to use default field options. + """ + + # Must be consistent with C++ FieldDescriptor::Type enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + TYPE_DOUBLE = 1 + TYPE_FLOAT = 2 + TYPE_INT64 = 3 + TYPE_UINT64 = 4 + TYPE_INT32 = 5 + TYPE_FIXED64 = 6 + TYPE_FIXED32 = 7 + TYPE_BOOL = 8 + TYPE_STRING = 9 + TYPE_GROUP = 10 + TYPE_MESSAGE = 11 + TYPE_BYTES = 12 + TYPE_UINT32 = 13 + TYPE_ENUM = 14 + TYPE_SFIXED32 = 15 + TYPE_SFIXED64 = 16 + TYPE_SINT32 = 17 + TYPE_SINT64 = 18 + MAX_TYPE = 18 + + # Must be consistent with C++ FieldDescriptor::CppType enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + CPPTYPE_INT32 = 1 + CPPTYPE_INT64 = 2 + CPPTYPE_UINT32 = 3 + CPPTYPE_UINT64 = 4 + CPPTYPE_DOUBLE = 5 + CPPTYPE_FLOAT = 6 + CPPTYPE_BOOL = 7 + CPPTYPE_ENUM = 8 + CPPTYPE_STRING = 9 + CPPTYPE_MESSAGE = 10 + MAX_CPPTYPE = 10 + + # Must be consistent with C++ FieldDescriptor::Label enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + LABEL_OPTIONAL = 1 + LABEL_REQUIRED = 2 + LABEL_REPEATED = 3 + MAX_LABEL = 3 + + def __init__(self, name, full_name, index, number, type, cpp_type, label, + default_value, message_type, enum_type, containing_type, + is_extension, extension_scope, options=None): + """The arguments are as described in the description of FieldDescriptor + attributes above. + + Note that containing_type may be None, and may be set later if necessary + (to deal with circular references between message types, for example). + Likewise for extension_scope. + """ + super(FieldDescriptor, self).__init__(options, 'FieldOptions') + self.name = name + self.full_name = full_name + self.index = index + self.number = number + self.type = type + self.cpp_type = cpp_type + self.label = label + self.default_value = default_value + self.containing_type = containing_type + self.message_type = message_type + self.enum_type = enum_type + self.is_extension = is_extension + self.extension_scope = extension_scope + + +class EnumDescriptor(DescriptorBase): + + """Descriptor for an enum defined in a .proto file. + + An EnumDescriptor instance has the following attributes: + + name: (str) Name of the enum type. + full_name: (str) Full name of the type, including package name + and any enclosing type(s). + filename: (str) Name of the .proto file in which this appears. + + values: (list of EnumValueDescriptors) List of the values + in this enum. + values_by_name: (dict str -> EnumValueDescriptor) Same as |values|, + but indexed by the "name" field of each EnumValueDescriptor. + values_by_number: (dict int -> EnumValueDescriptor) Same as |values|, + but indexed by the "number" field of each EnumValueDescriptor. + containing_type: (Descriptor) Descriptor of the immediate containing + type of this enum, or None if this is an enum defined at the + top level in a .proto file. Set by Descriptor's constructor + if we're passed into one. + options: (descriptor_pb2.EnumOptions) Enum options message or + None to use default enum options. + """ + + def __init__(self, name, full_name, filename, values, + containing_type=None, options=None): + """Arguments are as described in the attribute description above.""" + super(EnumDescriptor, self).__init__(options, 'EnumOptions') + self.name = name + self.full_name = full_name + self.filename = filename + self.values = values + for value in self.values: + value.type = self + self.values_by_name = dict((v.name, v) for v in values) + self.values_by_number = dict((v.number, v) for v in values) + self.containing_type = containing_type + + +class EnumValueDescriptor(DescriptorBase): + + """Descriptor for a single value within an enum. + + name: (str) Name of this value. + index: (int) Dense, 0-indexed index giving the order that this + value appears textually within its enum in the .proto file. + number: (int) Actual number assigned to this enum value. + type: (EnumDescriptor) EnumDescriptor to which this value + belongs. Set by EnumDescriptor's constructor if we're + passed into one. + options: (descriptor_pb2.EnumValueOptions) Enum value options message or + None to use default enum value options options. + """ + + def __init__(self, name, index, number, type=None, options=None): + """Arguments are as described in the attribute description above.""" + super(EnumValueDescriptor, self).__init__(options, 'EnumValueOptions') + self.name = name + self.index = index + self.number = number + self.type = type + + +class ServiceDescriptor(DescriptorBase): + + """Descriptor for a service. + + name: (str) Name of the service. + full_name: (str) Full name of the service, including package name. + index: (int) 0-indexed index giving the order that this services + definition appears withing the .proto file. + methods: (list of MethodDescriptor) List of methods provided by this + service. + options: (descriptor_pb2.ServiceOptions) Service options message or + None to use default service options. + """ + + def __init__(self, name, full_name, index, methods, options=None): + super(ServiceDescriptor, self).__init__(options, 'ServiceOptions') + self.name = name + self.full_name = full_name + self.index = index + self.methods = methods + # Set the containing service for each method in this service. + for method in self.methods: + method.containing_service = self + + def FindMethodByName(self, name): + """Searches for the specified method, and returns its descriptor.""" + for method in self.methods: + if name == method.name: + return method + return None + + +class MethodDescriptor(DescriptorBase): + + """Descriptor for a method in a service. + + name: (str) Name of the method within the service. + full_name: (str) Full name of method. + index: (int) 0-indexed index of the method inside the service. + containing_service: (ServiceDescriptor) The service that contains this + method. + input_type: The descriptor of the message that this method accepts. + output_type: The descriptor of the message that this method returns. + options: (descriptor_pb2.MethodOptions) Method options message or + None to use default method options. + """ + + def __init__(self, name, full_name, index, containing_service, + input_type, output_type, options=None): + """The arguments are as described in the description of MethodDescriptor + attributes above. + + Note that containing_service may be None, and may be set later if necessary. + """ + super(MethodDescriptor, self).__init__(options, 'MethodOptions') + self.name = name + self.full_name = full_name + self.index = index + self.containing_service = containing_service + self.input_type = input_type + self.output_type = output_type + + +def _ParseOptions(message, string): + """Parses serialized options. + + This helper function is used to parse serialized options in generated + proto2 files. It must not be used outside proto2. + """ + message.ParseFromString(string) + return message; diff --git a/python/google/protobuf/internal/__init__.py b/python/google/protobuf/internal/__init__.py new file mode 100755 index 00000000..e69de29b diff --git a/python/google/protobuf/internal/decoder.py b/python/google/protobuf/internal/decoder.py new file mode 100755 index 00000000..b81f04a5 --- /dev/null +++ b/python/google/protobuf/internal/decoder.py @@ -0,0 +1,194 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Class for decoding protocol buffer primitives. + +Contains the logic for decoding every logical protocol field type +from one of the 5 physical wire types. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import input_stream +from google.protobuf.internal import wire_format + + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by WireFormat from the C++ proto2 +# implementation. + + +class Decoder(object): + + """Decodes logical protocol buffer fields from the wire.""" + + def __init__(self, s): + """Initializes the decoder to read from s. + + Args: + s: An immutable sequence of bytes, which must be accessible + via the Python buffer() primitive (i.e., buffer(s)). + """ + self._stream = input_stream.InputStream(s) + + def EndOfStream(self): + """Returns true iff we've reached the end of the bytes we're reading.""" + return self._stream.EndOfStream() + + def Position(self): + """Returns the 0-indexed position in |s|.""" + return self._stream.Position() + + def ReadFieldNumberAndWireType(self): + """Reads a tag from the wire. Returns a (field_number, wire_type) pair.""" + tag_and_type = self.ReadUInt32() + return wire_format.UnpackTag(tag_and_type) + + def SkipBytes(self, bytes): + """Skips the specified number of bytes on the wire.""" + self._stream.SkipBytes(bytes) + + # Note that the Read*() methods below are not exactly symmetrical with the + # corresponding Encoder.Append*() methods. Those Encoder methods first + # encode a tag, but the Read*() methods below assume that the tag has already + # been read, and that the client wishes to read a field of the specified type + # starting at the current position. + + def ReadInt32(self): + """Reads and returns a signed, varint-encoded, 32-bit integer.""" + return self._stream.ReadVarint32() + + def ReadInt64(self): + """Reads and returns a signed, varint-encoded, 64-bit integer.""" + return self._stream.ReadVarint64() + + def ReadUInt32(self): + """Reads and returns an signed, varint-encoded, 32-bit integer.""" + return self._stream.ReadVarUInt32() + + def ReadUInt64(self): + """Reads and returns an signed, varint-encoded,64-bit integer.""" + return self._stream.ReadVarUInt64() + + def ReadSInt32(self): + """Reads and returns a signed, zigzag-encoded, varint-encoded, + 32-bit integer.""" + return wire_format.ZigZagDecode(self._stream.ReadVarUInt32()) + + def ReadSInt64(self): + """Reads and returns a signed, zigzag-encoded, varint-encoded, + 64-bit integer.""" + return wire_format.ZigZagDecode(self._stream.ReadVarUInt64()) + + def ReadFixed32(self): + """Reads and returns an unsigned, fixed-width, 32-bit integer.""" + return self._stream.ReadLittleEndian32() + + def ReadFixed64(self): + """Reads and returns an unsigned, fixed-width, 64-bit integer.""" + return self._stream.ReadLittleEndian64() + + def ReadSFixed32(self): + """Reads and returns a signed, fixed-width, 32-bit integer.""" + value = self._stream.ReadLittleEndian32() + if value >= (1 << 31): + value -= (1 << 32) + return value + + def ReadSFixed64(self): + """Reads and returns a signed, fixed-width, 64-bit integer.""" + value = self._stream.ReadLittleEndian64() + if value >= (1 << 63): + value -= (1 << 64) + return value + + def ReadFloat(self): + """Reads and returns a 4-byte floating-point number.""" + serialized = self._stream.ReadString(4) + return struct.unpack('f', serialized)[0] + + def ReadDouble(self): + """Reads and returns an 8-byte floating-point number.""" + serialized = self._stream.ReadString(8) + return struct.unpack('d', serialized)[0] + + def ReadBool(self): + """Reads and returns a bool.""" + i = self._stream.ReadVarUInt32() + return bool(i) + + def ReadEnum(self): + """Reads and returns an enum value.""" + return self._stream.ReadVarUInt32() + + def ReadString(self): + """Reads and returns a length-delimited string.""" + length = self._stream.ReadVarUInt32() + return self._stream.ReadString(length) + + def ReadBytes(self): + """Reads and returns a length-delimited byte sequence.""" + return self.ReadString() + + def ReadMessageInto(self, msg): + """Calls msg.MergeFromString() to merge + length-delimited serialized message data into |msg|. + + REQUIRES: The decoder must be positioned at the serialized "length" + prefix to a length-delmiited serialized message. + + POSTCONDITION: The decoder is positioned just after the + serialized message, and we have merged those serialized + contents into |msg|. + """ + length = self._stream.ReadVarUInt32() + sub_buffer = self._stream.GetSubBuffer(length) + num_bytes_used = msg.MergeFromString(sub_buffer) + if num_bytes_used != length: + raise message.DecodeError( + 'Submessage told to deserialize from %d-byte encoding, ' + 'but used only %d bytes' % (length, num_bytes_used)) + self._stream.SkipBytes(num_bytes_used) + + def ReadGroupInto(self, expected_field_number, group): + """Calls group.MergeFromString() to merge + END_GROUP-delimited serialized message data into |group|. + We'll raise an exception if we don't find an END_GROUP + tag immediately after the serialized message contents. + + REQUIRES: The decoder is positioned just after the START_GROUP + tag for this group. + + POSTCONDITION: The decoder is positioned just after the + END_GROUP tag for this group, and we have merged + the contents of the group into |group|. + """ + sub_buffer = self._stream.GetSubBuffer() # No a priori length limit. + num_bytes_used = group.MergeFromString(sub_buffer) + if num_bytes_used < 0: + raise message.DecodeError('Group message reported negative bytes read.') + self._stream.SkipBytes(num_bytes_used) + field_number, field_type = self.ReadFieldNumberAndWireType() + if field_type != wire_format.WIRETYPE_END_GROUP: + raise message.DecodeError('Group message did not end with an END_GROUP.') + if field_number != expected_field_number: + raise message.DecodeError('END_GROUP tag had field ' + 'number %d, was expecting field number %d' % ( + field_number, expected_field_number)) + # We're now positioned just after the END_GROUP tag. Perfect. diff --git a/python/google/protobuf/internal/decoder_test.py b/python/google/protobuf/internal/decoder_test.py new file mode 100755 index 00000000..626816f6 --- /dev/null +++ b/python/google/protobuf/internal/decoder_test.py @@ -0,0 +1,230 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.internal.decoder.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +import unittest +from google.protobuf.internal import wire_format +from google.protobuf.internal import encoder +from google.protobuf.internal import decoder +import logging +from google.protobuf.internal import input_stream +from google.protobuf import message +import mox + + +class DecoderTest(unittest.TestCase): + + def setUp(self): + self.mox = mox.Mox() + self.mock_stream = self.mox.CreateMock(input_stream.InputStream) + self.mock_message = self.mox.CreateMock(message.Message) + + def testReadFieldNumberAndWireType(self): + # Test field numbers that will require various varint sizes. + for expected_field_number in (1, 15, 16, 2047, 2048): + for expected_wire_type in range(6): # Highest-numbered wiretype is 5. + e = encoder.Encoder() + e._AppendTag(expected_field_number, expected_wire_type) + s = e.ToString() + d = decoder.Decoder(s) + field_number, wire_type = d.ReadFieldNumberAndWireType() + self.assertEqual(expected_field_number, field_number) + self.assertEqual(expected_wire_type, wire_type) + + def ReadScalarTestHelper(self, test_name, decoder_method, expected_result, + expected_stream_method_name, + stream_method_return, *args): + """Helper for testReadScalars below. + + Calls one of the Decoder.Read*() methods and ensures that the results are + as expected. + + Args: + test_name: Name of this test, used for logging only. + decoder_method: Unbound decoder.Decoder method to call. + expected_result: Value we expect returned from decoder_method(). + expected_stream_method_name: (string) Name of the InputStream + method we expect Decoder to call to actually read the value + on the wire. + stream_method_return: Value our mocked-out stream method should + return to the decoder. + args: Additional arguments that we expect to be passed to the + stream method. + """ + logging.info('Testing %s scalar input.\n' + 'Calling %r(), and expecting that to call the ' + 'stream method %s(%r), which will return %r. Finally, ' + 'expecting the Decoder method to return %r'% ( + test_name, decoder_method, + expected_stream_method_name, args, stream_method_return, + expected_result)) + + d = decoder.Decoder('') + d._stream = self.mock_stream + if decoder_method in (decoder.Decoder.ReadString, + decoder.Decoder.ReadBytes): + self.mock_stream.ReadVarUInt32().AndReturn(len(stream_method_return)) + # We have to use names instead of methods to work around some + # mox weirdness. (ResetAll() is overzealous). + expected_stream_method = getattr(self.mock_stream, + expected_stream_method_name) + expected_stream_method(*args).AndReturn(stream_method_return) + + self.mox.ReplayAll() + self.assertEqual(expected_result, decoder_method(d)) + self.mox.VerifyAll() + self.mox.ResetAll() + + def testReadScalars(self): + test_string = 'I can feel myself getting sutpider.' + scalar_tests = [ + ['int32', decoder.Decoder.ReadInt32, 0, 'ReadVarint32', 0], + ['int64', decoder.Decoder.ReadInt64, 0, 'ReadVarint64', 0], + ['uint32', decoder.Decoder.ReadUInt32, 0, 'ReadVarUInt32', 0], + ['uint64', decoder.Decoder.ReadUInt64, 0, 'ReadVarUInt64', 0], + ['fixed32', decoder.Decoder.ReadFixed32, 0xffffffff, + 'ReadLittleEndian32', 0xffffffff], + ['fixed64', decoder.Decoder.ReadFixed64, 0xffffffffffffffff, + 'ReadLittleEndian64', 0xffffffffffffffff], + ['sfixed32', decoder.Decoder.ReadSFixed32, -1, + 'ReadLittleEndian32', 0xffffffff], + ['sfixed64', decoder.Decoder.ReadSFixed64, -1, + 'ReadLittleEndian64', 0xffffffffffffffff], + ['float', decoder.Decoder.ReadFloat, 0.0, + 'ReadString', struct.pack('f', 0.0), 4], + ['double', decoder.Decoder.ReadDouble, 0.0, + 'ReadString', struct.pack('d', 0.0), 8], + ['bool', decoder.Decoder.ReadBool, True, 'ReadVarUInt32', 1], + ['enum', decoder.Decoder.ReadEnum, 23, 'ReadVarUInt32', 23], + ['string', decoder.Decoder.ReadString, + test_string, 'ReadString', test_string, len(test_string)], + ['bytes', decoder.Decoder.ReadBytes, + test_string, 'ReadString', test_string, len(test_string)], + # We test zigzag decoding routines more extensively below. + ['sint32', decoder.Decoder.ReadSInt32, -1, 'ReadVarUInt32', 1], + ['sint64', decoder.Decoder.ReadSInt64, -1, 'ReadVarUInt64', 1], + ] + # Ensure that we're testing different Decoder methods and using + # different test names in all test cases above. + self.assertEqual(len(scalar_tests), len(set(t[0] for t in scalar_tests))) + self.assertEqual(len(scalar_tests), len(set(t[1] for t in scalar_tests))) + for args in scalar_tests: + self.ReadScalarTestHelper(*args) + + def testReadMessageInto(self): + length = 23 + def Test(simulate_error): + d = decoder.Decoder('') + d._stream = self.mock_stream + self.mock_stream.ReadVarUInt32().AndReturn(length) + sub_buffer = object() + self.mock_stream.GetSubBuffer(length).AndReturn(sub_buffer) + + if simulate_error: + self.mock_message.MergeFromString(sub_buffer).AndReturn(length - 1) + self.mox.ReplayAll() + self.assertRaises( + message.DecodeError, d.ReadMessageInto, self.mock_message) + else: + self.mock_message.MergeFromString(sub_buffer).AndReturn(length) + self.mock_stream.SkipBytes(length) + self.mox.ReplayAll() + d.ReadMessageInto(self.mock_message) + + self.mox.VerifyAll() + self.mox.ResetAll() + + Test(simulate_error=False) + Test(simulate_error=True) + + def testReadGroupInto_Success(self): + # Test both the empty and nonempty cases. + for num_bytes in (5, 0): + field_number = expected_field_number = 10 + d = decoder.Decoder('') + d._stream = self.mock_stream + sub_buffer = object() + self.mock_stream.GetSubBuffer().AndReturn(sub_buffer) + self.mock_message.MergeFromString(sub_buffer).AndReturn(num_bytes) + self.mock_stream.SkipBytes(num_bytes) + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_END_GROUP)) + self.mox.ReplayAll() + d.ReadGroupInto(expected_field_number, self.mock_message) + self.mox.VerifyAll() + self.mox.ResetAll() + + def ReadGroupInto_FailureTestHelper(self, bytes_read): + d = decoder.Decoder('') + d._stream = self.mock_stream + sub_buffer = object() + self.mock_stream.GetSubBuffer().AndReturn(sub_buffer) + self.mock_message.MergeFromString(sub_buffer).AndReturn(bytes_read) + return d + + def testReadGroupInto_NegativeBytesReported(self): + expected_field_number = 10 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=-1) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testReadGroupInto_NoEndGroupTag(self): + field_number = expected_field_number = 10 + num_bytes = 5 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes) + self.mock_stream.SkipBytes(num_bytes) + # Right field number, wrong wire type. + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testReadGroupInto_WrongFieldNumberInEndGroupTag(self): + expected_field_number = 10 + field_number = expected_field_number + 1 + num_bytes = 5 + d = self.ReadGroupInto_FailureTestHelper(bytes_read=num_bytes) + self.mock_stream.SkipBytes(num_bytes) + # Wrong field number, right wire type. + self.mock_stream.ReadVarUInt32().AndReturn(wire_format.PackTag( + field_number, wire_format.WIRETYPE_END_GROUP)) + self.mox.ReplayAll() + self.assertRaises(message.DecodeError, + d.ReadGroupInto, expected_field_number, + self.mock_message) + self.mox.VerifyAll() + + def testSkipBytes(self): + d = decoder.Decoder('') + num_bytes = 1024 + self.mock_stream.SkipBytes(num_bytes) + d._stream = self.mock_stream + self.mox.ReplayAll() + d.SkipBytes(num_bytes) + self.mox.VerifyAll() + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/descriptor_test.py b/python/google/protobuf/internal/descriptor_test.py new file mode 100755 index 00000000..625d0326 --- /dev/null +++ b/python/google/protobuf/internal/descriptor_test.py @@ -0,0 +1,97 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Unittest for google.protobuf.internal.descriptor.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor + +class DescriptorTest(unittest.TestCase): + + def setUp(self): + self.my_enum = descriptor.EnumDescriptor( + name='ForeignEnum', + full_name='protobuf_unittest.ForeignEnum', + filename='ForeignEnum', + values=[ + descriptor.EnumValueDescriptor(name='FOREIGN_FOO', index=0, number=4), + descriptor.EnumValueDescriptor(name='FOREIGN_BAR', index=1, number=5), + descriptor.EnumValueDescriptor(name='FOREIGN_BAZ', index=2, number=6), + ]) + self.my_message = descriptor.Descriptor( + name='NestedMessage', + full_name='protobuf_unittest.TestAllTypes.NestedMessage', + filename='some/filename/some.proto', + containing_type=None, + fields=[ + descriptor.FieldDescriptor( + name='bb', + full_name='protobuf_unittest.TestAllTypes.NestedMessage.bb', + index=0, number=1, + type=5, cpp_type=1, label=1, + default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None), + ], + nested_types=[], + enum_types=[ + self.my_enum, + ], + extensions=[]) + self.my_method = descriptor.MethodDescriptor( + name='Bar', + full_name='protobuf_unittest.TestService.Bar', + index=0, + containing_service=None, + input_type=None, + output_type=None) + self.my_service = descriptor.ServiceDescriptor( + name='TestServiceWithOptions', + full_name='protobuf_unittest.TestServiceWithOptions', + index=0, + methods=[ + self.my_method + ]) + + def testEnumFixups(self): + self.assertEqual(self.my_enum, self.my_enum.values[0].type) + + def testContainingTypeFixups(self): + self.assertEqual(self.my_message, self.my_message.fields[0].containing_type) + self.assertEqual(self.my_message, self.my_enum.containing_type) + + def testContainingServiceFixups(self): + self.assertEqual(self.my_service, self.my_method.containing_service) + + def testGetOptions(self): + self.assertEqual(self.my_enum.GetOptions(), + descriptor_pb2.EnumOptions()) + self.assertEqual(self.my_enum.values[0].GetOptions(), + descriptor_pb2.EnumValueOptions()) + self.assertEqual(self.my_message.GetOptions(), + descriptor_pb2.MessageOptions()) + self.assertEqual(self.my_message.fields[0].GetOptions(), + descriptor_pb2.FieldOptions()) + self.assertEqual(self.my_method.GetOptions(), + descriptor_pb2.MethodOptions()) + self.assertEqual(self.my_service.GetOptions(), + descriptor_pb2.ServiceOptions()) + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/encoder.py b/python/google/protobuf/internal/encoder.py new file mode 100755 index 00000000..29c78b23 --- /dev/null +++ b/python/google/protobuf/internal/encoder.py @@ -0,0 +1,192 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Class for encoding protocol message primitives. + +Contains the logic for encoding every logical protocol field type +into one of the 5 physical wire types. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format +from google.protobuf.internal import output_stream + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by WireFormat from the C++ proto2 +# implementation. + + +class Encoder(object): + + """Encodes logical protocol buffer fields to the wire format.""" + + def __init__(self): + self._stream = output_stream.OutputStream() + + def ToString(self): + """Returns all values encoded in this object as a string.""" + return self._stream.ToString() + + # All the Append*() methods below first append a tag+type pair to the buffer + # before appending the specified value. + + def AppendInt32(self, field_number, value): + """Appends a 32-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarint32(value) + + def AppendInt64(self, field_number, value): + """Appends a 64-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarint64(value) + + def AppendUInt32(self, field_number, unsigned_value): + """Appends an unsigned 32-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarUInt32(unsigned_value) + + def AppendUInt64(self, field_number, unsigned_value): + """Appends an unsigned 64-bit integer to our buffer, varint-encoded.""" + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + self._stream.AppendVarUInt64(unsigned_value) + + def AppendSInt32(self, field_number, value): + """Appends a 32-bit integer to our buffer, zigzag-encoded and then + varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + zigzag_value = wire_format.ZigZagEncode(value) + self._stream.AppendVarUInt32(zigzag_value) + + def AppendSInt64(self, field_number, value): + """Appends a 64-bit integer to our buffer, zigzag-encoded and then + varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_VARINT) + zigzag_value = wire_format.ZigZagEncode(value) + self._stream.AppendVarUInt64(zigzag_value) + + def AppendFixed32(self, field_number, unsigned_value): + """Appends an unsigned 32-bit integer to our buffer, in little-endian + byte-order. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendLittleEndian32(unsigned_value) + + def AppendFixed64(self, field_number, unsigned_value): + """Appends an unsigned 64-bit integer to our buffer, in little-endian + byte-order. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendLittleEndian64(unsigned_value) + + def AppendSFixed32(self, field_number, value): + """Appends a signed 32-bit integer to our buffer, in little-endian + byte-order. + """ + sign = (value & 0x80000000) and -1 or 0 + if value >> 32 != sign: + raise message.EncodeError('SFixed32 out of range: %d' % value) + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendLittleEndian32(value & 0xffffffff) + + def AppendSFixed64(self, field_number, value): + """Appends a signed 64-bit integer to our buffer, in little-endian + byte-order. + """ + sign = (value & 0x8000000000000000) and -1 or 0 + if value >> 64 != sign: + raise message.EncodeError('SFixed64 out of range: %d' % value) + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendLittleEndian64(value & 0xffffffffffffffff) + + def AppendFloat(self, field_number, value): + """Appends a floating-point number to our buffer.""" + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED32) + self._stream.AppendRawBytes(struct.pack('f', value)) + + def AppendDouble(self, field_number, value): + """Appends a double-precision floating-point number to our buffer.""" + self._AppendTag(field_number, wire_format.WIRETYPE_FIXED64) + self._stream.AppendRawBytes(struct.pack('d', value)) + + def AppendBool(self, field_number, value): + """Appends a boolean to our buffer.""" + self.AppendInt32(field_number, value) + + def AppendEnum(self, field_number, value): + """Appends an enum value to our buffer.""" + self.AppendInt32(field_number, value) + + def AppendString(self, field_number, value): + """Appends a length-prefixed string to our buffer, with the + length varint-encoded. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + self._stream.AppendVarUInt32(len(value)) + self._stream.AppendRawBytes(value) + + def AppendBytes(self, field_number, value): + """Appends a length-prefixed sequence of bytes to our buffer, with the + length varint-encoded. + """ + self.AppendString(field_number, value) + + # TODO(robinson): For AppendGroup() and AppendMessage(), we'd really like to + # avoid the extra string copy here. We can do so if we widen the Message + # interface to be able to serialize to a stream in addition to a string. The + # challenge when thinking ahead to the Python/C API implementation of Message + # is finding a stream-like Python thing to which we can write raw bytes + # from C. I'm not sure such a thing exists(?). (array.array is pretty much + # what we want, but it's not directly exposed in the Python/C API). + + def AppendGroup(self, field_number, group): + """Appends a group to our buffer. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_START_GROUP) + self._stream.AppendRawBytes(group.SerializeToString()) + self._AppendTag(field_number, wire_format.WIRETYPE_END_GROUP) + + def AppendMessage(self, field_number, msg): + """Appends a nested message to our buffer. + """ + self._AppendTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + self._stream.AppendVarUInt32(msg.ByteSize()) + self._stream.AppendRawBytes(msg.SerializeToString()) + + def AppendMessageSetItem(self, field_number, msg): + """Appends an item using the message set wire format. + + The message set message looks like this: + message MessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required string message = 3; + } + } + """ + self._AppendTag(1, wire_format.WIRETYPE_START_GROUP) + self.AppendInt32(2, field_number) + self.AppendMessage(3, msg) + self._AppendTag(1, wire_format.WIRETYPE_END_GROUP) + + def _AppendTag(self, field_number, wire_type): + """Appends a tag containing field number and wire type information.""" + self._stream.AppendVarUInt32(wire_format.PackTag(field_number, wire_type)) diff --git a/python/google/protobuf/internal/encoder_test.py b/python/google/protobuf/internal/encoder_test.py new file mode 100755 index 00000000..be4276b6 --- /dev/null +++ b/python/google/protobuf/internal/encoder_test.py @@ -0,0 +1,211 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.internal.encoder.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +import logging +import unittest +from google.protobuf.internal import wire_format +from google.protobuf.internal import encoder +from google.protobuf.internal import output_stream +from google.protobuf import message +import mox + + +class EncoderTest(unittest.TestCase): + + def setUp(self): + self.mox = mox.Mox() + self.encoder = encoder.Encoder() + self.mock_stream = self.mox.CreateMock(output_stream.OutputStream) + self.mock_message = self.mox.CreateMock(message.Message) + self.encoder._stream = self.mock_stream + + def PackTag(self, field_number, wire_type): + return wire_format.PackTag(field_number, wire_type) + + def AppendScalarTestHelper(self, test_name, encoder_method, + expected_stream_method_name, + wire_type, field_value, expected_value=None): + """Helper for testAppendScalars. + + Calls one of the Encoder methods, and ensures that the Encoder + in turn makes the expected calls into its OutputStream. + + Args: + test_name: Name of this test, used only for logging. + encoder_method: Callable on self.encoder, which should + accept |field_value| as an argument. This is the Encoder + method we're testing. + expected_stream_method_name: (string) Name of the OutputStream + method we expect Encoder to call to actually put the value + on the wire. + wire_type: The WIRETYPE_* constant we expect encoder to + use in the specified encoder_method. + field_value: The value we're trying to encode. Passed + into encoder_method. + expected_value: The value we expect Encoder to pass into + the OutputStream method. If None, we expect field_value + to pass through unmodified. + """ + if expected_value is None: + expected_value = field_value + + logging.info('Testing %s scalar output.\n' + 'Calling %r(%r), and expecting that to call the ' + 'stream method %s(%r).' % ( + test_name, encoder_method, field_value, + expected_stream_method_name, expected_value)) + + field_number = 10 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32(self.PackTag(field_number, wire_type)) + # If we're length-delimited, we should then append the length. + if wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + self.mock_stream.AppendVarUInt32(len(field_value)) + # Should then append the value itself. + # We have to use names instead of methods to work around some + # mox weirdness. (ResetAll() is overzealous). + expected_stream_method = getattr(self.mock_stream, + expected_stream_method_name) + expected_stream_method(expected_value) + + self.mox.ReplayAll() + encoder_method(field_number, field_value) + self.mox.VerifyAll() + self.mox.ResetAll() + + def testAppendScalars(self): + scalar_tests = [ + ['int32', self.encoder.AppendInt32, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, 0], + ['int64', self.encoder.AppendInt64, 'AppendVarint64', + wire_format.WIRETYPE_VARINT, 0], + ['uint32', self.encoder.AppendUInt32, 'AppendVarUInt32', + wire_format.WIRETYPE_VARINT, 0], + ['uint64', self.encoder.AppendUInt64, 'AppendVarUInt64', + wire_format.WIRETYPE_VARINT, 0], + ['fixed32', self.encoder.AppendFixed32, 'AppendLittleEndian32', + wire_format.WIRETYPE_FIXED32, 0], + ['fixed64', self.encoder.AppendFixed64, 'AppendLittleEndian64', + wire_format.WIRETYPE_FIXED64, 0], + ['sfixed32', self.encoder.AppendSFixed32, 'AppendLittleEndian32', + wire_format.WIRETYPE_FIXED32, -1, 0xffffffff], + ['sfixed64', self.encoder.AppendSFixed64, 'AppendLittleEndian64', + wire_format.WIRETYPE_FIXED64, -1, 0xffffffffffffffff], + ['float', self.encoder.AppendFloat, 'AppendRawBytes', + wire_format.WIRETYPE_FIXED32, 0.0, struct.pack('f', 0.0)], + ['double', self.encoder.AppendDouble, 'AppendRawBytes', + wire_format.WIRETYPE_FIXED64, 0.0, struct.pack('d', 0.0)], + ['bool', self.encoder.AppendBool, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, False], + ['enum', self.encoder.AppendEnum, 'AppendVarint32', + wire_format.WIRETYPE_VARINT, 0], + ['string', self.encoder.AppendString, 'AppendRawBytes', + wire_format.WIRETYPE_LENGTH_DELIMITED, + "You're in a maze of twisty little passages, all alike."], + # We test zigzag encoding routines more extensively below. + ['sint32', self.encoder.AppendSInt32, 'AppendVarUInt32', + wire_format.WIRETYPE_VARINT, -1, 1], + ['sint64', self.encoder.AppendSInt64, 'AppendVarUInt64', + wire_format.WIRETYPE_VARINT, -1, 1], + ] + # Ensure that we're testing different Encoder methods and using + # different test names in all test cases above. + self.assertEqual(len(scalar_tests), len(set(t[0] for t in scalar_tests))) + self.assertEqual(len(scalar_tests), len(set(t[1] for t in scalar_tests))) + for args in scalar_tests: + self.AppendScalarTestHelper(*args) + + def testAppendGroup(self): + field_number = 23 + # Should first append the start-group marker. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_START_GROUP)) + # Should then serialize itself. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + # Should finally append the end-group marker. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_END_GROUP)) + + self.mox.ReplayAll() + self.encoder.AppendGroup(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendMessage(self): + field_number = 23 + byte_size = 42 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32( + self.PackTag(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED)) + # Should then append its length. + self.mock_message.ByteSize().AndReturn(byte_size) + self.mock_stream.AppendVarUInt32(byte_size) + # Should then serialize itself to the encoder. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + + self.mox.ReplayAll() + self.encoder.AppendMessage(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendMessageSetItem(self): + field_number = 23 + byte_size = 42 + # Should first append the field number and type information. + self.mock_stream.AppendVarUInt32( + self.PackTag(1, wire_format.WIRETYPE_START_GROUP)) + self.mock_stream.AppendVarUInt32( + self.PackTag(2, wire_format.WIRETYPE_VARINT)) + self.mock_stream.AppendVarint32(field_number) + self.mock_stream.AppendVarUInt32( + self.PackTag(3, wire_format.WIRETYPE_LENGTH_DELIMITED)) + # Should then append its length. + self.mock_message.ByteSize().AndReturn(byte_size) + self.mock_stream.AppendVarUInt32(byte_size) + # Should then serialize itself to the encoder. + self.mock_message.SerializeToString().AndReturn('foo') + self.mock_stream.AppendRawBytes('foo') + self.mock_stream.AppendVarUInt32( + self.PackTag(1, wire_format.WIRETYPE_END_GROUP)) + + self.mox.ReplayAll() + self.encoder.AppendMessageSetItem(field_number, self.mock_message) + self.mox.VerifyAll() + + def testAppendSFixed(self): + # Most of our bounds-checking is done in output_stream.py, + # but encoder.py is responsible for transforming signed + # fixed-width integers into unsigned ones, so we test here + # to ensure that we're not losing any entropy when we do + # that conversion. + field_number = 10 + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed32, + 10, wire_format.UINT32_MAX + 1) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed32, + 10, -(1 << 32)) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed64, + 10, wire_format.UINT64_MAX + 1) + self.assertRaises(message.EncodeError, self.encoder.AppendSFixed64, + 10, -(1 << 64)) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/generator_test.py b/python/google/protobuf/internal/generator_test.py new file mode 100755 index 00000000..02f993f7 --- /dev/null +++ b/python/google/protobuf/internal/generator_test.py @@ -0,0 +1,84 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +# TODO(robinson): Flesh this out considerably. We focused on reflection_test.py +# first, since it's testing the subtler code, and since it provides decent +# indirect testing of the protocol compiler output. + +"""Unittest that directly tests the output of the pure-Python protocol +compiler. See //net/proto2/internal/reflection_test.py for a test which +further ensures that we can use Python protocol message objects as we expect. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 + + +class GeneratorTest(unittest.TestCase): + + def testNestedMessageDescriptor(self): + field_name = 'optional_nested_message' + proto_type = unittest_pb2.TestAllTypes + self.assertEqual( + proto_type.NestedMessage.DESCRIPTOR, + proto_type.DESCRIPTOR.fields_by_name[field_name].message_type) + + def testEnums(self): + # We test only module-level enums here. + # TODO(robinson): Examine descriptors directly to check + # enum descriptor output. + self.assertEqual(4, unittest_pb2.FOREIGN_FOO) + self.assertEqual(5, unittest_pb2.FOREIGN_BAR) + self.assertEqual(6, unittest_pb2.FOREIGN_BAZ) + + proto = unittest_pb2.TestAllTypes() + self.assertEqual(1, proto.FOO) + self.assertEqual(1, unittest_pb2.TestAllTypes.FOO) + self.assertEqual(2, proto.BAR) + self.assertEqual(2, unittest_pb2.TestAllTypes.BAR) + self.assertEqual(3, proto.BAZ) + self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ) + + def testContainingTypeBehaviorForExtensions(self): + self.assertEqual(unittest_pb2.optional_int32_extension.containing_type, + unittest_pb2.TestAllExtensions.DESCRIPTOR) + self.assertEqual(unittest_pb2.TestRequired.single.containing_type, + unittest_pb2.TestAllExtensions.DESCRIPTOR) + + def testExtensionScope(self): + self.assertEqual(unittest_pb2.optional_int32_extension.extension_scope, + None) + self.assertEqual(unittest_pb2.TestRequired.single.extension_scope, + unittest_pb2.TestRequired.DESCRIPTOR) + + def testIsExtension(self): + self.assertTrue(unittest_pb2.optional_int32_extension.is_extension) + self.assertTrue(unittest_pb2.TestRequired.single.is_extension) + + message_descriptor = unittest_pb2.TestRequired.DESCRIPTOR + non_extension_descriptor = message_descriptor.fields_by_name['a'] + self.assertTrue(not non_extension_descriptor.is_extension) + + def testOptions(self): + proto = unittest_mset_pb2.TestMessageSet() + self.assertTrue(proto.DESCRIPTOR.GetOptions().message_set_wire_format) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/input_stream.py b/python/google/protobuf/internal/input_stream.py new file mode 100755 index 00000000..9f3b0f5a --- /dev/null +++ b/python/google/protobuf/internal/input_stream.py @@ -0,0 +1,211 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""InputStream is the primitive interface for reading bits from the wire. + +All protocol buffer deserialization can be expressed in terms of +the InputStream primitives provided here. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by CodedInputStream from the C++ +# proto2 implementation. + + +class InputStream(object): + + """Contains all logic for reading bits, and dealing with stream position. + + If an InputStream method ever raises an exception, the stream is left + in an indeterminate state and is not safe for further use. + """ + + def __init__(self, s): + # What we really want is something like array('B', s), where elements we + # read from the array are already given to us as one-byte integers. BUT + # using array() instead of buffer() would force full string copies to result + # from each GetSubBuffer() call. + # + # So, if the N serialized bytes of a single protocol buffer object are + # split evenly between 2 child messages, and so on recursively, using + # array('B', s) instead of buffer() would incur an additional N*logN bytes + # copied during deserialization. + # + # The higher constant overhead of having to ord() for every byte we read + # from the buffer in _ReadVarintHelper() could definitely lead to worse + # performance in many real-world scenarios, even if the asymptotic + # complexity is better. However, our real answer is that the mythical + # Python/C extension module output mode for the protocol compiler will + # be blazing-fast and will eliminate most use of this class anyway. + self._buffer = buffer(s) + self._pos = 0 + + def EndOfStream(self): + """Returns true iff we're at the end of the stream. + If this returns true, then a call to any other InputStream method + will raise an exception. + """ + return self._pos >= len(self._buffer) + + def Position(self): + """Returns the current position in the stream, or equivalently, the + number of bytes read so far. + """ + return self._pos + + def GetSubBuffer(self, size=None): + """Returns a sequence-like object that represents a portion of our + underlying sequence. + + Position 0 in the returned object corresponds to self.Position() + in this stream. + + If size is specified, then the returned object ends after the + next "size" bytes in this stream. If size is not specified, + then the returned object ends at the end of this stream. + + We guarantee that the returned object R supports the Python buffer + interface (and thus that the call buffer(R) will work). + + Note that the returned buffer is read-only. + + The intended use for this method is for nested-message and nested-group + deserialization, where we want to make a recursive MergeFromString() + call on the portion of the original sequence that contains the serialized + nested message. (And we'd like to do so without making unnecessary string + copies). + + REQUIRES: size is nonnegative. + """ + # Note that buffer() doesn't perform any actual string copy. + if size is None: + return buffer(self._buffer, self._pos) + else: + if size < 0: + raise message.DecodeError('Negative size %d' % size) + return buffer(self._buffer, self._pos, size) + + def SkipBytes(self, num_bytes): + """Skip num_bytes bytes ahead, or go to the end of the stream, whichever + comes first. + + REQUIRES: num_bytes is nonnegative. + """ + if num_bytes < 0: + raise message.DecodeError('Negative num_bytes %d' % num_bytes) + self._pos += num_bytes + self._pos = min(self._pos, len(self._buffer)) + + def ReadString(self, size): + """Reads up to 'size' bytes from the stream, stopping early + only if we reach the end of the stream. Returns the bytes read + as a string. + """ + if size < 0: + raise message.DecodeError('Negative size %d' % size) + s = (self._buffer[self._pos : self._pos + size]) + self._pos += len(s) # Only advance by the number of bytes actually read. + return s + + def ReadLittleEndian32(self): + """Interprets the next 4 bytes of the stream as a little-endian + encoded, unsiged 32-bit integer, and returns that integer. + """ + try: + i = struct.unpack(wire_format.FORMAT_UINT32_LITTLE_ENDIAN, + self._buffer[self._pos : self._pos + 4]) + self._pos += 4 + return i[0] # unpack() result is a 1-element tuple. + except struct.error, e: + raise message.DecodeError(e) + + def ReadLittleEndian64(self): + """Interprets the next 8 bytes of the stream as a little-endian + encoded, unsiged 64-bit integer, and returns that integer. + """ + try: + i = struct.unpack(wire_format.FORMAT_UINT64_LITTLE_ENDIAN, + self._buffer[self._pos : self._pos + 8]) + self._pos += 8 + return i[0] # unpack() result is a 1-element tuple. + except struct.error, e: + raise message.DecodeError(e) + + def ReadVarint32(self): + """Reads a varint from the stream, interprets this varint + as a signed, 32-bit integer, and returns the integer. + """ + i = self.ReadVarint64() + if not wire_format.INT32_MIN <= i <= wire_format.INT32_MAX: + raise message.DecodeError('Value out of range for int32: %d' % i) + return int(i) + + def ReadVarUInt32(self): + """Reads a varint from the stream, interprets this varint + as an unsigned, 32-bit integer, and returns the integer. + """ + i = self.ReadVarUInt64() + if i > wire_format.UINT32_MAX: + raise message.DecodeError('Value out of range for uint32: %d' % i) + return i + + def ReadVarint64(self): + """Reads a varint from the stream, interprets this varint + as a signed, 64-bit integer, and returns the integer. + """ + i = self.ReadVarUInt64() + if i > wire_format.INT64_MAX: + i -= (1 << 64) + return i + + def ReadVarUInt64(self): + """Reads a varint from the stream, interprets this varint + as an unsigned, 64-bit integer, and returns the integer. + """ + i = self._ReadVarintHelper() + if not 0 <= i <= wire_format.UINT64_MAX: + raise message.DecodeError('Value out of range for uint64: %d' % i) + return i + + def _ReadVarintHelper(self): + """Helper for the various varint-reading methods above. + Reads an unsigned, varint-encoded integer from the stream and + returns this integer. + + Does no bounds checking except to ensure that we read at most as many bytes + as could possibly be present in a varint-encoded 64-bit number. + """ + result = 0 + shift = 0 + while 1: + if shift >= 64: + raise message.DecodeError('Too many bytes when decoding varint.') + try: + b = ord(self._buffer[self._pos]) + except IndexError: + raise message.DecodeError('Truncated varint.') + self._pos += 1 + result |= ((b & 0x7f) << shift) + shift += 7 + if not (b & 0x80): + return result diff --git a/python/google/protobuf/internal/input_stream_test.py b/python/google/protobuf/internal/input_stream_test.py new file mode 100755 index 00000000..2d685545 --- /dev/null +++ b/python/google/protobuf/internal/input_stream_test.py @@ -0,0 +1,279 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.internal.input_stream.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import wire_format +from google.protobuf.internal import input_stream + + +class InputStreamTest(unittest.TestCase): + + def testEndOfStream(self): + stream = input_stream.InputStream('abcd') + self.assertFalse(stream.EndOfStream()) + self.assertEqual('abcd', stream.ReadString(10)) + self.assertTrue(stream.EndOfStream()) + + def testPosition(self): + stream = input_stream.InputStream('abcd') + self.assertEqual(0, stream.Position()) + self.assertEqual(0, stream.Position()) # No side-effects. + stream.ReadString(1) + self.assertEqual(1, stream.Position()) + stream.ReadString(1) + self.assertEqual(2, stream.Position()) + stream.ReadString(10) + self.assertEqual(4, stream.Position()) # Can't go past end of stream. + + def testGetSubBuffer(self): + stream = input_stream.InputStream('abcd') + # Try leaving out the size. + self.assertEqual('abcd', str(stream.GetSubBuffer())) + stream.SkipBytes(1) + # GetSubBuffer() always starts at current size. + self.assertEqual('bcd', str(stream.GetSubBuffer())) + # Try 0-size. + self.assertEqual('', str(stream.GetSubBuffer(0))) + # Negative sizes should raise an error. + self.assertRaises(message.DecodeError, stream.GetSubBuffer, -1) + # Positive sizes should work as expected. + self.assertEqual('b', str(stream.GetSubBuffer(1))) + self.assertEqual('bc', str(stream.GetSubBuffer(2))) + # Sizes longer than remaining bytes in the buffer should + # return the whole remaining buffer. + self.assertEqual('bcd', str(stream.GetSubBuffer(1000))) + + def testSkipBytes(self): + stream = input_stream.InputStream('') + # Skipping bytes when at the end of stream + # should have no effect. + stream.SkipBytes(0) + stream.SkipBytes(1) + stream.SkipBytes(2) + self.assertTrue(stream.EndOfStream()) + self.assertEqual(0, stream.Position()) + + # Try skipping within a stream. + stream = input_stream.InputStream('abcd') + self.assertEqual(0, stream.Position()) + stream.SkipBytes(1) + self.assertEqual(1, stream.Position()) + stream.SkipBytes(10) # Can't skip past the end. + self.assertEqual(4, stream.Position()) + + # Ensure that a negative skip raises an exception. + stream = input_stream.InputStream('abcd') + stream.SkipBytes(1) + self.assertRaises(message.DecodeError, stream.SkipBytes, -1) + + def testReadString(self): + s = 'abcd' + # Also test going past the total stream length. + for i in range(len(s) + 10): + stream = input_stream.InputStream(s) + self.assertEqual(s[:i], stream.ReadString(i)) + self.assertEqual(min(i, len(s)), stream.Position()) + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadString, -1) + + def EnsureFailureOnEmptyStream(self, input_stream_method): + """Helper for integer-parsing tests below. + Ensures that the given InputStream method raises a DecodeError + if called on a stream with no bytes remaining. + """ + stream = input_stream.InputStream('') + self.assertRaises(message.DecodeError, input_stream_method, stream) + + def testReadLittleEndian32(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadLittleEndian32) + s = '' + # Read 0. + s += '\x00\x00\x00\x00' + # Read 1. + s += '\x01\x00\x00\x00' + # Read a bunch of different bytes. + s += '\x01\x02\x03\x04' + # Read max unsigned 32-bit int. + s += '\xff\xff\xff\xff' + # Try a read with fewer than 4 bytes left in the stream. + s += '\x00\x00\x00' + stream = input_stream.InputStream(s) + self.assertEqual(0, stream.ReadLittleEndian32()) + self.assertEqual(4, stream.Position()) + self.assertEqual(1, stream.ReadLittleEndian32()) + self.assertEqual(8, stream.Position()) + self.assertEqual(0x04030201, stream.ReadLittleEndian32()) + self.assertEqual(12, stream.Position()) + self.assertEqual(wire_format.UINT32_MAX, stream.ReadLittleEndian32()) + self.assertEqual(16, stream.Position()) + self.assertRaises(message.DecodeError, stream.ReadLittleEndian32) + + def testReadLittleEndian64(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadLittleEndian64) + s = '' + # Read 0. + s += '\x00\x00\x00\x00\x00\x00\x00\x00' + # Read 1. + s += '\x01\x00\x00\x00\x00\x00\x00\x00' + # Read a bunch of different bytes. + s += '\x01\x02\x03\x04\x05\x06\x07\x08' + # Read max unsigned 64-bit int. + s += '\xff\xff\xff\xff\xff\xff\xff\xff' + # Try a read with fewer than 8 bytes left in the stream. + s += '\x00\x00\x00' + stream = input_stream.InputStream(s) + self.assertEqual(0, stream.ReadLittleEndian64()) + self.assertEqual(8, stream.Position()) + self.assertEqual(1, stream.ReadLittleEndian64()) + self.assertEqual(16, stream.Position()) + self.assertEqual(0x0807060504030201, stream.ReadLittleEndian64()) + self.assertEqual(24, stream.Position()) + self.assertEqual(wire_format.UINT64_MAX, stream.ReadLittleEndian64()) + self.assertEqual(32, stream.Position()) + self.assertRaises(message.DecodeError, stream.ReadLittleEndian64) + + def ReadVarintSuccessTestHelper(self, varints_and_ints, read_method): + """Helper for tests below that test successful reads of various varints. + + Args: + varints_and_ints: Iterable of (str, integer) pairs, where the string + gives the wire encoding and the integer gives the value we expect + to be returned by the read_method upon encountering this string. + read_method: Unbound InputStream method that is capable of reading + the encoded strings provided in the first elements of varints_and_ints. + """ + s = ''.join(s for s, i in varints_and_ints) + stream = input_stream.InputStream(s) + expected_pos = 0 + self.assertEqual(expected_pos, stream.Position()) + for s, expected_int in varints_and_ints: + self.assertEqual(expected_int, read_method(stream)) + expected_pos += len(s) + self.assertEqual(expected_pos, stream.Position()) + + def testReadVarint32Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01', -1), + ('\xff\xff\xff\xff\x07', wire_format.INT32_MAX), + ('\x80\x80\x80\x80\xf8\xff\xff\xff\xff\x01', wire_format.INT32_MIN), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarint32) + + def testReadVarint32Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarint32) + + # Try and fail to read INT32_MAX + 1. + s = '\x80\x80\x80\x80\x08' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + # Try and fail to read INT32_MIN - 1. + s = '\xfe\xff\xff\xff\xf7\xff\xff\xff\xff\x01' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint32) + + def testReadVarUInt32Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\x0f', wire_format.UINT32_MAX), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarUInt32) + + def testReadVarUInt32Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarUInt32) + # Try and fail to read UINT32_MAX + 1 + s = '\x80\x80\x80\x80\x10' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt32) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt32) + + def testReadVarint64Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01', -1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\xff\xff\xff\xff\xff\xff\xff\xff\x7f', wire_format.INT64_MAX), + ('\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01', wire_format.INT64_MIN), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarint64) + + def testReadVarint64Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarint64) + # Try and fail to read something with the mythical 64th bit set. + s = '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint64) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarint64) + + def testReadVarUInt64Success(self): + varints_and_ints = [ + ('\x00', 0), + ('\x01', 1), + ('\x7f', 127), + ('\x80\x01', 128), + ('\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01', 1 << 63), + ] + self.ReadVarintSuccessTestHelper(varints_and_ints, + input_stream.InputStream.ReadVarUInt64) + + def testReadVarUInt64Failure(self): + self.EnsureFailureOnEmptyStream(input_stream.InputStream.ReadVarUInt64) + # Try and fail to read something with the mythical 64th bit set. + s = '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x02' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt64) + + # Try and fail to read something that looks like + # a varint with more than 10 bytes. + s = '\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00' + stream = input_stream.InputStream(s) + self.assertRaises(message.DecodeError, stream.ReadVarUInt64) + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/message_listener.py b/python/google/protobuf/internal/message_listener.py new file mode 100755 index 00000000..3747909e --- /dev/null +++ b/python/google/protobuf/internal/message_listener.py @@ -0,0 +1,55 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Defines a listener interface for observing certain +state transitions on Message objects. + +Also defines a null implementation of this interface. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + + +class MessageListener(object): + + """Listens for transitions to nonempty and for invalidations of cached + byte sizes. Meant to be registered via Message._SetListener(). + """ + + def TransitionToNonempty(self): + """Called the *first* time that this message becomes nonempty. + Implementations are free (but not required) to call this method multiple + times after the message has become nonempty. + """ + raise NotImplementedError + + def ByteSizeDirty(self): + """Called *every* time the cached byte size value + for this object is invalidated (transitions from being + "clean" to "dirty"). + """ + raise NotImplementedError + + +class NullMessageListener(object): + + """No-op MessageListener implementation.""" + + def TransitionToNonempty(self): + pass + + def ByteSizeDirty(self): + pass diff --git a/python/google/protobuf/internal/more_extensions.proto b/python/google/protobuf/internal/more_extensions.proto new file mode 100644 index 00000000..48df6f55 --- /dev/null +++ b/python/google/protobuf/internal/more_extensions.proto @@ -0,0 +1,44 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: robinson@google.com (Will Robinson) + + +package google.protobuf.internal; + + +message TopLevelMessage { + optional ExtendedMessage submessage = 1; +} + + +message ExtendedMessage { + extensions 1 to max; +} + + +message ForeignMessage { + optional int32 foreign_message_int = 1; +} + + +extend ExtendedMessage { + optional int32 optional_int_extension = 1; + optional ForeignMessage optional_message_extension = 2; + + repeated int32 repeated_int_extension = 3; + repeated ForeignMessage repeated_message_extension = 4; +} diff --git a/python/google/protobuf/internal/more_messages.proto b/python/google/protobuf/internal/more_messages.proto new file mode 100644 index 00000000..bfa12273 --- /dev/null +++ b/python/google/protobuf/internal/more_messages.proto @@ -0,0 +1,37 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: robinson@google.com (Will Robinson) + + +package google.protobuf.internal; + +// A message where tag numbers are listed out of order, to allow us to test our +// canonicalization of serialized output, which should always be in tag order. +// We also mix in some extensions for extra fun. +message OutOfOrderFields { + optional sint32 optional_sint32 = 5; + extensions 4 to 4; + optional uint32 optional_uint32 = 3; + extensions 2 to 2; + optional int32 optional_int32 = 1; +}; + + +extend OutOfOrderFields { + optional uint64 optional_uint64 = 4; + optional int64 optional_int64 = 2; +} diff --git a/python/google/protobuf/internal/output_stream.py b/python/google/protobuf/internal/output_stream.py new file mode 100755 index 00000000..ccb91225 --- /dev/null +++ b/python/google/protobuf/internal/output_stream.py @@ -0,0 +1,111 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""OutputStream is the primitive interface for sticking bits on the wire. + +All protocol buffer serialization can be expressed in terms of +the OutputStream primitives provided here. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import array +import struct +from google.protobuf import message +from google.protobuf.internal import wire_format + + + +# Note that much of this code is ported from //net/proto/ProtocolBuffer, and +# that the interface is strongly inspired by CodedOutputStream from the C++ +# proto2 implementation. + + +class OutputStream(object): + + """Contains all logic for writing bits, and ToString() to get the result.""" + + def __init__(self): + self._buffer = array.array('B') + + def AppendRawBytes(self, raw_bytes): + """Appends raw_bytes to our internal buffer.""" + self._buffer.fromstring(raw_bytes) + + def AppendLittleEndian32(self, unsigned_value): + """Appends an unsigned 32-bit integer to the internal buffer, + in little-endian byte order. + """ + if not 0 <= unsigned_value <= wire_format.UINT32_MAX: + raise message.EncodeError( + 'Unsigned 32-bit out of range: %d' % unsigned_value) + self._buffer.fromstring(struct.pack( + wire_format.FORMAT_UINT32_LITTLE_ENDIAN, unsigned_value)) + + def AppendLittleEndian64(self, unsigned_value): + """Appends an unsigned 64-bit integer to the internal buffer, + in little-endian byte order. + """ + if not 0 <= unsigned_value <= wire_format.UINT64_MAX: + raise message.EncodeError( + 'Unsigned 64-bit out of range: %d' % unsigned_value) + self._buffer.fromstring(struct.pack( + wire_format.FORMAT_UINT64_LITTLE_ENDIAN, unsigned_value)) + + def AppendVarint32(self, value): + """Appends a signed 32-bit integer to the internal buffer, + encoded as a varint. (Note that a negative varint32 will + always require 10 bytes of space.) + """ + if not wire_format.INT32_MIN <= value <= wire_format.INT32_MAX: + raise message.EncodeError('Value out of range: %d' % value) + self.AppendVarint64(value) + + def AppendVarUInt32(self, value): + """Appends an unsigned 32-bit integer to the internal buffer, + encoded as a varint. + """ + if not 0 <= value <= wire_format.UINT32_MAX: + raise message.EncodeError('Value out of range: %d' % value) + self.AppendVarUInt64(value) + + def AppendVarint64(self, value): + """Appends a signed 64-bit integer to the internal buffer, + encoded as a varint. + """ + if not wire_format.INT64_MIN <= value <= wire_format.INT64_MAX: + raise message.EncodeError('Value out of range: %d' % value) + if value < 0: + value += (1 << 64) + self.AppendVarUInt64(value) + + def AppendVarUInt64(self, unsigned_value): + """Appends an unsigned 64-bit integer to the internal buffer, + encoded as a varint. + """ + if not 0 <= unsigned_value <= wire_format.UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % unsigned_value) + while True: + bits = unsigned_value & 0x7f + unsigned_value >>= 7 + if not unsigned_value: + self._buffer.append(bits) + break + self._buffer.append(0x80|bits) + + def ToString(self): + """Returns a string containing the bytes in our internal buffer.""" + return self._buffer.tostring() diff --git a/python/google/protobuf/internal/output_stream_test.py b/python/google/protobuf/internal/output_stream_test.py new file mode 100755 index 00000000..026f6161 --- /dev/null +++ b/python/google/protobuf/internal/output_stream_test.py @@ -0,0 +1,162 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.internal.output_stream.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import output_stream +from google.protobuf.internal import wire_format + + +class OutputStreamTest(unittest.TestCase): + + def setUp(self): + self.stream = output_stream.OutputStream() + + def testAppendRawBytes(self): + # Empty string. + self.stream.AppendRawBytes('') + self.assertEqual('', self.stream.ToString()) + + # Nonempty string. + self.stream.AppendRawBytes('abc') + self.assertEqual('abc', self.stream.ToString()) + + # Ensure that we're actually appending. + self.stream.AppendRawBytes('def') + self.assertEqual('abcdef', self.stream.ToString()) + + def AppendNumericTestHelper(self, append_fn, values_and_strings): + """For each (value, expected_string) pair in values_and_strings, + calls an OutputStream.Append*(value) method on an OutputStream and ensures + that the string written to that stream matches expected_string. + + Args: + append_fn: Unbound OutputStream method that takes an integer or + long value as input. + values_and_strings: Iterable of (value, expected_string) pairs. + """ + for conversion in (int, long): + for value, string in values_and_strings: + stream = output_stream.OutputStream() + expected_string = '' + append_fn(stream, conversion(value)) + expected_string += string + self.assertEqual(expected_string, stream.ToString()) + + def AppendOverflowTestHelper(self, append_fn, value): + """Calls an OutputStream.Append*(value) method and asserts + that the method raises message.EncodeError. + + Args: + append_fn: Unbound OutputStream method that takes an integer or + long value as input. + value: Value to pass to append_fn which should cause an + message.EncodeError. + """ + stream = output_stream.OutputStream() + self.assertRaises(message.EncodeError, append_fn, stream, value) + + def testAppendLittleEndian32(self): + append_fn = output_stream.OutputStream.AppendLittleEndian32 + values_and_expected_strings = [ + (0, '\x00\x00\x00\x00'), + (1, '\x01\x00\x00\x00'), + ((1 << 32) - 1, '\xff\xff\xff\xff'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, 1 << 32) + self.AppendOverflowTestHelper(append_fn, -1) + + def testAppendLittleEndian64(self): + append_fn = output_stream.OutputStream.AppendLittleEndian64 + values_and_expected_strings = [ + (0, '\x00\x00\x00\x00\x00\x00\x00\x00'), + (1, '\x01\x00\x00\x00\x00\x00\x00\x00'), + ((1 << 64) - 1, '\xff\xff\xff\xff\xff\xff\xff\xff'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, 1 << 64) + self.AppendOverflowTestHelper(append_fn, -1) + + def testAppendVarint32(self): + append_fn = output_stream.OutputStream.AppendVarint32 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (-1, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + (wire_format.INT32_MAX, '\xff\xff\xff\xff\x07'), + (wire_format.INT32_MIN, '\x80\x80\x80\x80\xf8\xff\xff\xff\xff\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, wire_format.INT32_MAX + 1) + self.AppendOverflowTestHelper(append_fn, wire_format.INT32_MIN - 1) + + def testAppendVarUInt32(self): + append_fn = output_stream.OutputStream.AppendVarUInt32 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (wire_format.UINT32_MAX, '\xff\xff\xff\xff\x0f'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, -1) + self.AppendOverflowTestHelper(append_fn, wire_format.UINT32_MAX + 1) + + def testAppendVarint64(self): + append_fn = output_stream.OutputStream.AppendVarint64 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (-1, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + (wire_format.INT64_MAX, '\xff\xff\xff\xff\xff\xff\xff\xff\x7f'), + (wire_format.INT64_MIN, '\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, wire_format.INT64_MAX + 1) + self.AppendOverflowTestHelper(append_fn, wire_format.INT64_MIN - 1) + + def testAppendVarUInt64(self): + append_fn = output_stream.OutputStream.AppendVarUInt64 + values_and_expected_strings = [ + (0, '\x00'), + (1, '\x01'), + (127, '\x7f'), + (128, '\x80\x01'), + (wire_format.UINT64_MAX, '\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01'), + ] + self.AppendNumericTestHelper(append_fn, values_and_expected_strings) + + self.AppendOverflowTestHelper(append_fn, -1) + self.AppendOverflowTestHelper(append_fn, wire_format.UINT64_MAX + 1) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/reflection_test.py b/python/google/protobuf/internal/reflection_test.py new file mode 100755 index 00000000..55777819 --- /dev/null +++ b/python/google/protobuf/internal/reflection_test.py @@ -0,0 +1,1514 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Unittest for reflection.py, which also indirectly tests the output of the +pure-Python protocol compiler. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import operator + +import unittest +# TODO(robinson): When we split this test in two, only some of these imports +# will be necessary in each test. +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_mset_pb2 +from google.protobuf import unittest_pb2 +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor +from google.protobuf import message +from google.protobuf import reflection +from google.protobuf.internal import more_extensions_pb2 +from google.protobuf.internal import more_messages_pb2 +from google.protobuf.internal import wire_format +from google.protobuf.internal import test_util +from google.protobuf.internal import decoder + + +class RefectionTest(unittest.TestCase): + + def testSimpleHasBits(self): + # Test a scalar. + proto = unittest_pb2.TestAllTypes() + self.assertTrue(not proto.HasField('optional_int32')) + self.assertEqual(0, proto.optional_int32) + # HasField() shouldn't be true if all we've done is + # read the default value. + self.assertTrue(not proto.HasField('optional_int32')) + proto.optional_int32 = 1 + # Setting a value however *should* set the "has" bit. + self.assertTrue(proto.HasField('optional_int32')) + proto.ClearField('optional_int32') + # And clearing that value should unset the "has" bit. + self.assertTrue(not proto.HasField('optional_int32')) + + def testHasBitsWithSinglyNestedScalar(self): + # Helper used to test foreign messages and groups. + # + # composite_field_name should be the name of a non-repeated + # composite (i.e., foreign or group) field in TestAllTypes, + # and scalar_field_name should be the name of an integer-valued + # scalar field within that composite. + # + # I never thought I'd miss C++ macros and templates so much. :( + # This helper is semantically just: + # + # assert proto.composite_field.scalar_field == 0 + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + # + # proto.composite_field.scalar_field = 10 + # old_composite_field = proto.composite_field + # + # assert proto.composite_field.scalar_field == 10 + # assert proto.composite_field.HasField('scalar_field') + # assert proto.HasField('composite_field') + # + # proto.ClearField('composite_field') + # + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + # assert proto.composite_field.scalar_field == 0 + # + # # Now ensure that ClearField('composite_field') disconnected + # # the old field object from the object tree... + # assert old_composite_field is not proto.composite_field + # old_composite_field.scalar_field = 20 + # assert not proto.composite_field.HasField('scalar_field') + # assert not proto.HasField('composite_field') + def TestCompositeHasBits(composite_field_name, scalar_field_name): + proto = unittest_pb2.TestAllTypes() + # First, check that we can get the scalar value, and see that it's the + # default (0), but that proto.HasField('omposite') and + # proto.composite.HasField('scalar') will still return False. + composite_field = getattr(proto, composite_field_name) + original_scalar_value = getattr(composite_field, scalar_field_name) + self.assertEqual(0, original_scalar_value) + # Assert that the composite object does not "have" the scalar. + self.assertTrue(not composite_field.HasField(scalar_field_name)) + # Assert that proto does not "have" the composite field. + self.assertTrue(not proto.HasField(composite_field_name)) + + # Now set the scalar within the composite field. Ensure that the setting + # is reflected, and that proto.HasField('composite') and + # proto.composite.HasField('scalar') now both return True. + new_val = 20 + setattr(composite_field, scalar_field_name, new_val) + self.assertEqual(new_val, getattr(composite_field, scalar_field_name)) + # Hold on to a reference to the current composite_field object. + old_composite_field = composite_field + # Assert that the has methods now return true. + self.assertTrue(composite_field.HasField(scalar_field_name)) + self.assertTrue(proto.HasField(composite_field_name)) + + # Now call the clear method... + proto.ClearField(composite_field_name) + + # ...and ensure that the "has" bits are all back to False... + composite_field = getattr(proto, composite_field_name) + self.assertTrue(not composite_field.HasField(scalar_field_name)) + self.assertTrue(not proto.HasField(composite_field_name)) + # ...and ensure that the scalar field has returned to its default. + self.assertEqual(0, getattr(composite_field, scalar_field_name)) + + # Finally, ensure that modifications to the old composite field object + # don't have any effect on the parent. + # + # (NOTE that when we clear the composite field in the parent, we actually + # don't recursively clear down the tree. Instead, we just disconnect the + # cleared composite from the tree.) + self.assertTrue(old_composite_field is not composite_field) + setattr(old_composite_field, scalar_field_name, new_val) + self.assertTrue(not composite_field.HasField(scalar_field_name)) + self.assertTrue(not proto.HasField(composite_field_name)) + self.assertEqual(0, getattr(composite_field, scalar_field_name)) + + # Test simple, single-level nesting when we set a scalar. + TestCompositeHasBits('optionalgroup', 'a') + TestCompositeHasBits('optional_nested_message', 'bb') + TestCompositeHasBits('optional_foreign_message', 'c') + TestCompositeHasBits('optional_import_message', 'd') + + def testReferencesToNestedMessage(self): + proto = unittest_pb2.TestAllTypes() + nested = proto.optional_nested_message + del proto + # A previous version had a bug where this would raise an exception when + # hitting a now-dead weak reference. + nested.bb = 23 + + def testDisconnectingNestedMessageBeforeSettingField(self): + proto = unittest_pb2.TestAllTypes() + nested = proto.optional_nested_message + proto.ClearField('optional_nested_message') # Should disconnect from parent + self.assertTrue(nested is not proto.optional_nested_message) + nested.bb = 23 + self.assertTrue(not proto.HasField('optional_nested_message')) + self.assertEqual(0, proto.optional_nested_message.bb) + + def testHasBitsWhenModifyingRepeatedFields(self): + # Test nesting when we add an element to a repeated field in a submessage. + proto = unittest_pb2.TestNestedMessageHasBits() + proto.optional_nested_message.nestedmessage_repeated_int32.append(5) + self.assertEqual( + [5], proto.optional_nested_message.nestedmessage_repeated_int32) + self.assertTrue(proto.HasField('optional_nested_message')) + + # Do the same test, but with a repeated composite field within the + # submessage. + proto.ClearField('optional_nested_message') + self.assertTrue(not proto.HasField('optional_nested_message')) + proto.optional_nested_message.nestedmessage_repeated_foreignmessage.add() + self.assertTrue(proto.HasField('optional_nested_message')) + + def testHasBitsForManyLevelsOfNesting(self): + # Test nesting many levels deep. + recursive_proto = unittest_pb2.TestMutualRecursionA() + self.assertTrue(not recursive_proto.HasField('bb')) + self.assertEqual(0, recursive_proto.bb.a.bb.a.bb.optional_int32) + self.assertTrue(not recursive_proto.HasField('bb')) + recursive_proto.bb.a.bb.a.bb.optional_int32 = 5 + self.assertEqual(5, recursive_proto.bb.a.bb.a.bb.optional_int32) + self.assertTrue(recursive_proto.HasField('bb')) + self.assertTrue(recursive_proto.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.HasField('bb')) + self.assertTrue(recursive_proto.bb.a.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.bb.a.HasField('bb')) + self.assertTrue(not recursive_proto.bb.a.bb.a.bb.HasField('a')) + self.assertTrue(recursive_proto.bb.a.bb.a.bb.HasField('optional_int32')) + + def testSingularListFields(self): + proto = unittest_pb2.TestAllTypes() + proto.optional_fixed32 = 1 + proto.optional_int32 = 5 + proto.optional_string = 'foo' + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['optional_int32' ], 5), + (proto.DESCRIPTOR.fields_by_name['optional_fixed32'], 1), + (proto.DESCRIPTOR.fields_by_name['optional_string' ], 'foo') ], + proto.ListFields()) + + def testRepeatedListFields(self): + proto = unittest_pb2.TestAllTypes() + proto.repeated_fixed32.append(1) + proto.repeated_int32.append(5) + proto.repeated_int32.append(11) + proto.repeated_string.append('foo') + proto.repeated_string.append('bar') + proto.repeated_string.append('baz') + proto.optional_int32 = 21 + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['optional_int32' ], 21), + (proto.DESCRIPTOR.fields_by_name['repeated_int32' ], [5, 11]), + (proto.DESCRIPTOR.fields_by_name['repeated_fixed32'], [1]), + (proto.DESCRIPTOR.fields_by_name['repeated_string' ], + ['foo', 'bar', 'baz']) ], + proto.ListFields()) + + def testSingularListExtensions(self): + proto = unittest_pb2.TestAllExtensions() + proto.Extensions[unittest_pb2.optional_fixed32_extension] = 1 + proto.Extensions[unittest_pb2.optional_int32_extension ] = 5 + proto.Extensions[unittest_pb2.optional_string_extension ] = 'foo' + self.assertEqual( + [ (unittest_pb2.optional_int32_extension , 5), + (unittest_pb2.optional_fixed32_extension, 1), + (unittest_pb2.optional_string_extension , 'foo') ], + proto.ListFields()) + + def testRepeatedListExtensions(self): + proto = unittest_pb2.TestAllExtensions() + proto.Extensions[unittest_pb2.repeated_fixed32_extension].append(1) + proto.Extensions[unittest_pb2.repeated_int32_extension ].append(5) + proto.Extensions[unittest_pb2.repeated_int32_extension ].append(11) + proto.Extensions[unittest_pb2.repeated_string_extension ].append('foo') + proto.Extensions[unittest_pb2.repeated_string_extension ].append('bar') + proto.Extensions[unittest_pb2.repeated_string_extension ].append('baz') + proto.Extensions[unittest_pb2.optional_int32_extension ] = 21 + self.assertEqual( + [ (unittest_pb2.optional_int32_extension , 21), + (unittest_pb2.repeated_int32_extension , [5, 11]), + (unittest_pb2.repeated_fixed32_extension, [1]), + (unittest_pb2.repeated_string_extension , ['foo', 'bar', 'baz']) ], + proto.ListFields()) + + def testListFieldsAndExtensions(self): + proto = unittest_pb2.TestFieldOrderings() + test_util.SetAllFieldsAndExtensions(proto) + unittest_pb2.my_extension_int + self.assertEqual( + [ (proto.DESCRIPTOR.fields_by_name['my_int' ], 1), + (unittest_pb2.my_extension_int , 23), + (proto.DESCRIPTOR.fields_by_name['my_string'], 'foo'), + (unittest_pb2.my_extension_string , 'bar'), + (proto.DESCRIPTOR.fields_by_name['my_float' ], 1.0) ], + proto.ListFields()) + + def testDefaultValues(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(0, proto.optional_int32) + self.assertEqual(0, proto.optional_int64) + self.assertEqual(0, proto.optional_uint32) + self.assertEqual(0, proto.optional_uint64) + self.assertEqual(0, proto.optional_sint32) + self.assertEqual(0, proto.optional_sint64) + self.assertEqual(0, proto.optional_fixed32) + self.assertEqual(0, proto.optional_fixed64) + self.assertEqual(0, proto.optional_sfixed32) + self.assertEqual(0, proto.optional_sfixed64) + self.assertEqual(0.0, proto.optional_float) + self.assertEqual(0.0, proto.optional_double) + self.assertEqual(False, proto.optional_bool) + self.assertEqual('', proto.optional_string) + self.assertEqual('', proto.optional_bytes) + + self.assertEqual(41, proto.default_int32) + self.assertEqual(42, proto.default_int64) + self.assertEqual(43, proto.default_uint32) + self.assertEqual(44, proto.default_uint64) + self.assertEqual(-45, proto.default_sint32) + self.assertEqual(46, proto.default_sint64) + self.assertEqual(47, proto.default_fixed32) + self.assertEqual(48, proto.default_fixed64) + self.assertEqual(49, proto.default_sfixed32) + self.assertEqual(-50, proto.default_sfixed64) + self.assertEqual(51.5, proto.default_float) + self.assertEqual(52e3, proto.default_double) + self.assertEqual(True, proto.default_bool) + self.assertEqual('hello', proto.default_string) + self.assertEqual('world', proto.default_bytes) + self.assertEqual(unittest_pb2.TestAllTypes.BAR, proto.default_nested_enum) + self.assertEqual(unittest_pb2.FOREIGN_BAR, proto.default_foreign_enum) + self.assertEqual(unittest_import_pb2.IMPORT_BAR, + proto.default_import_enum) + + def testHasFieldWithUnknownFieldName(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(ValueError, proto.HasField, 'nonexistent_field') + + def testClearFieldWithUnknownFieldName(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(ValueError, proto.ClearField, 'nonexistent_field') + + def testDisallowedAssignments(self): + # It's illegal to assign values directly to repeated fields + # or to nonrepeated composite fields. Ensure that this fails. + proto = unittest_pb2.TestAllTypes() + # Repeated fields. + self.assertRaises(AttributeError, setattr, proto, 'repeated_int32', 10) + # Lists shouldn't work, either. + self.assertRaises(AttributeError, setattr, proto, 'repeated_int32', [10]) + # Composite fields. + self.assertRaises(AttributeError, setattr, proto, + 'optional_nested_message', 23) + # proto.nonexistent_field = 23 should fail as well. + self.assertRaises(AttributeError, setattr, proto, 'nonexistent_field', 23) + + # TODO(robinson): Add type-safety check for enums. + def testSingleScalarTypeSafety(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(TypeError, setattr, proto, 'optional_int32', 1.1) + self.assertRaises(TypeError, setattr, proto, 'optional_int32', 'foo') + self.assertRaises(TypeError, setattr, proto, 'optional_string', 10) + self.assertRaises(TypeError, setattr, proto, 'optional_bytes', 10) + + def testSingleScalarBoundsChecking(self): + def TestMinAndMaxIntegers(field_name, expected_min, expected_max): + pb = unittest_pb2.TestAllTypes() + setattr(pb, field_name, expected_min) + setattr(pb, field_name, expected_max) + self.assertRaises(ValueError, setattr, pb, field_name, expected_min - 1) + self.assertRaises(ValueError, setattr, pb, field_name, expected_max + 1) + + TestMinAndMaxIntegers('optional_int32', -(1 << 31), (1 << 31) - 1) + TestMinAndMaxIntegers('optional_uint32', 0, 0xffffffff) + TestMinAndMaxIntegers('optional_int64', -(1 << 63), (1 << 63) - 1) + TestMinAndMaxIntegers('optional_uint64', 0, 0xffffffffffffffff) + TestMinAndMaxIntegers('optional_nested_enum', -(1 << 31), (1 << 31) - 1) + + def testRepeatedScalarTypeSafety(self): + proto = unittest_pb2.TestAllTypes() + self.assertRaises(TypeError, proto.repeated_int32.append, 1.1) + self.assertRaises(TypeError, proto.repeated_int32.append, 'foo') + self.assertRaises(TypeError, proto.repeated_string, 10) + self.assertRaises(TypeError, proto.repeated_bytes, 10) + + proto.repeated_int32.append(10) + proto.repeated_int32[0] = 23 + self.assertRaises(IndexError, proto.repeated_int32.__setitem__, 500, 23) + self.assertRaises(TypeError, proto.repeated_int32.__setitem__, 0, 'abc') + + def testSingleScalarGettersAndSetters(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(0, proto.optional_int32) + proto.optional_int32 = 1 + self.assertEqual(1, proto.optional_int32) + # TODO(robinson): Test all other scalar field types. + + def testSingleScalarClearField(self): + proto = unittest_pb2.TestAllTypes() + # Should be allowed to clear something that's not there (a no-op). + proto.ClearField('optional_int32') + proto.optional_int32 = 1 + self.assertTrue(proto.HasField('optional_int32')) + proto.ClearField('optional_int32') + self.assertEqual(0, proto.optional_int32) + self.assertTrue(not proto.HasField('optional_int32')) + # TODO(robinson): Test all other scalar field types. + + def testEnums(self): + proto = unittest_pb2.TestAllTypes() + self.assertEqual(1, proto.FOO) + self.assertEqual(1, unittest_pb2.TestAllTypes.FOO) + self.assertEqual(2, proto.BAR) + self.assertEqual(2, unittest_pb2.TestAllTypes.BAR) + self.assertEqual(3, proto.BAZ) + self.assertEqual(3, unittest_pb2.TestAllTypes.BAZ) + + def testRepeatedScalars(self): + proto = unittest_pb2.TestAllTypes() + + self.assertTrue(not proto.repeated_int32) + self.assertEqual(0, len(proto.repeated_int32)) + proto.repeated_int32.append(5); + proto.repeated_int32.append(10); + self.assertTrue(proto.repeated_int32) + self.assertEqual(2, len(proto.repeated_int32)) + + self.assertEqual([5, 10], proto.repeated_int32) + self.assertEqual(5, proto.repeated_int32[0]) + self.assertEqual(10, proto.repeated_int32[-1]) + # Test out-of-bounds indices. + self.assertRaises(IndexError, proto.repeated_int32.__getitem__, 1234) + self.assertRaises(IndexError, proto.repeated_int32.__getitem__, -1234) + # Test incorrect types passed to __getitem__. + self.assertRaises(TypeError, proto.repeated_int32.__getitem__, 'foo') + self.assertRaises(TypeError, proto.repeated_int32.__getitem__, None) + + # Test that we can use the field as an iterator. + result = [] + for i in proto.repeated_int32: + result.append(i) + self.assertEqual([5, 10], result) + + # Test clearing. + proto.ClearField('repeated_int32') + self.assertTrue(not proto.repeated_int32) + self.assertEqual(0, len(proto.repeated_int32)) + + def testRepeatedComposites(self): + proto = unittest_pb2.TestAllTypes() + self.assertTrue(not proto.repeated_nested_message) + self.assertEqual(0, len(proto.repeated_nested_message)) + m0 = proto.repeated_nested_message.add() + m1 = proto.repeated_nested_message.add() + self.assertTrue(proto.repeated_nested_message) + self.assertEqual(2, len(proto.repeated_nested_message)) + self.assertTrue(m0 is proto.repeated_nested_message[0]) + self.assertTrue(m1 is proto.repeated_nested_message[1]) + self.assertTrue(isinstance(m0, unittest_pb2.TestAllTypes.NestedMessage)) + + # Test out-of-bounds indices. + self.assertRaises(IndexError, proto.repeated_nested_message.__getitem__, + 1234) + self.assertRaises(IndexError, proto.repeated_nested_message.__getitem__, + -1234) + + # Test incorrect types passed to __getitem__. + self.assertRaises(TypeError, proto.repeated_nested_message.__getitem__, + 'foo') + self.assertRaises(TypeError, proto.repeated_nested_message.__getitem__, + None) + + # Test that we can use the field as an iterator. + result = [] + for i in proto.repeated_nested_message: + result.append(i) + self.assertEqual(2, len(result)) + self.assertTrue(m0 is result[0]) + self.assertTrue(m1 is result[1]) + + # Test clearing. + proto.ClearField('repeated_nested_message') + self.assertTrue(not proto.repeated_nested_message) + self.assertEqual(0, len(proto.repeated_nested_message)) + + def testHandWrittenReflection(self): + # TODO(robinson): We probably need a better way to specify + # protocol types by hand. But then again, this isn't something + # we expect many people to do. Hmm. + FieldDescriptor = descriptor.FieldDescriptor + foo_field_descriptor = FieldDescriptor( + name='foo_field', full_name='MyProto.foo_field', + index=0, number=1, type=FieldDescriptor.TYPE_INT64, + cpp_type=FieldDescriptor.CPPTYPE_INT64, + label=FieldDescriptor.LABEL_OPTIONAL, default_value=0, + containing_type=None, message_type=None, enum_type=None, + is_extension=False, extension_scope=None, + options=descriptor_pb2.FieldOptions()) + mydescriptor = descriptor.Descriptor( + name='MyProto', full_name='MyProto', filename='ignored', + containing_type=None, nested_types=[], enum_types=[], + fields=[foo_field_descriptor], extensions=[], + options=descriptor_pb2.MessageOptions()) + class MyProtoClass(message.Message): + DESCRIPTOR = mydescriptor + __metaclass__ = reflection.GeneratedProtocolMessageType + myproto_instance = MyProtoClass() + self.assertEqual(0, myproto_instance.foo_field) + self.assertTrue(not myproto_instance.HasField('foo_field')) + myproto_instance.foo_field = 23 + self.assertEqual(23, myproto_instance.foo_field) + self.assertTrue(myproto_instance.HasField('foo_field')) + + def testTopLevelExtensionsForOptionalScalar(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.optional_int32_extension + self.assertTrue(not extendee_proto.HasExtension(extension)) + self.assertEqual(0, extendee_proto.Extensions[extension]) + # As with normal scalar fields, just doing a read doesn't actually set the + # "has" bit. + self.assertTrue(not extendee_proto.HasExtension(extension)) + # Actually set the thing. + extendee_proto.Extensions[extension] = 23 + self.assertEqual(23, extendee_proto.Extensions[extension]) + self.assertTrue(extendee_proto.HasExtension(extension)) + # Ensure that clearing works as well. + extendee_proto.ClearExtension(extension) + self.assertEqual(0, extendee_proto.Extensions[extension]) + self.assertTrue(not extendee_proto.HasExtension(extension)) + + def testTopLevelExtensionsForRepeatedScalar(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.repeated_string_extension + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + extendee_proto.Extensions[extension].append('foo') + self.assertEqual(['foo'], extendee_proto.Extensions[extension]) + string_list = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + self.assertTrue(string_list is not extendee_proto.Extensions[extension]) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testTopLevelExtensionsForOptionalMessage(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.optional_foreign_message_extension + self.assertTrue(not extendee_proto.HasExtension(extension)) + self.assertEqual(0, extendee_proto.Extensions[extension].c) + # As with normal (non-extension) fields, merely reading from the + # thing shouldn't set the "has" bit. + self.assertTrue(not extendee_proto.HasExtension(extension)) + extendee_proto.Extensions[extension].c = 23 + self.assertEqual(23, extendee_proto.Extensions[extension].c) + self.assertTrue(extendee_proto.HasExtension(extension)) + # Save a reference here. + foreign_message = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertTrue(foreign_message is not extendee_proto.Extensions[extension]) + # Setting a field on foreign_message now shouldn't set + # any "has" bits on extendee_proto. + foreign_message.c = 42 + self.assertEqual(42, foreign_message.c) + self.assertTrue(foreign_message.HasField('c')) + self.assertTrue(not extendee_proto.HasExtension(extension)) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testTopLevelExtensionsForRepeatedMessage(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.repeatedgroup_extension + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + group = extendee_proto.Extensions[extension].add() + group.a = 23 + self.assertEqual(23, extendee_proto.Extensions[extension][0].a) + group.a = 42 + self.assertEqual(42, extendee_proto.Extensions[extension][0].a) + group_list = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + self.assertEqual(0, len(extendee_proto.Extensions[extension])) + self.assertTrue(group_list is not extendee_proto.Extensions[extension]) + # Shouldn't be allowed to do Extensions[extension] = 'a' + self.assertRaises(TypeError, operator.setitem, extendee_proto.Extensions, + extension, 'a') + + def testNestedExtensions(self): + extendee_proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.single + + # We just test the non-repeated case. + self.assertTrue(not extendee_proto.HasExtension(extension)) + required = extendee_proto.Extensions[extension] + self.assertEqual(0, required.a) + self.assertTrue(not extendee_proto.HasExtension(extension)) + required.a = 23 + self.assertEqual(23, extendee_proto.Extensions[extension].a) + self.assertTrue(extendee_proto.HasExtension(extension)) + extendee_proto.ClearExtension(extension) + self.assertTrue(required is not extendee_proto.Extensions[extension]) + self.assertTrue(not extendee_proto.HasExtension(extension)) + + # If message A directly contains message B, and + # a.HasField('b') is currently False, then mutating any + # extension in B should change a.HasField('b') to True + # (and so on up the object tree). + def testHasBitsForAncestorsOfExtendedMessage(self): + # Optional scalar extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension]) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension] = 23 + self.assertEqual(23, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_int_extension]) + self.assertTrue(toplevel.HasField('submessage')) + + # Repeated scalar extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual([], toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension]) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension].append(23) + self.assertEqual([23], toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_int_extension]) + self.assertTrue(toplevel.HasField('submessage')) + + # Optional message extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int) + self.assertTrue(not toplevel.HasField('submessage')) + toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int = 23 + self.assertEqual(23, toplevel.submessage.Extensions[ + more_extensions_pb2.optional_message_extension].foreign_message_int) + self.assertTrue(toplevel.HasField('submessage')) + + # Repeated message extension. + toplevel = more_extensions_pb2.TopLevelMessage() + self.assertTrue(not toplevel.HasField('submessage')) + self.assertEqual(0, len(toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension])) + self.assertTrue(not toplevel.HasField('submessage')) + foreign = toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension].add() + self.assertTrue(foreign is toplevel.submessage.Extensions[ + more_extensions_pb2.repeated_message_extension][0]) + self.assertTrue(toplevel.HasField('submessage')) + + def testDisconnectionAfterClearingEmptyMessage(self): + toplevel = more_extensions_pb2.TopLevelMessage() + extendee_proto = toplevel.submessage + extension = more_extensions_pb2.optional_message_extension + extension_proto = extendee_proto.Extensions[extension] + extendee_proto.ClearExtension(extension) + extension_proto.foreign_message_int = 23 + + self.assertTrue(not toplevel.HasField('submessage')) + self.assertTrue(extension_proto is not extendee_proto.Extensions[extension]) + + def testExtensionFailureModes(self): + extendee_proto = unittest_pb2.TestAllExtensions() + + # Try non-extension-handle arguments to HasExtension, + # ClearExtension(), and Extensions[]... + self.assertRaises(KeyError, extendee_proto.HasExtension, 1234) + self.assertRaises(KeyError, extendee_proto.ClearExtension, 1234) + self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__, 1234) + self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__, 1234, 5) + + # Try something that *is* an extension handle, just not for + # this message... + unknown_handle = more_extensions_pb2.optional_int_extension + self.assertRaises(KeyError, extendee_proto.HasExtension, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.ClearExtension, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.Extensions.__getitem__, + unknown_handle) + self.assertRaises(KeyError, extendee_proto.Extensions.__setitem__, + unknown_handle, 5) + + # Try call HasExtension() with a valid handle, but for a + # *repeated* field. (Just as with non-extension repeated + # fields, Has*() isn't supported for extension repeated fields). + self.assertRaises(KeyError, extendee_proto.HasExtension, + unittest_pb2.repeated_string_extension) + + def testMergeFromSingularField(self): + # Test merge with just a singular field. + proto1 = unittest_pb2.TestAllTypes() + proto1.optional_int32 = 1 + + proto2 = unittest_pb2.TestAllTypes() + # This shouldn't get overwritten. + proto2.optional_string = 'value' + + proto2.MergeFrom(proto1) + self.assertEqual(1, proto2.optional_int32) + self.assertEqual('value', proto2.optional_string) + + def testMergeFromRepeatedField(self): + # Test merge with just a repeated field. + proto1 = unittest_pb2.TestAllTypes() + proto1.repeated_int32.append(1) + proto1.repeated_int32.append(2) + + proto2 = unittest_pb2.TestAllTypes() + proto2.repeated_int32.append(0) + proto2.MergeFrom(proto1) + + self.assertEqual(0, proto2.repeated_int32[0]) + self.assertEqual(1, proto2.repeated_int32[1]) + self.assertEqual(2, proto2.repeated_int32[2]) + + def testMergeFromOptionalGroup(self): + # Test merge with an optional group. + proto1 = unittest_pb2.TestAllTypes() + proto1.optionalgroup.a = 12 + proto2 = unittest_pb2.TestAllTypes() + proto2.MergeFrom(proto1) + self.assertEqual(12, proto2.optionalgroup.a) + + def testMergeFromRepeatedNestedMessage(self): + # Test merge with a repeated nested message. + proto1 = unittest_pb2.TestAllTypes() + m = proto1.repeated_nested_message.add() + m.bb = 123 + m = proto1.repeated_nested_message.add() + m.bb = 321 + + proto2 = unittest_pb2.TestAllTypes() + m = proto2.repeated_nested_message.add() + m.bb = 999 + proto2.MergeFrom(proto1) + self.assertEqual(999, proto2.repeated_nested_message[0].bb) + self.assertEqual(123, proto2.repeated_nested_message[1].bb) + self.assertEqual(321, proto2.repeated_nested_message[2].bb) + + def testMergeFromAllFields(self): + # With all fields set. + proto1 = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto1) + proto2 = unittest_pb2.TestAllTypes() + proto2.MergeFrom(proto1) + + # Messages should be equal. + self.assertEqual(proto2, proto1) + + # Serialized string should be equal too. + string1 = proto1.SerializeToString() + string2 = proto2.SerializeToString() + self.assertEqual(string1, string2) + + def testMergeFromExtensionsSingular(self): + proto1 = unittest_pb2.TestAllExtensions() + proto1.Extensions[unittest_pb2.optional_int32_extension] = 1 + + proto2 = unittest_pb2.TestAllExtensions() + proto2.MergeFrom(proto1) + self.assertEqual( + 1, proto2.Extensions[unittest_pb2.optional_int32_extension]) + + def testMergeFromExtensionsRepeated(self): + proto1 = unittest_pb2.TestAllExtensions() + proto1.Extensions[unittest_pb2.repeated_int32_extension].append(1) + proto1.Extensions[unittest_pb2.repeated_int32_extension].append(2) + + proto2 = unittest_pb2.TestAllExtensions() + proto2.Extensions[unittest_pb2.repeated_int32_extension].append(0) + proto2.MergeFrom(proto1) + self.assertEqual( + 3, len(proto2.Extensions[unittest_pb2.repeated_int32_extension])) + self.assertEqual( + 0, proto2.Extensions[unittest_pb2.repeated_int32_extension][0]) + self.assertEqual( + 1, proto2.Extensions[unittest_pb2.repeated_int32_extension][1]) + self.assertEqual( + 2, proto2.Extensions[unittest_pb2.repeated_int32_extension][2]) + + def testMergeFromExtensionsNestedMessage(self): + proto1 = unittest_pb2.TestAllExtensions() + ext1 = proto1.Extensions[ + unittest_pb2.repeated_nested_message_extension] + m = ext1.add() + m.bb = 222 + m = ext1.add() + m.bb = 333 + + proto2 = unittest_pb2.TestAllExtensions() + ext2 = proto2.Extensions[ + unittest_pb2.repeated_nested_message_extension] + m = ext2.add() + m.bb = 111 + + proto2.MergeFrom(proto1) + ext2 = proto2.Extensions[ + unittest_pb2.repeated_nested_message_extension] + self.assertEqual(3, len(ext2)) + self.assertEqual(111, ext2[0].bb) + self.assertEqual(222, ext2[1].bb) + self.assertEqual(333, ext2[2].bb) + + def testCopyFromSingularField(self): + # Test copy with just a singular field. + proto1 = unittest_pb2.TestAllTypes() + proto1.optional_int32 = 1 + proto1.optional_string = 'important-text' + + proto2 = unittest_pb2.TestAllTypes() + proto2.optional_string = 'value' + + proto2.CopyFrom(proto1) + self.assertEqual(1, proto2.optional_int32) + self.assertEqual('important-text', proto2.optional_string) + + def testCopyFromRepeatedField(self): + # Test copy with a repeated field. + proto1 = unittest_pb2.TestAllTypes() + proto1.repeated_int32.append(1) + proto1.repeated_int32.append(2) + + proto2 = unittest_pb2.TestAllTypes() + proto2.repeated_int32.append(0) + proto2.CopyFrom(proto1) + + self.assertEqual(1, proto2.repeated_int32[0]) + self.assertEqual(2, proto2.repeated_int32[1]) + + def testCopyFromAllFields(self): + # With all fields set. + proto1 = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto1) + proto2 = unittest_pb2.TestAllTypes() + proto2.CopyFrom(proto1) + + # Messages should be equal. + self.assertEqual(proto2, proto1) + + # Serialized string should be equal too. + string1 = proto1.SerializeToString() + string2 = proto2.SerializeToString() + self.assertEqual(string1, string2) + + def testCopyFromSelf(self): + proto1 = unittest_pb2.TestAllTypes() + proto1.repeated_int32.append(1) + proto1.optional_int32 = 2 + proto1.optional_string = 'important-text' + + proto1.CopyFrom(proto1) + self.assertEqual(1, proto1.repeated_int32[0]) + self.assertEqual(2, proto1.optional_int32) + self.assertEqual('important-text', proto1.optional_string) + + def testClear(self): + proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto) + # Clear the message. + proto.Clear() + self.assertEquals(proto.ByteSize(), 0) + empty_proto = unittest_pb2.TestAllTypes() + self.assertEquals(proto, empty_proto) + + # Test if extensions which were set are cleared. + proto = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(proto) + # Clear the message. + proto.Clear() + self.assertEquals(proto.ByteSize(), 0) + empty_proto = unittest_pb2.TestAllExtensions() + self.assertEquals(proto, empty_proto) + + def testIsInitialized(self): + # Trivial cases - all optional fields and extensions. + proto = unittest_pb2.TestAllTypes() + self.assertTrue(proto.IsInitialized()) + proto = unittest_pb2.TestAllExtensions() + self.assertTrue(proto.IsInitialized()) + + # The case of uninitialized required fields. + proto = unittest_pb2.TestRequired() + self.assertFalse(proto.IsInitialized()) + proto.a = proto.b = proto.c = 2 + self.assertTrue(proto.IsInitialized()) + + # The case of uninitialized submessage. + proto = unittest_pb2.TestRequiredForeign() + self.assertTrue(proto.IsInitialized()) + proto.optional_message.a = 1 + self.assertFalse(proto.IsInitialized()) + proto.optional_message.b = 0 + proto.optional_message.c = 0 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized repeated submessage. + message1 = proto.repeated_message.add() + self.assertFalse(proto.IsInitialized()) + message1.a = message1.b = message1.c = 0 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized repeated group in an extension. + proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.multi + message1 = proto.Extensions[extension].add() + message2 = proto.Extensions[extension].add() + self.assertFalse(proto.IsInitialized()) + message1.a = 1 + message1.b = 1 + message1.c = 1 + self.assertFalse(proto.IsInitialized()) + message2.a = 2 + message2.b = 2 + message2.c = 2 + self.assertTrue(proto.IsInitialized()) + + # Uninitialized nonrepeated message in an extension. + proto = unittest_pb2.TestAllExtensions() + extension = unittest_pb2.TestRequired.single + proto.Extensions[extension].a = 1 + self.assertFalse(proto.IsInitialized()) + proto.Extensions[extension].b = 2 + proto.Extensions[extension].c = 3 + self.assertTrue(proto.IsInitialized()) + + +# Since we had so many tests for protocol buffer equality, we broke these out +# into separate TestCase classes. + + +class TestAllTypesEqualityTest(unittest.TestCase): + + def setUp(self): + self.first_proto = unittest_pb2.TestAllTypes() + self.second_proto = unittest_pb2.TestAllTypes() + + def testSelfEquality(self): + self.assertEqual(self.first_proto, self.first_proto) + + def testEmptyProtosEqual(self): + self.assertEqual(self.first_proto, self.second_proto) + + +class FullProtosEqualityTest(unittest.TestCase): + + """Equality tests using completely-full protos as a starting point.""" + + def setUp(self): + self.first_proto = unittest_pb2.TestAllTypes() + self.second_proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(self.first_proto) + test_util.SetAllFields(self.second_proto) + + def testAllFieldsFilledEquality(self): + self.assertEqual(self.first_proto, self.second_proto) + + def testNonRepeatedScalar(self): + # Nonrepeated scalar field change should cause inequality. + self.first_proto.optional_int32 += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + # ...as should clearing a field. + self.first_proto.ClearField('optional_int32') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testNonRepeatedComposite(self): + # Change a nonrepeated composite field. + self.first_proto.optional_nested_message.bb += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.optional_nested_message.bb -= 1 + self.assertEqual(self.first_proto, self.second_proto) + # Clear a field in the nested message. + self.first_proto.optional_nested_message.ClearField('bb') + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.optional_nested_message.bb = ( + self.second_proto.optional_nested_message.bb) + self.assertEqual(self.first_proto, self.second_proto) + # Remove the nested message entirely. + self.first_proto.ClearField('optional_nested_message') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testRepeatedScalar(self): + # Change a repeated scalar field. + self.first_proto.repeated_int32.append(5) + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.ClearField('repeated_int32') + self.assertNotEqual(self.first_proto, self.second_proto) + + def testRepeatedComposite(self): + # Change value within a repeated composite field. + self.first_proto.repeated_nested_message[0].bb += 1 + self.assertNotEqual(self.first_proto, self.second_proto) + self.first_proto.repeated_nested_message[0].bb -= 1 + self.assertEqual(self.first_proto, self.second_proto) + # Add a value to a repeated composite field. + self.first_proto.repeated_nested_message.add() + self.assertNotEqual(self.first_proto, self.second_proto) + self.second_proto.repeated_nested_message.add() + self.assertEqual(self.first_proto, self.second_proto) + + def testNonRepeatedScalarHasBits(self): + # Ensure that we test "has" bits as well as value for + # nonrepeated scalar field. + self.first_proto.ClearField('optional_int32') + self.second_proto.optional_int32 = 0 + self.assertNotEqual(self.first_proto, self.second_proto) + + def testNonRepeatedCompositeHasBits(self): + # Ensure that we test "has" bits as well as value for + # nonrepeated composite field. + self.first_proto.ClearField('optional_nested_message') + self.second_proto.optional_nested_message.ClearField('bb') + self.assertNotEqual(self.first_proto, self.second_proto) + # TODO(robinson): Replace next two lines with method + # to set the "has" bit without changing the value, + # if/when such a method exists. + self.first_proto.optional_nested_message.bb = 0 + self.first_proto.optional_nested_message.ClearField('bb') + self.assertEqual(self.first_proto, self.second_proto) + + +class ExtensionEqualityTest(unittest.TestCase): + + def testExtensionEquality(self): + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + self.assertEqual(first_proto, second_proto) + test_util.SetAllExtensions(first_proto) + self.assertNotEqual(first_proto, second_proto) + test_util.SetAllExtensions(second_proto) + self.assertEqual(first_proto, second_proto) + + # Ensure that we check value equality. + first_proto.Extensions[unittest_pb2.optional_int32_extension] += 1 + self.assertNotEqual(first_proto, second_proto) + first_proto.Extensions[unittest_pb2.optional_int32_extension] -= 1 + self.assertEqual(first_proto, second_proto) + + # Ensure that we also look at "has" bits. + first_proto.ClearExtension(unittest_pb2.optional_int32_extension) + second_proto.Extensions[unittest_pb2.optional_int32_extension] = 0 + self.assertNotEqual(first_proto, second_proto) + first_proto.Extensions[unittest_pb2.optional_int32_extension] = 0 + self.assertEqual(first_proto, second_proto) + + # Ensure that differences in cached values + # don't matter if "has" bits are both false. + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + self.assertEqual( + 0, first_proto.Extensions[unittest_pb2.optional_int32_extension]) + self.assertEqual(first_proto, second_proto) + + +class MutualRecursionEqualityTest(unittest.TestCase): + + def testEqualityWithMutualRecursion(self): + first_proto = unittest_pb2.TestMutualRecursionA() + second_proto = unittest_pb2.TestMutualRecursionA() + self.assertEqual(first_proto, second_proto) + first_proto.bb.a.bb.optional_int32 = 23 + self.assertNotEqual(first_proto, second_proto) + second_proto.bb.a.bb.optional_int32 = 23 + self.assertEqual(first_proto, second_proto) + + +class ByteSizeTest(unittest.TestCase): + + def setUp(self): + self.proto = unittest_pb2.TestAllTypes() + self.extended_proto = more_extensions_pb2.ExtendedMessage() + + def Size(self): + return self.proto.ByteSize() + + def testEmptyMessage(self): + self.assertEqual(0, self.proto.ByteSize()) + + def testVarints(self): + def Test(i, expected_varint_size): + self.proto.Clear() + self.proto.optional_int64 = i + # Add one to the varint size for the tag info + # for tag 1. + self.assertEqual(expected_varint_size + 1, self.Size()) + Test(0, 1) + Test(1, 1) + for i, num_bytes in zip(range(7, 63, 7), range(1, 10000)): + Test((1 << i) - 1, num_bytes) + Test(-1, 10) + Test(-2, 10) + Test(-(1 << 63), 10) + + def testStrings(self): + self.proto.optional_string = '' + # Need one byte for tag info (tag #14), and one byte for length. + self.assertEqual(2, self.Size()) + + self.proto.optional_string = 'abc' + # Need one byte for tag info (tag #14), and one byte for length. + self.assertEqual(2 + len(self.proto.optional_string), self.Size()) + + self.proto.optional_string = 'x' * 128 + # Need one byte for tag info (tag #14), and TWO bytes for length. + self.assertEqual(3 + len(self.proto.optional_string), self.Size()) + + def testOtherNumerics(self): + self.proto.optional_fixed32 = 1234 + # One byte for tag and 4 bytes for fixed32. + self.assertEqual(5, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_fixed64 = 1234 + # One byte for tag and 8 bytes for fixed64. + self.assertEqual(9, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_float = 1.234 + # One byte for tag and 4 bytes for float. + self.assertEqual(5, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_double = 1.234 + # One byte for tag and 8 bytes for float. + self.assertEqual(9, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + self.proto.optional_sint32 = 64 + # One byte for tag and 2 bytes for zig-zag-encoded 64. + self.assertEqual(3, self.Size()) + self.proto = unittest_pb2.TestAllTypes() + + def testComposites(self): + # 3 bytes. + self.proto.optional_nested_message.bb = (1 << 14) + # Plus one byte for bb tag. + # Plus 1 byte for optional_nested_message serialized size. + # Plus two bytes for optional_nested_message tag. + self.assertEqual(3 + 1 + 1 + 2, self.Size()) + + def testGroups(self): + # 4 bytes. + self.proto.optionalgroup.a = (1 << 21) + # Plus two bytes for |a| tag. + # Plus 2 * two bytes for START_GROUP and END_GROUP tags. + self.assertEqual(4 + 2 + 2*2, self.Size()) + + def testRepeatedScalars(self): + self.proto.repeated_int32.append(10) # 1 byte. + self.proto.repeated_int32.append(128) # 2 bytes. + # Also need 2 bytes for each entry for tag. + self.assertEqual(1 + 2 + 2*2, self.Size()) + + def testRepeatedComposites(self): + # Empty message. 2 bytes tag plus 1 byte length. + foreign_message_0 = self.proto.repeated_nested_message.add() + # 2 bytes tag plus 1 byte length plus 1 byte bb tag 1 byte int. + foreign_message_1 = self.proto.repeated_nested_message.add() + foreign_message_1.bb = 7 + self.assertEqual(2 + 1 + 2 + 1 + 1 + 1, self.Size()) + + def testRepeatedGroups(self): + # 2-byte START_GROUP plus 2-byte END_GROUP. + group_0 = self.proto.repeatedgroup.add() + # 2-byte START_GROUP plus 2-byte |a| tag + 1-byte |a| + # plus 2-byte END_GROUP. + group_1 = self.proto.repeatedgroup.add() + group_1.a = 7 + self.assertEqual(2 + 2 + 2 + 2 + 1 + 2, self.Size()) + + def testExtensions(self): + proto = unittest_pb2.TestAllExtensions() + self.assertEqual(0, proto.ByteSize()) + extension = unittest_pb2.optional_int32_extension # Field #1, 1 byte. + proto.Extensions[extension] = 23 + # 1 byte for tag, 1 byte for value. + self.assertEqual(2, proto.ByteSize()) + + def testCacheInvalidationForNonrepeatedScalar(self): + # Test non-extension. + self.proto.optional_int32 = 1 + self.assertEqual(2, self.proto.ByteSize()) + self.proto.optional_int32 = 128 + self.assertEqual(3, self.proto.ByteSize()) + self.proto.ClearField('optional_int32') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.optional_int_extension + self.extended_proto.Extensions[extension] = 1 + self.assertEqual(2, self.extended_proto.ByteSize()) + self.extended_proto.Extensions[extension] = 128 + self.assertEqual(3, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForRepeatedScalar(self): + # Test non-extension. + self.proto.repeated_int32.append(1) + self.assertEqual(3, self.proto.ByteSize()) + self.proto.repeated_int32.append(1) + self.assertEqual(6, self.proto.ByteSize()) + self.proto.repeated_int32[1] = 128 + self.assertEqual(7, self.proto.ByteSize()) + self.proto.ClearField('repeated_int32') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.repeated_int_extension + repeated = self.extended_proto.Extensions[extension] + repeated.append(1) + self.assertEqual(2, self.extended_proto.ByteSize()) + repeated.append(1) + self.assertEqual(4, self.extended_proto.ByteSize()) + repeated[1] = 128 + self.assertEqual(5, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForNonrepeatedMessage(self): + # Test non-extension. + self.proto.optional_foreign_message.c = 1 + self.assertEqual(5, self.proto.ByteSize()) + self.proto.optional_foreign_message.c = 128 + self.assertEqual(6, self.proto.ByteSize()) + self.proto.optional_foreign_message.ClearField('c') + self.assertEqual(3, self.proto.ByteSize()) + self.proto.ClearField('optional_foreign_message') + self.assertEqual(0, self.proto.ByteSize()) + child = self.proto.optional_foreign_message + self.proto.ClearField('optional_foreign_message') + child.c = 128 + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.optional_message_extension + child = self.extended_proto.Extensions[extension] + self.assertEqual(0, self.extended_proto.ByteSize()) + child.foreign_message_int = 1 + self.assertEqual(4, self.extended_proto.ByteSize()) + child.foreign_message_int = 128 + self.assertEqual(5, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + def testCacheInvalidationForRepeatedMessage(self): + # Test non-extension. + child0 = self.proto.repeated_foreign_message.add() + self.assertEqual(3, self.proto.ByteSize()) + self.proto.repeated_foreign_message.add() + self.assertEqual(6, self.proto.ByteSize()) + child0.c = 1 + self.assertEqual(8, self.proto.ByteSize()) + self.proto.ClearField('repeated_foreign_message') + self.assertEqual(0, self.proto.ByteSize()) + + # Test within extension. + extension = more_extensions_pb2.repeated_message_extension + child_list = self.extended_proto.Extensions[extension] + child0 = child_list.add() + self.assertEqual(2, self.extended_proto.ByteSize()) + child_list.add() + self.assertEqual(4, self.extended_proto.ByteSize()) + child0.foreign_message_int = 1 + self.assertEqual(6, self.extended_proto.ByteSize()) + child0.ClearField('foreign_message_int') + self.assertEqual(4, self.extended_proto.ByteSize()) + self.extended_proto.ClearExtension(extension) + self.assertEqual(0, self.extended_proto.ByteSize()) + + +# TODO(robinson): We need cross-language serialization consistency tests. +# Issues to be sure to cover include: +# * Handling of unrecognized tags ("uninterpreted_bytes"). +# * Handling of MessageSets. +# * Consistent ordering of tags in the wire format, +# including ordering between extensions and non-extension +# fields. +# * Consistent serialization of negative numbers, especially +# negative int32s. +# * Handling of empty submessages (with and without "has" +# bits set). + +class SerializationTest(unittest.TestCase): + + def testSerializeEmtpyMessage(self): + first_proto = unittest_pb2.TestAllTypes() + second_proto = unittest_pb2.TestAllTypes() + serialized = first_proto.SerializeToString() + self.assertEqual(first_proto.ByteSize(), len(serialized)) + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testSerializeAllFields(self): + first_proto = unittest_pb2.TestAllTypes() + second_proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(first_proto) + serialized = first_proto.SerializeToString() + self.assertEqual(first_proto.ByteSize(), len(serialized)) + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testSerializeAllExtensions(self): + first_proto = unittest_pb2.TestAllExtensions() + second_proto = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(first_proto) + serialized = first_proto.SerializeToString() + second_proto.MergeFromString(serialized) + self.assertEqual(first_proto, second_proto) + + def testCanonicalSerializationOrder(self): + proto = more_messages_pb2.OutOfOrderFields() + # These are also their tag numbers. Even though we're setting these in + # reverse-tag order AND they're listed in reverse tag-order in the .proto + # file, they should nonetheless be serialized in tag order. + proto.optional_sint32 = 5 + proto.Extensions[more_messages_pb2.optional_uint64] = 4 + proto.optional_uint32 = 3 + proto.Extensions[more_messages_pb2.optional_int64] = 2 + proto.optional_int32 = 1 + serialized = proto.SerializeToString() + self.assertEqual(proto.ByteSize(), len(serialized)) + d = decoder.Decoder(serialized) + ReadTag = d.ReadFieldNumberAndWireType + self.assertEqual((1, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(1, d.ReadInt32()) + self.assertEqual((2, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(2, d.ReadInt64()) + self.assertEqual((3, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(3, d.ReadUInt32()) + self.assertEqual((4, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(4, d.ReadUInt64()) + self.assertEqual((5, wire_format.WIRETYPE_VARINT), ReadTag()) + self.assertEqual(5, d.ReadSInt32()) + + def testCanonicalSerializationOrderSameAsCpp(self): + # Copy of the same test we use for C++. + proto = unittest_pb2.TestFieldOrderings() + test_util.SetAllFieldsAndExtensions(proto) + serialized = proto.SerializeToString() + test_util.ExpectAllFieldsAndExtensionsInOrder(serialized) + + def testMergeFromStringWhenFieldsAlreadySet(self): + first_proto = unittest_pb2.TestAllTypes() + first_proto.repeated_string.append('foobar') + first_proto.optional_int32 = 23 + first_proto.optional_nested_message.bb = 42 + serialized = first_proto.SerializeToString() + + second_proto = unittest_pb2.TestAllTypes() + second_proto.repeated_string.append('baz') + second_proto.optional_int32 = 100 + second_proto.optional_nested_message.bb = 999 + + second_proto.MergeFromString(serialized) + # Ensure that we append to repeated fields. + self.assertEqual(['baz', 'foobar'], list(second_proto.repeated_string)) + # Ensure that we overwrite nonrepeatd scalars. + self.assertEqual(23, second_proto.optional_int32) + # Ensure that we recursively call MergeFromString() on + # submessages. + self.assertEqual(42, second_proto.optional_nested_message.bb) + + def testMessageSetWireFormat(self): + proto = unittest_mset_pb2.TestMessageSet() + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + extension_message2 = unittest_mset_pb2.TestMessageSetExtension2 + extension1 = extension_message1.message_set_extension + extension2 = extension_message2.message_set_extension + proto.Extensions[extension1].i = 123 + proto.Extensions[extension2].str = 'foo' + + # Serialize using the MessageSet wire format (this is specified in the + # .proto file). + serialized = proto.SerializeToString() + + raw = unittest_mset_pb2.RawMessageSet() + self.assertEqual(False, + raw.DESCRIPTOR.GetOptions().message_set_wire_format) + raw.MergeFromString(serialized) + self.assertEqual(2, len(raw.item)) + + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.MergeFromString(raw.item[0].message) + self.assertEqual(123, message1.i) + + message2 = unittest_mset_pb2.TestMessageSetExtension2() + message2.MergeFromString(raw.item[1].message) + self.assertEqual('foo', message2.str) + + # Deserialize using the MessageSet wire format. + proto2 = unittest_mset_pb2.TestMessageSet() + proto2.MergeFromString(serialized) + self.assertEqual(123, proto2.Extensions[extension1].i) + self.assertEqual('foo', proto2.Extensions[extension2].str) + + # Check byte size. + self.assertEqual(proto2.ByteSize(), len(serialized)) + self.assertEqual(proto.ByteSize(), len(serialized)) + + def testMessageSetWireFormatUnknownExtension(self): + # Create a message using the message set wire format with an unknown + # message. + raw = unittest_mset_pb2.RawMessageSet() + + # Add an item. + item = raw.item.add() + item.type_id = 1545008 + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.i = 12345 + item.message = message1.SerializeToString() + + # Add a second, unknown extension. + item = raw.item.add() + item.type_id = 1545009 + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + message1 = unittest_mset_pb2.TestMessageSetExtension1() + message1.i = 12346 + item.message = message1.SerializeToString() + + # Add another unknown extension. + item = raw.item.add() + item.type_id = 1545010 + message1 = unittest_mset_pb2.TestMessageSetExtension2() + message1.str = 'foo' + item.message = message1.SerializeToString() + + serialized = raw.SerializeToString() + + # Parse message using the message set wire format. + proto = unittest_mset_pb2.TestMessageSet() + proto.MergeFromString(serialized) + + # Check that the message parsed well. + extension_message1 = unittest_mset_pb2.TestMessageSetExtension1 + extension1 = extension_message1.message_set_extension + self.assertEquals(12345, proto.Extensions[extension1].i) + + def testUnknownFields(self): + proto = unittest_pb2.TestAllTypes() + test_util.SetAllFields(proto) + + serialized = proto.SerializeToString() + + # The empty message should be parsable with all of the fields + # unknown. + proto2 = unittest_pb2.TestEmptyMessage() + + # Parsing this message should succeed. + proto2.MergeFromString(serialized) + + def _CheckRaises(self, exc_class, callable_obj, exception): + """This method checks if the excpetion type and message are as expected.""" + try: + callable_obj() + except exc_class, ex: + # Check if the exception message is the right one. + self.assertEqual(exception, str(ex)) + return + else: + raise self.failureException('%s not raised' % str(exc_class)) + + def testSerializeUninitialized(self): + proto = unittest_pb2.TestRequired() + self._CheckRaises( + message.EncodeError, + proto.SerializeToString, + 'Required field protobuf_unittest.TestRequired.a is not set.') + # Shouldn't raise exceptions. + partial = proto.SerializePartialToString() + + proto.a = 1 + self._CheckRaises( + message.EncodeError, + proto.SerializeToString, + 'Required field protobuf_unittest.TestRequired.b is not set.') + # Shouldn't raise exceptions. + partial = proto.SerializePartialToString() + + proto.b = 2 + self._CheckRaises( + message.EncodeError, + proto.SerializeToString, + 'Required field protobuf_unittest.TestRequired.c is not set.') + # Shouldn't raise exceptions. + partial = proto.SerializePartialToString() + + proto.c = 3 + serialized = proto.SerializeToString() + # Shouldn't raise exceptions. + partial = proto.SerializePartialToString() + + proto2 = unittest_pb2.TestRequired() + proto2.MergeFromString(serialized) + self.assertEqual(1, proto2.a) + self.assertEqual(2, proto2.b) + self.assertEqual(3, proto2.c) + proto2.ParseFromString(partial) + self.assertEqual(1, proto2.a) + self.assertEqual(2, proto2.b) + self.assertEqual(3, proto2.c) + + +class OptionsTest(unittest.TestCase): + + def testMessageOptions(self): + proto = unittest_mset_pb2.TestMessageSet() + self.assertEqual(True, + proto.DESCRIPTOR.GetOptions().message_set_wire_format) + proto = unittest_pb2.TestAllTypes() + self.assertEqual(False, + proto.DESCRIPTOR.GetOptions().message_set_wire_format) + + +class UtilityTest(unittest.TestCase): + + def testImergeSorted(self): + ImergeSorted = reflection._ImergeSorted + # Various types of emptiness. + self.assertEqual([], list(ImergeSorted())) + self.assertEqual([], list(ImergeSorted([]))) + self.assertEqual([], list(ImergeSorted([], []))) + + # One nonempty list. + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 2, 3]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 2, 3], []))) + self.assertEqual([1, 2, 3], list(ImergeSorted([], [1, 2, 3]))) + + # Merging some nonempty lists together. + self.assertEqual([1, 2, 3], list(ImergeSorted([1, 3], [2]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1], [3], [2]))) + self.assertEqual([1, 2, 3], list(ImergeSorted([1], [3], [2], []))) + + # Elements repeated across component iterators. + self.assertEqual([1, 2, 2, 3, 3], + list(ImergeSorted([1, 2], [3], [2, 3]))) + + # Elements repeated within an iterator. + self.assertEqual([1, 2, 2, 3, 3], + list(ImergeSorted([1, 2, 2], [3], [3]))) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/service_reflection_test.py b/python/google/protobuf/internal/service_reflection_test.py new file mode 100755 index 00000000..d43ed641 --- /dev/null +++ b/python/google/protobuf/internal/service_reflection_test.py @@ -0,0 +1,116 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Tests for google.protobuf.internal.service_reflection.""" + +__author__ = 'petar@google.com (Petar Petrov)' + +import unittest +from google.protobuf import unittest_pb2 +from google.protobuf import service_reflection +from google.protobuf import service + + +class FooUnitTest(unittest.TestCase): + + def testService(self): + class MockRpcChannel(service.RpcChannel): + def CallMethod(self, method, controller, request, response, callback): + self.method = method + self.controller = controller + self.request = request + callback(response) + + class MockRpcController(service.RpcController): + def SetFailed(self, msg): + self.failure_message = msg + + self.callback_response = None + + class MyService(unittest_pb2.TestService): + pass + + self.callback_response = None + + def MyCallback(response): + self.callback_response = response + + rpc_controller = MockRpcController() + channel = MockRpcChannel() + srvc = MyService() + srvc.Foo(rpc_controller, unittest_pb2.FooRequest(), MyCallback) + self.assertEqual('Method Foo not implemented.', + rpc_controller.failure_message) + self.assertEqual(None, self.callback_response) + + rpc_controller.failure_message = None + + service_descriptor = unittest_pb2.TestService.DESCRIPTOR + srvc.CallMethod(service_descriptor.methods[1], rpc_controller, + unittest_pb2.BarRequest(), MyCallback) + self.assertEqual('Method Bar not implemented.', + rpc_controller.failure_message) + self.assertEqual(None, self.callback_response) + + class MyServiceImpl(unittest_pb2.TestService): + def Foo(self, rpc_controller, request, done): + self.foo_called = True + def Bar(self, rpc_controller, request, done): + self.bar_called = True + + srvc = MyServiceImpl() + rpc_controller.failure_message = None + srvc.Foo(rpc_controller, unittest_pb2.FooRequest(), MyCallback) + self.assertEqual(None, rpc_controller.failure_message) + self.assertEqual(True, srvc.foo_called) + + rpc_controller.failure_message = None + srvc.CallMethod(service_descriptor.methods[1], rpc_controller, + unittest_pb2.BarRequest(), MyCallback) + self.assertEqual(None, rpc_controller.failure_message) + self.assertEqual(True, srvc.bar_called) + + def testServiceStub(self): + class MockRpcChannel(service.RpcChannel): + def CallMethod(self, method, controller, request, + response_class, callback): + self.method = method + self.controller = controller + self.request = request + callback(response_class()) + + self.callback_response = None + + def MyCallback(response): + self.callback_response = response + + channel = MockRpcChannel() + stub = unittest_pb2.TestService_Stub(channel) + rpc_controller = 'controller' + request = 'request' + + # Invoke method. + stub.Foo(rpc_controller, request, MyCallback) + + self.assertTrue(isinstance(self.callback_response, + unittest_pb2.FooResponse)) + self.assertEqual(request, channel.request) + self.assertEqual(rpc_controller, channel.controller) + self.assertEqual(stub.GetDescriptor().methods[0], channel.method) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/internal/test_util.py b/python/google/protobuf/internal/test_util.py new file mode 100755 index 00000000..d9106421 --- /dev/null +++ b/python/google/protobuf/internal/test_util.py @@ -0,0 +1,354 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Utilities for Python proto2 tests. + +This is intentionally modeled on C++ code in +//net/proto2/internal/test_util.*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import os.path + +from google.protobuf import unittest_import_pb2 +from google.protobuf import unittest_pb2 + + +def SetAllFields(message): + """Sets every field in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllTypes instance. + """ + + # + # Optional fields. + # + + message.optional_int32 = 101 + message.optional_int64 = 102 + message.optional_uint32 = 103 + message.optional_uint64 = 104 + message.optional_sint32 = 105 + message.optional_sint64 = 106 + message.optional_fixed32 = 107 + message.optional_fixed64 = 108 + message.optional_sfixed32 = 109 + message.optional_sfixed64 = 110 + message.optional_float = 111 + message.optional_double = 112 + message.optional_bool = True + # TODO(robinson): Firmly spec out and test how + # protos interact with unicode. One specific example: + # what happens if we change the literal below to + # u'115'? What *should* happen? Still some discussion + # to finish with Kenton about bytes vs. strings + # and forcing everything to be utf8. :-/ + message.optional_string = '115' + message.optional_bytes = '116' + + message.optionalgroup.a = 117 + message.optional_nested_message.bb = 118 + message.optional_foreign_message.c = 119 + message.optional_import_message.d = 120 + + message.optional_nested_enum = unittest_pb2.TestAllTypes.BAZ + message.optional_foreign_enum = unittest_pb2.FOREIGN_BAZ + message.optional_import_enum = unittest_import_pb2.IMPORT_BAZ + + message.optional_string_piece = '124' + message.optional_cord = '125' + + # + # Repeated fields. + # + + message.repeated_int32.append(201) + message.repeated_int64.append(202) + message.repeated_uint32.append(203) + message.repeated_uint64.append(204) + message.repeated_sint32.append(205) + message.repeated_sint64.append(206) + message.repeated_fixed32.append(207) + message.repeated_fixed64.append(208) + message.repeated_sfixed32.append(209) + message.repeated_sfixed64.append(210) + message.repeated_float.append(211) + message.repeated_double.append(212) + message.repeated_bool.append(True) + message.repeated_string.append('215') + message.repeated_bytes.append('216') + + message.repeatedgroup.add().a = 217 + message.repeated_nested_message.add().bb = 218 + message.repeated_foreign_message.add().c = 219 + message.repeated_import_message.add().d = 220 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAR) + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAR) + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAR) + + message.repeated_string_piece.append('224') + message.repeated_cord.append('225') + + # Add a second one of each field. + message.repeated_int32.append(301) + message.repeated_int64.append(302) + message.repeated_uint32.append(303) + message.repeated_uint64.append(304) + message.repeated_sint32.append(305) + message.repeated_sint64.append(306) + message.repeated_fixed32.append(307) + message.repeated_fixed64.append(308) + message.repeated_sfixed32.append(309) + message.repeated_sfixed64.append(310) + message.repeated_float.append(311) + message.repeated_double.append(312) + message.repeated_bool.append(False) + message.repeated_string.append('315') + message.repeated_bytes.append('316') + + message.repeatedgroup.add().a = 317 + message.repeated_nested_message.add().bb = 318 + message.repeated_foreign_message.add().c = 319 + message.repeated_import_message.add().d = 320 + + message.repeated_nested_enum.append(unittest_pb2.TestAllTypes.BAZ) + message.repeated_foreign_enum.append(unittest_pb2.FOREIGN_BAZ) + message.repeated_import_enum.append(unittest_import_pb2.IMPORT_BAZ) + + message.repeated_string_piece.append('324') + message.repeated_cord.append('325') + + # + # Fields that have defaults. + # + + message.default_int32 = 401 + message.default_int64 = 402 + message.default_uint32 = 403 + message.default_uint64 = 404 + message.default_sint32 = 405 + message.default_sint64 = 406 + message.default_fixed32 = 407 + message.default_fixed64 = 408 + message.default_sfixed32 = 409 + message.default_sfixed64 = 410 + message.default_float = 411 + message.default_double = 412 + message.default_bool = False + message.default_string = '415' + message.default_bytes = '416' + + message.default_nested_enum = unittest_pb2.TestAllTypes.FOO + message.default_foreign_enum = unittest_pb2.FOREIGN_FOO + message.default_import_enum = unittest_import_pb2.IMPORT_FOO + + message.default_string_piece = '424' + message.default_cord = '425' + + +def SetAllExtensions(message): + """Sets every extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions instance. + """ + + extensions = message.Extensions + pb2 = unittest_pb2 + import_pb2 = unittest_import_pb2 + + # + # Optional fields. + # + + extensions[pb2.optional_int32_extension] = 101 + extensions[pb2.optional_int64_extension] = 102 + extensions[pb2.optional_uint32_extension] = 103 + extensions[pb2.optional_uint64_extension] = 104 + extensions[pb2.optional_sint32_extension] = 105 + extensions[pb2.optional_sint64_extension] = 106 + extensions[pb2.optional_fixed32_extension] = 107 + extensions[pb2.optional_fixed64_extension] = 108 + extensions[pb2.optional_sfixed32_extension] = 109 + extensions[pb2.optional_sfixed64_extension] = 110 + extensions[pb2.optional_float_extension] = 111 + extensions[pb2.optional_double_extension] = 112 + extensions[pb2.optional_bool_extension] = True + extensions[pb2.optional_string_extension] = '115' + extensions[pb2.optional_bytes_extension] = '116' + + extensions[pb2.optionalgroup_extension].a = 117 + extensions[pb2.optional_nested_message_extension].bb = 118 + extensions[pb2.optional_foreign_message_extension].c = 119 + extensions[pb2.optional_import_message_extension].d = 120 + + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_nested_enum_extension] = pb2.TestAllTypes.BAZ + extensions[pb2.optional_foreign_enum_extension] = pb2.FOREIGN_BAZ + extensions[pb2.optional_import_enum_extension] = import_pb2.IMPORT_BAZ + + extensions[pb2.optional_string_piece_extension] = '124' + extensions[pb2.optional_cord_extension] = '125' + + # + # Repeated fields. + # + + extensions[pb2.repeated_int32_extension].append(201) + extensions[pb2.repeated_int64_extension].append(202) + extensions[pb2.repeated_uint32_extension].append(203) + extensions[pb2.repeated_uint64_extension].append(204) + extensions[pb2.repeated_sint32_extension].append(205) + extensions[pb2.repeated_sint64_extension].append(206) + extensions[pb2.repeated_fixed32_extension].append(207) + extensions[pb2.repeated_fixed64_extension].append(208) + extensions[pb2.repeated_sfixed32_extension].append(209) + extensions[pb2.repeated_sfixed64_extension].append(210) + extensions[pb2.repeated_float_extension].append(211) + extensions[pb2.repeated_double_extension].append(212) + extensions[pb2.repeated_bool_extension].append(True) + extensions[pb2.repeated_string_extension].append('215') + extensions[pb2.repeated_bytes_extension].append('216') + + extensions[pb2.repeatedgroup_extension].add().a = 217 + extensions[pb2.repeated_nested_message_extension].add().bb = 218 + extensions[pb2.repeated_foreign_message_extension].add().c = 219 + extensions[pb2.repeated_import_message_extension].add().d = 220 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAR) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAR) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAR) + + extensions[pb2.repeated_string_piece_extension].append('224') + extensions[pb2.repeated_cord_extension].append('225') + + # Append a second one of each field. + extensions[pb2.repeated_int32_extension].append(301) + extensions[pb2.repeated_int64_extension].append(302) + extensions[pb2.repeated_uint32_extension].append(303) + extensions[pb2.repeated_uint64_extension].append(304) + extensions[pb2.repeated_sint32_extension].append(305) + extensions[pb2.repeated_sint64_extension].append(306) + extensions[pb2.repeated_fixed32_extension].append(307) + extensions[pb2.repeated_fixed64_extension].append(308) + extensions[pb2.repeated_sfixed32_extension].append(309) + extensions[pb2.repeated_sfixed64_extension].append(310) + extensions[pb2.repeated_float_extension].append(311) + extensions[pb2.repeated_double_extension].append(312) + extensions[pb2.repeated_bool_extension].append(False) + extensions[pb2.repeated_string_extension].append('315') + extensions[pb2.repeated_bytes_extension].append('316') + + extensions[pb2.repeatedgroup_extension].add().a = 317 + extensions[pb2.repeated_nested_message_extension].add().bb = 318 + extensions[pb2.repeated_foreign_message_extension].add().c = 319 + extensions[pb2.repeated_import_message_extension].add().d = 320 + + extensions[pb2.repeated_nested_enum_extension].append(pb2.TestAllTypes.BAZ) + extensions[pb2.repeated_foreign_enum_extension].append(pb2.FOREIGN_BAZ) + extensions[pb2.repeated_import_enum_extension].append(import_pb2.IMPORT_BAZ) + + extensions[pb2.repeated_string_piece_extension].append('324') + extensions[pb2.repeated_cord_extension].append('325') + + # + # Fields with defaults. + # + + extensions[pb2.default_int32_extension] = 401 + extensions[pb2.default_int64_extension] = 402 + extensions[pb2.default_uint32_extension] = 403 + extensions[pb2.default_uint64_extension] = 404 + extensions[pb2.default_sint32_extension] = 405 + extensions[pb2.default_sint64_extension] = 406 + extensions[pb2.default_fixed32_extension] = 407 + extensions[pb2.default_fixed64_extension] = 408 + extensions[pb2.default_sfixed32_extension] = 409 + extensions[pb2.default_sfixed64_extension] = 410 + extensions[pb2.default_float_extension] = 411 + extensions[pb2.default_double_extension] = 412 + extensions[pb2.default_bool_extension] = False + extensions[pb2.default_string_extension] = '415' + extensions[pb2.default_bytes_extension] = '416' + + extensions[pb2.default_nested_enum_extension] = pb2.TestAllTypes.FOO + extensions[pb2.default_foreign_enum_extension] = pb2.FOREIGN_FOO + extensions[pb2.default_import_enum_extension] = import_pb2.IMPORT_FOO + + extensions[pb2.default_string_piece_extension] = '424' + extensions[pb2.default_cord_extension] = '425' + + +def SetAllFieldsAndExtensions(message): + """Sets every field and extension in the message to a unique value. + + Args: + message: A unittest_pb2.TestAllExtensions message. + """ + message.my_int = 1 + message.my_string = 'foo' + message.my_float = 1.0 + message.Extensions[unittest_pb2.my_extension_int] = 23 + message.Extensions[unittest_pb2.my_extension_string] = 'bar' + + +def ExpectAllFieldsAndExtensionsInOrder(serialized): + """Ensures that serialized is the serialization we expect for a message + filled with SetAllFieldsAndExtensions(). (Specifically, ensures that the + serialization is in canonical, tag-number order). + """ + my_extension_int = unittest_pb2.my_extension_int + my_extension_string = unittest_pb2.my_extension_string + expected_strings = [] + message = unittest_pb2.TestFieldOrderings() + message.my_int = 1 # Field 1. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_int] = 23 # Field 5. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_string = 'foo' # Field 11. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.Extensions[my_extension_string] = 'bar' # Field 50. + expected_strings.append(message.SerializeToString()) + message.Clear() + message.my_float = 1.0 + expected_strings.append(message.SerializeToString()) + message.Clear() + expected = ''.join(expected_strings) + + if expected != serialized: + raise ValueError('Expected %r, found %r' % (expected, serialized)) + +def GoldenFile(filename): + """Finds the given golden file and returns a file object representing it.""" + + # Search up the directory tree looking for the C++ protobuf source code. + path = '.' + while os.path.exists(path): + if os.path.exists(os.path.join(path, 'src/google/protobuf')): + # Found it. Load the golden file from the testdata directory. + return file(os.path.join(path, 'src/google/protobuf/testdata', filename)) + path = os.path.join(path, '..') + + raise RuntimeError( + 'Could not find golden files. This test must be run from within the ' + 'protobuf source package so that it can read test data files from the ' + 'C++ source tree.') diff --git a/python/google/protobuf/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py new file mode 100755 index 00000000..c8fc79dc --- /dev/null +++ b/python/google/protobuf/internal/text_format_test.py @@ -0,0 +1,106 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.text_format.""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import difflib + +import unittest +from google.protobuf import text_format +from google.protobuf.internal import test_util +from google.protobuf import unittest_pb2 +from google.protobuf import unittest_mset_pb2 + +class TextFormatTest(unittest.TestCase): + def CompareToGoldenFile(self, text, golden_filename): + f = test_util.GoldenFile(golden_filename) + golden_lines = f.readlines() + f.close() + self.CompareToGoldenLines(text, golden_lines) + + def CompareToGoldenText(self, text, golden_text): + self.CompareToGoldenLines(text, golden_text.splitlines(1)) + + def CompareToGoldenLines(self, text, golden_lines): + actual_lines = text.splitlines(1) + self.assertEqual(golden_lines, actual_lines, + "Text doesn't match golden. Diff:\n" + + ''.join(difflib.ndiff(golden_lines, actual_lines))) + + def testPrintAllFields(self): + message = unittest_pb2.TestAllTypes() + test_util.SetAllFields(message) + self.CompareToGoldenFile( + self.RemoveRedundantZeros(text_format.MessageToString(message)), + 'text_format_unittest_data.txt') + + def testPrintAllExtensions(self): + message = unittest_pb2.TestAllExtensions() + test_util.SetAllExtensions(message) + self.CompareToGoldenFile( + self.RemoveRedundantZeros(text_format.MessageToString(message)), + 'text_format_unittest_extensions_data.txt') + + def testPrintMessageSet(self): + message = unittest_mset_pb2.TestMessageSetContainer() + ext1 = unittest_mset_pb2.TestMessageSetExtension1.message_set_extension + ext2 = unittest_mset_pb2.TestMessageSetExtension2.message_set_extension + message.message_set.Extensions[ext1].i = 23 + message.message_set.Extensions[ext2].str = 'foo' + self.CompareToGoldenText(text_format.MessageToString(message), + 'message_set {\n' + ' [protobuf_unittest.TestMessageSetExtension1] {\n' + ' i: 23\n' + ' }\n' + ' [protobuf_unittest.TestMessageSetExtension2] {\n' + ' str: \"foo\"\n' + ' }\n' + '}\n') + + def testPrintExotic(self): + message = unittest_pb2.TestAllTypes() + message.repeated_int64.append(-9223372036854775808); + message.repeated_uint64.append(18446744073709551615); + message.repeated_double.append(123.456); + message.repeated_double.append(1.23e22); + message.repeated_double.append(1.23e-18); + message.repeated_string.append('\000\001\a\b\f\n\r\t\v\\\'\"'); + self.CompareToGoldenText( + self.RemoveRedundantZeros(text_format.MessageToString(message)), + 'repeated_int64: -9223372036854775808\n' + 'repeated_uint64: 18446744073709551615\n' + 'repeated_double: 123.456\n' + 'repeated_double: 1.23e+22\n' + 'repeated_double: 1.23e-18\n' + 'repeated_string: ' + '\"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\\'\\\"\"\n') + + def testMessageToString(self): + message = unittest_pb2.ForeignMessage() + message.c = 123 + self.assertEqual('c: 123\n', str(message)) + + def RemoveRedundantZeros(self, text): + # Some platforms print 1e+5 as 1e+005. This is fine, but we need to remove + # these zeros in order to match the golden file. + return text.replace('e+0','e+').replace('e+0','e+') \ + .replace('e-0','e-').replace('e-0','e-') + +if __name__ == '__main__': + unittest.main() + diff --git a/python/google/protobuf/internal/type_checkers.py b/python/google/protobuf/internal/type_checkers.py new file mode 100755 index 00000000..c3b8764d --- /dev/null +++ b/python/google/protobuf/internal/type_checkers.py @@ -0,0 +1,216 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Provides type checking routines. + +This module defines type checking utilities in the forms of dictionaries: + +VALUE_CHECKERS: A dictionary of field types and a value validation object. +TYPE_TO_BYTE_SIZE_FN: A dictionary with field types and a size computing + function. +TYPE_TO_SERIALIZE_METHOD: A dictionary with field types and serialization + function. +FIELD_TYPE_TO_WIRE_TYPE: A dictionary with field typed and their + coresponding wire types. +TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization + function. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format +from google.protobuf import descriptor + +_FieldDescriptor = descriptor.FieldDescriptor + +# None of the typecheckers below make any attempt to guard against people +# subclassing builtin types and doing weird things. We're not trying to +# protect against malicious clients here, just people accidentally shooting +# themselves in the foot in obvious ways. + +class TypeChecker(object): + + """Type checker used to catch type errors as early as possible + when the client is setting scalar fields in protocol messages. + """ + + def __init__(self, *acceptable_types): + self._acceptable_types = acceptable_types + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, self._acceptable_types): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), self._acceptable_types)) + raise TypeError(message) + + +# IntValueChecker and its subclasses perform integer type-checks +# and bounds-checks. +class IntValueChecker(object): + + """Checker used for integer fields. Performs type-check and range check.""" + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, (int, long)): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int, long))) + raise TypeError(message) + if not self._MIN <= proposed_value <= self._MAX: + raise ValueError('Value out of range: %d' % proposed_value) + + +class Int32ValueChecker(IntValueChecker): + # We're sure to use ints instead of longs here since comparison may be more + # efficient. + _MIN = -2147483648 + _MAX = 2147483647 + + +class Uint32ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 32) - 1 + + +class Int64ValueChecker(IntValueChecker): + _MIN = -(1 << 63) + _MAX = (1 << 63) - 1 + + +class Uint64ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 64) - 1 + + +# Type-checkers for all scalar CPPTYPEs. +VALUE_CHECKERS = { + _FieldDescriptor.CPPTYPE_INT32: Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_INT64: Int64ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT32: Uint32ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT64: Uint64ValueChecker(), + _FieldDescriptor.CPPTYPE_DOUBLE: TypeChecker( + float, int, long), + _FieldDescriptor.CPPTYPE_FLOAT: TypeChecker( + float, int, long), + _FieldDescriptor.CPPTYPE_BOOL: TypeChecker(bool, int), + _FieldDescriptor.CPPTYPE_ENUM: Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_STRING: TypeChecker(str), + } + + +# Map from field type to a function F, such that F(field_num, value) +# gives the total byte size for a value of the given type. This +# byte size includes tag information and any other additional space +# associated with serializing "value". +TYPE_TO_BYTE_SIZE_FN = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize, + _FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize, + _FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize, + _FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize, + _FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize, + _FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize, + _FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize, + _FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize, + _FieldDescriptor.TYPE_STRING: wire_format.StringByteSize, + _FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize, + _FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize, + _FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize, + _FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize, + _FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize, + _FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize, + _FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize, + _FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize, + _FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize + } + + +# Maps from field type to an unbound Encoder method F, such that +# F(encoder, field_number, value) will append the serialization +# of a value of this type to the encoder. +_Encoder = encoder.Encoder +TYPE_TO_SERIALIZE_METHOD = { + _FieldDescriptor.TYPE_DOUBLE: _Encoder.AppendDouble, + _FieldDescriptor.TYPE_FLOAT: _Encoder.AppendFloat, + _FieldDescriptor.TYPE_INT64: _Encoder.AppendInt64, + _FieldDescriptor.TYPE_UINT64: _Encoder.AppendUInt64, + _FieldDescriptor.TYPE_INT32: _Encoder.AppendInt32, + _FieldDescriptor.TYPE_FIXED64: _Encoder.AppendFixed64, + _FieldDescriptor.TYPE_FIXED32: _Encoder.AppendFixed32, + _FieldDescriptor.TYPE_BOOL: _Encoder.AppendBool, + _FieldDescriptor.TYPE_STRING: _Encoder.AppendString, + _FieldDescriptor.TYPE_GROUP: _Encoder.AppendGroup, + _FieldDescriptor.TYPE_MESSAGE: _Encoder.AppendMessage, + _FieldDescriptor.TYPE_BYTES: _Encoder.AppendBytes, + _FieldDescriptor.TYPE_UINT32: _Encoder.AppendUInt32, + _FieldDescriptor.TYPE_ENUM: _Encoder.AppendEnum, + _FieldDescriptor.TYPE_SFIXED32: _Encoder.AppendSFixed32, + _FieldDescriptor.TYPE_SFIXED64: _Encoder.AppendSFixed64, + _FieldDescriptor.TYPE_SINT32: _Encoder.AppendSInt32, + _FieldDescriptor.TYPE_SINT64: _Encoder.AppendSInt64, + } + + +# Maps from field type to expected wiretype. +FIELD_TYPE_TO_WIRE_TYPE = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_STRING: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP, + _FieldDescriptor.TYPE_MESSAGE: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_BYTES: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT, + } + + +# Maps from field type to an unbound Decoder method F, +# such that F(decoder) will read a field of the requested type. +# +# Note that Message and Group are intentionally missing here. +# They're handled by _RecursivelyMerge(). +_Decoder = decoder.Decoder +TYPE_TO_DESERIALIZE_METHOD = { + _FieldDescriptor.TYPE_DOUBLE: _Decoder.ReadDouble, + _FieldDescriptor.TYPE_FLOAT: _Decoder.ReadFloat, + _FieldDescriptor.TYPE_INT64: _Decoder.ReadInt64, + _FieldDescriptor.TYPE_UINT64: _Decoder.ReadUInt64, + _FieldDescriptor.TYPE_INT32: _Decoder.ReadInt32, + _FieldDescriptor.TYPE_FIXED64: _Decoder.ReadFixed64, + _FieldDescriptor.TYPE_FIXED32: _Decoder.ReadFixed32, + _FieldDescriptor.TYPE_BOOL: _Decoder.ReadBool, + _FieldDescriptor.TYPE_STRING: _Decoder.ReadString, + _FieldDescriptor.TYPE_BYTES: _Decoder.ReadBytes, + _FieldDescriptor.TYPE_UINT32: _Decoder.ReadUInt32, + _FieldDescriptor.TYPE_ENUM: _Decoder.ReadEnum, + _FieldDescriptor.TYPE_SFIXED32: _Decoder.ReadSFixed32, + _FieldDescriptor.TYPE_SFIXED64: _Decoder.ReadSFixed64, + _FieldDescriptor.TYPE_SINT32: _Decoder.ReadSInt32, + _FieldDescriptor.TYPE_SINT64: _Decoder.ReadSInt64, + } diff --git a/python/google/protobuf/internal/wire_format.py b/python/google/protobuf/internal/wire_format.py new file mode 100755 index 00000000..ea219e27 --- /dev/null +++ b/python/google/protobuf/internal/wire_format.py @@ -0,0 +1,222 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Constants and static functions to support protocol buffer wire format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import message + + +TAG_TYPE_BITS = 3 # Number of bits used to hold type info in a proto tag. +_TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1 # 0x7 + +# These numbers identify the wire type of a protocol buffer value. +# We use the least-significant TAG_TYPE_BITS bits of the varint-encoded +# tag-and-type to store one of these WIRETYPE_* constants. +# These values must match WireType enum in //net/proto2/public/wire_format.h. +WIRETYPE_VARINT = 0 +WIRETYPE_FIXED64 = 1 +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 +WIRETYPE_END_GROUP = 4 +WIRETYPE_FIXED32 = 5 +_WIRETYPE_MAX = 5 + + +# Bounds for various integer types. +INT32_MAX = int((1 << 31) - 1) +INT32_MIN = int(-(1 << 31)) +UINT32_MAX = (1 << 32) - 1 + +INT64_MAX = (1 << 63) - 1 +INT64_MIN = -(1 << 63) +UINT64_MAX = (1 << 64) - 1 + +# "struct" format strings that will encode/decode the specified formats. +FORMAT_UINT32_LITTLE_ENDIAN = '> TAG_TYPE_BITS), (tag & _TAG_TYPE_MASK) + + +def ZigZagEncode(value): + """ZigZag Transform: Encodes signed integers so that they can be + effectively used with varint encoding. See wire_format.h for + more details. + """ + if value >= 0: + return value << 1 + return (value << 1) ^ (~0) + + +def ZigZagDecode(value): + """Inverse of ZigZagEncode().""" + if not value & 0x1: + return value >> 1 + return (value >> 1) ^ (~0) + + + +# The *ByteSize() functions below return the number of bytes required to +# serialize "field number + type" information and then serialize the value. + + +def Int32ByteSize(field_number, int32): + return Int64ByteSize(field_number, int32) + + +def Int64ByteSize(field_number, int64): + # Have to convert to uint before calling UInt64ByteSize(). + return UInt64ByteSize(field_number, 0xffffffffffffffff & int64) + + +def UInt32ByteSize(field_number, uint32): + return UInt64ByteSize(field_number, uint32) + + +def UInt64ByteSize(field_number, uint64): + return _TagByteSize(field_number) + _VarUInt64ByteSizeNoTag(uint64) + + +def SInt32ByteSize(field_number, int32): + return UInt32ByteSize(field_number, ZigZagEncode(int32)) + + +def SInt64ByteSize(field_number, int64): + return UInt64ByteSize(field_number, ZigZagEncode(int64)) + + +def Fixed32ByteSize(field_number, fixed32): + return _TagByteSize(field_number) + 4 + + +def Fixed64ByteSize(field_number, fixed64): + return _TagByteSize(field_number) + 8 + + +def SFixed32ByteSize(field_number, sfixed32): + return _TagByteSize(field_number) + 4 + + +def SFixed64ByteSize(field_number, sfixed64): + return _TagByteSize(field_number) + 8 + + +def FloatByteSize(field_number, flt): + return _TagByteSize(field_number) + 4 + + +def DoubleByteSize(field_number, double): + return _TagByteSize(field_number) + 8 + + +def BoolByteSize(field_number, b): + return _TagByteSize(field_number) + 1 + + +def EnumByteSize(field_number, enum): + return UInt32ByteSize(field_number, enum) + + +def StringByteSize(field_number, string): + return (_TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(len(string)) + + len(string)) + + +def BytesByteSize(field_number, b): + return StringByteSize(field_number, b) + + +def GroupByteSize(field_number, message): + return (2 * _TagByteSize(field_number) # START and END group. + + message.ByteSize()) + + +def MessageByteSize(field_number, message): + return (_TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(message.ByteSize()) + + message.ByteSize()) + + +def MessageSetItemByteSize(field_number, msg): + # First compute the sizes of the tags. + # There are 2 tags for the beginning and ending of the repeated group, that + # is field number 1, one with field number 2 (type_id) and one with field + # number 3 (message). + total_size = (2 * _TagByteSize(1) + _TagByteSize(2) + _TagByteSize(3)) + + # Add the number of bytes for type_id. + total_size += _VarUInt64ByteSizeNoTag(field_number) + + message_size = msg.ByteSize() + + # The number of bytes for encoding the length of the message. + total_size += _VarUInt64ByteSizeNoTag(message_size) + + # The size of the message. + total_size += message_size + return total_size + + +# Private helper functions for the *ByteSize() functions above. + + +def _TagByteSize(field_number): + """Returns the bytes required to serialize a tag with this field number.""" + # Just pass in type 0, since the type won't affect the tag+type size. + return _VarUInt64ByteSizeNoTag(PackTag(field_number, 0)) + + +def _VarUInt64ByteSizeNoTag(uint64): + """Returns the bytes required to serialize a single varint. + uint64 must be unsigned. + """ + if uint64 > UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % uint64) + bytes = 1 + while uint64 > 0x7f: + bytes += 1 + uint64 >>= 7 + return bytes diff --git a/python/google/protobuf/internal/wire_format_test.py b/python/google/protobuf/internal/wire_format_test.py new file mode 100755 index 00000000..87e0ddf5 --- /dev/null +++ b/python/google/protobuf/internal/wire_format_test.py @@ -0,0 +1,232 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Test for google.protobuf.internal.wire_format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import unittest +from google.protobuf import message +from google.protobuf.internal import wire_format + + +class WireFormatTest(unittest.TestCase): + + def testPackTag(self): + field_number = 0xabc + tag_type = 2 + self.assertEqual((field_number << 3) | tag_type, + wire_format.PackTag(field_number, tag_type)) + PackTag = wire_format.PackTag + # Number too high. + self.assertRaises(message.EncodeError, PackTag, field_number, 6) + # Number too low. + self.assertRaises(message.EncodeError, PackTag, field_number, -1) + + def testUnpackTag(self): + # Test field numbers that will require various varint sizes. + for expected_field_number in (1, 15, 16, 2047, 2048): + for expected_wire_type in range(6): # Highest-numbered wiretype is 5. + field_number, wire_type = wire_format.UnpackTag( + wire_format.PackTag(expected_field_number, expected_wire_type)) + self.assertEqual(expected_field_number, field_number) + self.assertEqual(expected_wire_type, wire_type) + + self.assertRaises(TypeError, wire_format.UnpackTag, None) + self.assertRaises(TypeError, wire_format.UnpackTag, 'abc') + self.assertRaises(TypeError, wire_format.UnpackTag, 0.0) + self.assertRaises(TypeError, wire_format.UnpackTag, object()) + + def testZigZagEncode(self): + Z = wire_format.ZigZagEncode + self.assertEqual(0, Z(0)) + self.assertEqual(1, Z(-1)) + self.assertEqual(2, Z(1)) + self.assertEqual(3, Z(-2)) + self.assertEqual(4, Z(2)) + self.assertEqual(0xfffffffe, Z(0x7fffffff)) + self.assertEqual(0xffffffff, Z(-0x80000000)) + self.assertEqual(0xfffffffffffffffe, Z(0x7fffffffffffffff)) + self.assertEqual(0xffffffffffffffff, Z(-0x8000000000000000)) + + self.assertRaises(TypeError, Z, None) + self.assertRaises(TypeError, Z, 'abcd') + self.assertRaises(TypeError, Z, 0.0) + self.assertRaises(TypeError, Z, object()) + + def testZigZagDecode(self): + Z = wire_format.ZigZagDecode + self.assertEqual(0, Z(0)) + self.assertEqual(-1, Z(1)) + self.assertEqual(1, Z(2)) + self.assertEqual(-2, Z(3)) + self.assertEqual(2, Z(4)) + self.assertEqual(0x7fffffff, Z(0xfffffffe)) + self.assertEqual(-0x80000000, Z(0xffffffff)) + self.assertEqual(0x7fffffffffffffff, Z(0xfffffffffffffffe)) + self.assertEqual(-0x8000000000000000, Z(0xffffffffffffffff)) + + self.assertRaises(TypeError, Z, None) + self.assertRaises(TypeError, Z, 'abcd') + self.assertRaises(TypeError, Z, 0.0) + self.assertRaises(TypeError, Z, object()) + + def NumericByteSizeTestHelper(self, byte_size_fn, value, expected_value_size): + # Use field numbers that cause various byte sizes for the tag information. + for field_number, tag_bytes in ((15, 1), (16, 2), (2047, 2), (2048, 3)): + expected_size = expected_value_size + tag_bytes + actual_size = byte_size_fn(field_number, value) + self.assertEqual(expected_size, actual_size, + 'byte_size_fn: %s, field_number: %d, value: %r\n' + 'Expected: %d, Actual: %d'% ( + byte_size_fn, field_number, value, expected_size, actual_size)) + + def testByteSizeFunctions(self): + # Test all numeric *ByteSize() functions. + NUMERIC_ARGS = [ + # Int32ByteSize(). + [wire_format.Int32ByteSize, 0, 1], + [wire_format.Int32ByteSize, 127, 1], + [wire_format.Int32ByteSize, 128, 2], + [wire_format.Int32ByteSize, -1, 10], + # Int64ByteSize(). + [wire_format.Int64ByteSize, 0, 1], + [wire_format.Int64ByteSize, 127, 1], + [wire_format.Int64ByteSize, 128, 2], + [wire_format.Int64ByteSize, -1, 10], + # UInt32ByteSize(). + [wire_format.UInt32ByteSize, 0, 1], + [wire_format.UInt32ByteSize, 127, 1], + [wire_format.UInt32ByteSize, 128, 2], + [wire_format.UInt32ByteSize, wire_format.UINT32_MAX, 5], + # UInt64ByteSize(). + [wire_format.UInt64ByteSize, 0, 1], + [wire_format.UInt64ByteSize, 127, 1], + [wire_format.UInt64ByteSize, 128, 2], + [wire_format.UInt64ByteSize, wire_format.UINT64_MAX, 10], + # SInt32ByteSize(). + [wire_format.SInt32ByteSize, 0, 1], + [wire_format.SInt32ByteSize, -1, 1], + [wire_format.SInt32ByteSize, 1, 1], + [wire_format.SInt32ByteSize, -63, 1], + [wire_format.SInt32ByteSize, 63, 1], + [wire_format.SInt32ByteSize, -64, 1], + [wire_format.SInt32ByteSize, 64, 2], + # SInt64ByteSize(). + [wire_format.SInt64ByteSize, 0, 1], + [wire_format.SInt64ByteSize, -1, 1], + [wire_format.SInt64ByteSize, 1, 1], + [wire_format.SInt64ByteSize, -63, 1], + [wire_format.SInt64ByteSize, 63, 1], + [wire_format.SInt64ByteSize, -64, 1], + [wire_format.SInt64ByteSize, 64, 2], + # Fixed32ByteSize(). + [wire_format.Fixed32ByteSize, 0, 4], + [wire_format.Fixed32ByteSize, wire_format.UINT32_MAX, 4], + # Fixed64ByteSize(). + [wire_format.Fixed64ByteSize, 0, 8], + [wire_format.Fixed64ByteSize, wire_format.UINT64_MAX, 8], + # SFixed32ByteSize(). + [wire_format.SFixed32ByteSize, 0, 4], + [wire_format.SFixed32ByteSize, wire_format.INT32_MIN, 4], + [wire_format.SFixed32ByteSize, wire_format.INT32_MAX, 4], + # SFixed64ByteSize(). + [wire_format.SFixed64ByteSize, 0, 8], + [wire_format.SFixed64ByteSize, wire_format.INT64_MIN, 8], + [wire_format.SFixed64ByteSize, wire_format.INT64_MAX, 8], + # FloatByteSize(). + [wire_format.FloatByteSize, 0.0, 4], + [wire_format.FloatByteSize, 1000000000.0, 4], + [wire_format.FloatByteSize, -1000000000.0, 4], + # DoubleByteSize(). + [wire_format.DoubleByteSize, 0.0, 8], + [wire_format.DoubleByteSize, 1000000000.0, 8], + [wire_format.DoubleByteSize, -1000000000.0, 8], + # BoolByteSize(). + [wire_format.BoolByteSize, False, 1], + [wire_format.BoolByteSize, True, 1], + # EnumByteSize(). + [wire_format.EnumByteSize, 0, 1], + [wire_format.EnumByteSize, 127, 1], + [wire_format.EnumByteSize, 128, 2], + [wire_format.EnumByteSize, wire_format.UINT32_MAX, 5], + ] + for args in NUMERIC_ARGS: + self.NumericByteSizeTestHelper(*args) + + # Test strings and bytes. + for byte_size_fn in (wire_format.StringByteSize, wire_format.BytesByteSize): + # 1 byte for tag, 1 byte for length, 3 bytes for contents. + self.assertEqual(5, byte_size_fn(10, 'abc')) + # 2 bytes for tag, 1 byte for length, 3 bytes for contents. + self.assertEqual(6, byte_size_fn(16, 'abc')) + # 2 bytes for tag, 2 bytes for length, 128 bytes for contents. + self.assertEqual(132, byte_size_fn(16, 'a' * 128)) + + class MockMessage(object): + def __init__(self, byte_size): + self.byte_size = byte_size + def ByteSize(self): + return self.byte_size + + message_byte_size = 10 + mock_message = MockMessage(byte_size=message_byte_size) + # Test groups. + # (2 * 1) bytes for begin and end tags, plus message_byte_size. + self.assertEqual(2 + message_byte_size, + wire_format.GroupByteSize(1, mock_message)) + # (2 * 2) bytes for begin and end tags, plus message_byte_size. + self.assertEqual(4 + message_byte_size, + wire_format.GroupByteSize(16, mock_message)) + + # Test messages. + # 1 byte for tag, plus 1 byte for length, plus contents. + self.assertEqual(2 + mock_message.byte_size, + wire_format.MessageByteSize(1, mock_message)) + # 2 bytes for tag, plus 1 byte for length, plus contents. + self.assertEqual(3 + mock_message.byte_size, + wire_format.MessageByteSize(16, mock_message)) + # 2 bytes for tag, plus 2 bytes for length, plus contents. + mock_message.byte_size = 128 + self.assertEqual(4 + mock_message.byte_size, + wire_format.MessageByteSize(16, mock_message)) + + + # Test message set item byte size. + # 4 bytes for tags, plus 1 byte for length, plus 1 byte for type_id, + # plus contents. + mock_message.byte_size = 10 + self.assertEqual(mock_message.byte_size + 6, + wire_format.MessageSetItemByteSize(1, mock_message)) + + # 4 bytes for tags, plus 2 bytes for length, plus 1 byte for type_id, + # plus contents. + mock_message.byte_size = 128 + self.assertEqual(mock_message.byte_size + 7, + wire_format.MessageSetItemByteSize(1, mock_message)) + + # 4 bytes for tags, plus 2 bytes for length, plus 2 byte for type_id, + # plus contents. + self.assertEqual(mock_message.byte_size + 8, + wire_format.MessageSetItemByteSize(128, mock_message)) + + # Too-long varint. + self.assertRaises(message.EncodeError, + wire_format.UInt64ByteSize, 1, 1 << 128) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py new file mode 100755 index 00000000..593f6a63 --- /dev/null +++ b/python/google/protobuf/message.py @@ -0,0 +1,232 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +# TODO(robinson): We should just make these methods all "pure-virtual" and move +# all implementation out, into reflection.py for now. + + +"""Contains an abstract base class for protocol messages.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +from google.protobuf import text_format + +class Error(Exception): pass +class DecodeError(Error): pass +class EncodeError(Error): pass + + +class Message(object): + + """Abstract base class for protocol messages. + + Protocol message classes are almost always generated by the protocol + compiler. These generated types subclass Message and implement the methods + shown below. + + TODO(robinson): Link to an HTML document here. + + TODO(robinson): Document that instances of this class will also + have an Extensions attribute with __getitem__ and __setitem__. + Again, not sure how to best convey this. + + TODO(robinson): Document that the class must also have a static + RegisterExtension(extension_field) method. + Not sure how to best express at this point. + """ + + # TODO(robinson): Document these fields and methods. + + __slots__ = [] + + DESCRIPTOR = None + + def __eq__(self, other_msg): + raise NotImplementedError + + def __ne__(self, other_msg): + # Can't just say self != other_msg, since that would infinitely recurse. :) + return not self == other_msg + + def __str__(self): + return text_format.MessageToString(self) + + def MergeFrom(self, other_msg): + """Merges the contents of the specified message into current message. + + This method merges the contents of the specified message into the current + message. Singular fields that are set in the specified message overwrite + the corresponding fields in the current message. Repeated fields are + appended. Singular sub-messages and groups are recursively merged. + + Args: + other_msg: Message to merge into the current message. + """ + raise NotImplementedError + + def CopyFrom(self, other_msg): + """Copies the content of the specified message into the current message. + + The method clears the current message and then merges the specified + message using MergeFrom. + + Args: + other_msg: Message to copy into the current one. + """ + if self == other_msg: + return + self.Clear() + self.MergeFrom(other_msg) + + def Clear(self): + """Clears all data that was set in the message.""" + raise NotImplementedError + + def IsInitialized(self): + """Checks if the message is initialized. + + Returns: + The method returns True if the message is initialized (i.e. all of its + required fields are set). + """ + raise NotImplementedError + + # TODO(robinson): MergeFromString() should probably return None and be + # implemented in terms of a helper that returns the # of bytes read. Our + # deserialization routines would use the helper when recursively + # deserializing, but the end user would almost always just want the no-return + # MergeFromString(). + + def MergeFromString(self, serialized): + """Merges serialized protocol buffer data into this message. + + When we find a field in |serialized| that is already present + in this message: + - If it's a "repeated" field, we append to the end of our list. + - Else, if it's a scalar, we overwrite our field. + - Else, (it's a nonrepeated composite), we recursively merge + into the existing composite. + + TODO(robinson): Document handling of unknown fields. + + Args: + serialized: Any object that allows us to call buffer(serialized) + to access a string of bytes using the buffer interface. + + TODO(robinson): When we switch to a helper, this will return None. + + Returns: + The number of bytes read from |serialized|. + For non-group messages, this will always be len(serialized), + but for messages which are actually groups, this will + generally be less than len(serialized), since we must + stop when we reach an END_GROUP tag. Note that if + we *do* stop because of an END_GROUP tag, the number + of bytes returned does not include the bytes + for the END_GROUP tag information. + """ + raise NotImplementedError + + def ParseFromString(self, serialized): + """Like MergeFromString(), except we clear the object first.""" + self.Clear() + self.MergeFromString(serialized) + + def SerializeToString(self): + """Serializes the protocol message to a binary string. + + Returns: + A binary string representation of the message if all of the required + fields in the message are set (i.e. the message is initialized). + + Raises: + message.EncodeError if the message isn't initialized. + """ + raise NotImplementedError + + def SerializePartialToString(self): + """Serializes the protocol message to a binary string. + + This method is similar to SerializeToString but doesn't check if the + message is initialized. + + Returns: + A string representation of the partial message. + """ + raise NotImplementedError + + # TODO(robinson): Decide whether we like these better + # than auto-generated has_foo() and clear_foo() methods + # on the instances themselves. This way is less consistent + # with C++, but it makes reflection-type access easier and + # reduces the number of magically autogenerated things. + # + # TODO(robinson): Be sure to document (and test) exactly + # which field names are accepted here. Are we case-sensitive? + # What do we do with fields that share names with Python keywords + # like 'lambda' and 'yield'? + # + # nnorwitz says: + # """ + # Typically (in python), an underscore is appended to names that are + # keywords. So they would become lambda_ or yield_. + # """ + def ListFields(self, field_name): + """Returns a list of (FieldDescriptor, value) tuples for all + fields in the message which are not empty. A singular field is non-empty + if HasField() would return true, and a repeated field is non-empty if + it contains at least one element. The fields are ordered by field + number""" + raise NotImplementedError + + def HasField(self, field_name): + raise NotImplementedError + + def ClearField(self, field_name): + raise NotImplementedError + + def HasExtension(self, extension_handle): + raise NotImplementedError + + def ClearExtension(self, extension_handle): + raise NotImplementedError + + def ByteSize(self): + """Returns the serialized size of this message. + Recursively calls ByteSize() on all contained messages. + """ + raise NotImplementedError + + def _SetListener(self, message_listener): + """Internal method used by the protocol message implementation. + Clients should not call this directly. + + Sets a listener that this message will call on certain state transitions. + + The purpose of this method is to register back-edges from children to + parents at runtime, for the purpose of setting "has" bits and + byte-size-dirty bits in the parent and ancestor objects whenever a child or + descendant object is modified. + + If the client wants to disconnect this Message from the object tree, she + explicitly sets callback to None. + + If message_listener is None, unregisters any existing listener. Otherwise, + message_listener must implement the MessageListener interface in + internal/message_listener.py, and we discard any listener registered + via a previous _SetListener() call. + """ + raise NotImplementedError diff --git a/python/google/protobuf/reflection.py b/python/google/protobuf/reflection.py new file mode 100755 index 00000000..ef054466 --- /dev/null +++ b/python/google/protobuf/reflection.py @@ -0,0 +1,1627 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +# This code is meant to work on Python 2.4 and above only. +# +# TODO(robinson): Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import heapq +import threading +import weakref +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import type_checkers +from google.protobuf.internal import wire_format +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod + +_FieldDescriptor = descriptor_mod.FieldDescriptor + + +class GeneratedProtocolMessageType(type): + + """Metaclass for protocol message classes created at runtime from Descriptors. + + We add implementations for all methods described in the Message class. We + also create properties to allow getting/setting all fields in the protocol + message. Finally, we create slots to prevent users from accidentally + "setting" nonexistent fields in the protocol message, which then wouldn't get + serialized / deserialized properly. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + class MyProtoClass(Message): + __metaclass__ = GeneratedProtocolMessageType + DESCRIPTOR = mydescriptor + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __new__(cls, name, bases, dictionary): + """Custom allocation for runtime-generated class types. + + We override __new__ because this is apparently the only place + where we can meaningfully set __slots__ on the class we're creating(?). + (The interplay between metaclasses and slots is not very well-documented). + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + + Returns: + Newly-allocated class. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + _AddSlots(descriptor, dictionary) + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + superclass = super(GeneratedProtocolMessageType, cls) + return superclass.__new__(cls, name, bases, dictionary) + + def __init__(cls, name, bases, dictionary): + """Here we perform the majority of our work on the class. + We add enum getters, an __init__ method, implementations + of all Message methods, and properties for all fields + in the protocol type. + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + # We act as a "friend" class of the descriptor, setting + # its _concrete_class attribute the first time we use a + # given descriptor to initialize a concrete protocol message + # class. + concrete_class_attr_name = '_concrete_class' + if not hasattr(descriptor, concrete_class_attr_name): + setattr(descriptor, concrete_class_attr_name, cls) + cls._known_extensions = [] + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(cls) + superclass = super(GeneratedProtocolMessageType, cls) + superclass.__init__(cls, name, bases, dictionary) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + return proto_field_name + + +def _ValueFieldName(proto_field_name): + """Returns the name of the (internal) instance attribute which objects + should use to store the current value for a given protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + return '_value_' + proto_field_name + + +def _HasFieldName(proto_field_name): + """Returns the name of the (internal) instance attribute which + objects should use to store a boolean telling whether this field + is explicitly set or not. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + return '_has_' + proto_field_name + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + field_names = [_ValueFieldName(f.name) for f in message_descriptor.fields] + field_names.extend(_HasFieldName(f.name) for f in message_descriptor.fields + if f.label != _FieldDescriptor.LABEL_REPEATED) + field_names.extend(('Extensions', + '_cached_byte_size', + '_cached_byte_size_dirty', + '_called_transition_to_nonempty', + '_listener', + '_lock', '__weakref__')) + dictionary['__slots__'] = field_names + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extension_dict = descriptor.extensions_by_name + for extension_name, extension_field in extension_dict.iteritems(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _DefaultValueForField(message, field): + """Returns a default value for a field. + + Args: + message: Message instance containing this field, or a weakref proxy + of same. + field: FieldDescriptor object for this field. + + Returns: A default value for this field. May refer back to |message| + via a weak reference. + """ + # TODO(robinson): Only the repeated fields need a reference to 'message' (so + # that they can set the 'has' bit on the containing Message when someone + # append()s a value). We could special-case this, and avoid an extra + # function call on __init__() and Clear() for non-repeated fields. + + # TODO(robinson): Find a better place for the default value assertion in this + # function. No need to repeat them every time the client calls Clear('foo'). + # (We should probably just assert these things once and as early as possible, + # by tightening checking in the descriptor classes.) + if field.label == _FieldDescriptor.LABEL_REPEATED: + if field.default_value != []: + raise ValueError('Repeated field default value not empty list: %s' % ( + field.default_value)) + listener = _Listener(message, None) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + return _RepeatedCompositeFieldContainer(listener, field.message_type) + else: + return _RepeatedScalarFieldContainer( + listener, type_checkers.VALUE_CHECKERS[field.cpp_type]) + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + assert field.default_value is None + + return field.default_value + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + fields = message_descriptor.fields + def init(self): + self._cached_byte_size = 0 + self._cached_byte_size_dirty = False + self._listener = message_listener_mod.NullMessageListener() + self._called_transition_to_nonempty = False + # TODO(robinson): We should only create a lock if we really need one + # in this class. + self._lock = threading.Lock() + for field in fields: + default_value = _DefaultValueForField(self, field) + python_field_name = _ValueFieldName(field.name) + setattr(self, python_field_name, default_value) + if field.label != _FieldDescriptor.LABEL_REPEATED: + setattr(self, _HasFieldName(field.name), False) + self.Extensions = _ExtensionDict(self, cls._known_extensions) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + Clients can use this property to get and (in the case + of non-repeated scalar fields) directly set the value + of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + if field.label == _FieldDescriptor.LABEL_REPEATED: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. Clients + can use this property to get the value of the field, which will be either a + _RepeatedScalarFieldContainer or _RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + + def getter(self): + return getattr(self, python_field_name) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + type_checker = type_checkers.VALUE_CHECKERS[field.cpp_type] + + def getter(self): + return getattr(self, python_field_name) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + def setter(self, new_value): + type_checker.CheckValue(new_value) + setattr(self, has_field_name, True) + self._MarkByteSizeDirty() + self._MaybeCallTransitionToNonemptyCallback() + setattr(self, python_field_name, new_value) + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO(robinson): Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + property_name = _PropertyName(proto_field_name) + message_type = field.message_type + + def getter(self): + # TODO(robinson): Appropriately scary note about double-checked locking. + field_value = getattr(self, python_field_name) + if field_value is None: + self._lock.acquire() + try: + field_value = getattr(self, python_field_name) + if field_value is None: + field_class = message_type._concrete_class + field_value = field_class() + field_value._SetListener(_Listener(self, has_field_name)) + setattr(self, python_field_name, field_value) + finally: + self._lock.release() + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, property(getter, setter, doc=doc)) + + +def _AddStaticMethods(cls): + # TODO(robinson): This probably needs to be thread-safe(?) + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + cls._known_extensions.append(extension_handle) + cls.RegisterExtension = staticmethod(RegisterExtension) + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + # Ensure that we always list in ascending field-number order. + # For non-extension fields, we can do the sort once, here, at import-time. + # For extensions, we sort on each ListFields() call, though + # we could do better if we have to. + fields = sorted(message_descriptor.fields, key=lambda f: f.number) + has_field_names = (_HasFieldName(f.name) for f in fields) + value_field_names = (_ValueFieldName(f.name) for f in fields) + triplets = zip(has_field_names, value_field_names, fields) + + def ListFields(self): + # We need to list all extension and non-extension fields + # together, in sorted order by field number. + + # Step 0: Get an iterator over all "set" non-extension fields, + # sorted by field number. + # This iterator yields (field_number, field_descriptor, value) tuples. + def SortedSetFieldsIter(): + # Note that triplets is already sorted by field number. + for has_field_name, value_field_name, field_descriptor in triplets: + if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED: + value = getattr(self, _ValueFieldName(field_descriptor.name)) + if len(value) > 0: + yield (field_descriptor.number, field_descriptor, value) + elif getattr(self, _HasFieldName(field_descriptor.name)): + value = getattr(self, _ValueFieldName(field_descriptor.name)) + yield (field_descriptor.number, field_descriptor, value) + sorted_fields = SortedSetFieldsIter() + + # Step 1: Get an iterator over all "set" extension fields, + # sorted by field number. + # This iterator ALSO yields (field_number, field_descriptor, value) tuples. + # TODO(robinson): It's not necessary to repeat this with each + # serialization call. We can do better. + sorted_extension_fields = sorted( + [(f.number, f, v) for f, v in self.Extensions._ListSetExtensions()]) + + # Step 2: Create a composite iterator that merges the extension- + # and non-extension fields, and that still yields fields in + # sorted order. + all_set_fields = _ImergeSorted(sorted_fields, sorted_extension_fields) + + # Step 3: Strip off the field numbers and return. + return [field[1:] for field in all_set_fields] + + cls.ListFields = ListFields + +def _AddHasFieldMethod(cls): + """Helper for _AddMessageMethods().""" + def HasField(self, field_name): + try: + return getattr(self, _HasFieldName(field_name)) + except AttributeError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + cls.HasField = HasField + + +def _AddClearFieldMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearField(self, field_name): + try: + field = self.DESCRIPTOR.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message has no "%s" field.' % field_name) + proto_field_name = field.name + python_field_name = _ValueFieldName(proto_field_name) + has_field_name = _HasFieldName(proto_field_name) + default_value = _DefaultValueForField(self, field) + if field.label == _FieldDescriptor.LABEL_REPEATED: + self._MarkByteSizeDirty() + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + old_field_value = getattr(self, python_field_name) + if old_field_value is not None: + # Snip the old object out of the object tree. + old_field_value._SetListener(None) + if getattr(self, has_field_name): + setattr(self, has_field_name, False) + # Set dirty bit on ourself and parents only if + # we're actually changing state. + self._MarkByteSizeDirty() + setattr(self, python_field_name, default_value) + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearExtension(self, extension_handle): + self.Extensions._ClearExtension(extension_handle) + cls.ClearExtension = ClearExtension + + +def _AddClearMethod(cls): + """Helper for _AddMessageMethods().""" + def Clear(self): + # Clear fields. + fields = self.DESCRIPTOR.fields + for field in fields: + self.ClearField(field.name) + # Clear extensions. + extensions = self.Extensions._ListSetExtensions() + for extension in extensions: + self.ClearExtension(extension[0]) + cls.Clear = Clear + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def HasExtension(self, extension_handle): + return self.Extensions._HasExtension(extension_handle) + cls.HasExtension = HasExtension + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __eq__(self, other): + if self is other: + return True + + # Compare all fields contained directly in this message. + for field_descriptor in message_descriptor.fields: + label = field_descriptor.label + property_name = _PropertyName(field_descriptor.name) + # Non-repeated field equality requires matching "has" bits as well + # as having an equal value. + if label != _FieldDescriptor.LABEL_REPEATED: + self_has = self.HasField(property_name) + other_has = other.HasField(property_name) + if self_has != other_has: + return False + if not self_has: + # If the "has" bit for this field is False, we must stop here. + # Otherwise we will recurse forever on recursively-defined protos. + continue + if getattr(self, property_name) != getattr(other, property_name): + return False + + # Compare the extensions present in both messages. + return self.Extensions == other.Extensions + cls.__eq__ = __eq__ + + +def _AddSetListenerMethod(cls): + """Helper for _AddMessageMethods().""" + def SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + cls._SetListener = SetListener + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + The returned byte count includes space for tag information and any + other additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number + is stored as part of a varint-encoded tag, this has an impact + on the total bytes required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants + within FieldDescriptor. + """ + try: + fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def BytesForField(message, field, value): + """Returns the number of bytes required to serialize a single field + in message. The field may be repeated or not, composite or not. + + Args: + message: The Message instance containing a field of the given type. + field: A FieldDescriptor describing the field of interest. + value: The value whose byte size we're interested in. + + Returns: The number of bytes required to serialize the current value + of "field" in "message", including space for tags and any other + necessary information. + """ + + if _MessageSetField(field): + return wire_format.MessageSetItemByteSize(field.number, value) + + field_number, field_type = field.number, field.type + + # Repeated fields. + if field.label == _FieldDescriptor.LABEL_REPEATED: + elements = value + else: + elements = [value] + + size = sum(_BytesForNonRepeatedElement(element, field_number, field_type) + for element in elements) + return size + + fields = message_descriptor.fields + has_field_names = (_HasFieldName(f.name) for f in fields) + zipped = zip(has_field_names, fields) + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + # Hardcoded fields first. + for has_field_name, field in zipped: + if (field.label == _FieldDescriptor.LABEL_REPEATED + or getattr(self, has_field_name)): + value = getattr(self, _ValueFieldName(field.name)) + size += BytesForField(self, field, value) + # Extensions next. + for field, value in self.Extensions._ListSetExtensions(): + size += BytesForField(self, field, value) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + return size + cls.ByteSize = ByteSize + + +def _MessageSetField(field_descriptor): + """Checks if a field should be serialized using the message set wire format. + + Args: + field_descriptor: Descriptor of the field. + + Returns: + True if the field should be serialized using the message set wire format, + false otherwise. + """ + return (field_descriptor.is_extension and + field_descriptor.label != _FieldDescriptor.LABEL_REPEATED and + field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and + field_descriptor.containing_type.GetOptions().message_set_wire_format) + + +def _SerializeValueToEncoder(value, field_number, field_descriptor, encoder): + """Appends the serialization of a single value to encoder. + + Args: + value: Value to serialize. + field_number: Field number of this value. + field_descriptor: Descriptor of the field to serialize. + encoder: encoder.Encoder object to which we should serialize this value. + """ + if _MessageSetField(field_descriptor): + encoder.AppendMessageSetItem(field_number, value) + return + + try: + method = type_checkers.TYPE_TO_SERIALIZE_METHOD[field_descriptor.type] + method(encoder, field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % + field_descriptor.type) + + +def _ImergeSorted(*streams): + """Merges N sorted iterators into a single sorted iterator. + Each element in streams must be an iterable that yields + its elements in sorted order, and the elements contained + in each stream must all be comparable. + + There may be repeated elements in the component streams or + across the streams; the repeated elements will all be repeated + in the merged iterator as well. + + I believe that the heapq module at HEAD in the Python + sources has a method like this, but for now we roll our own. + """ + iters = [iter(stream) for stream in streams] + heap = [] + for index, it in enumerate(iters): + try: + heap.append((it.next(), index)) + except StopIteration: + pass + heapq.heapify(heap) + + while heap: + smallest_value, idx = heap[0] + yield smallest_value + try: + next_element = iters[idx].next() + heapq.heapreplace(heap, (next_element, idx)) + except StopIteration: + heapq.heappop(heap) + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializeToString(self): + # Check if the message has all of its required fields set. + errors = [] + if not _InternalIsInitialized(self, errors): + raise message_mod.EncodeError('\n'.join(errors)) + return self.SerializePartialToString() + cls.SerializeToString = SerializeToString + + +def _AddSerializePartialToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + Encoder = encoder.Encoder + + def SerializePartialToString(self): + encoder = Encoder() + # We need to serialize all extension and non-extension fields + # together, in sorted order by field number. + for field_descriptor, field_value in self.ListFields(): + if field_descriptor.label == _FieldDescriptor.LABEL_REPEATED: + repeated_value = field_value + else: + repeated_value = [field_value] + for element in repeated_value: + _SerializeValueToEncoder(element, field_descriptor.number, + field_descriptor, encoder) + return encoder.ToString() + cls.SerializePartialToString = SerializePartialToString + + +def _WireTypeForFieldType(field_type): + """Given a field type, returns the expected wire type.""" + try: + return type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_type] + except KeyError: + raise message_mod.DecodeError('Unknown field type: %d' % field_type) + + +def _RecursivelyMerge(field_number, field_type, decoder, message): + """Decodes a message from decoder into message. + message is either a group or a nested message within some containing + protocol message. If it's a group, we use the group protocol to + deserialize, and if it's a nested message, we use the nested-message + protocol. + + Args: + field_number: The field number of message in its enclosing protocol buffer. + field_type: The field type of message. Must be either TYPE_MESSAGE + or TYPE_GROUP. + decoder: Decoder to read from. + message: Message to deserialize into. + """ + if field_type == _FieldDescriptor.TYPE_MESSAGE: + decoder.ReadMessageInto(message) + elif field_type == _FieldDescriptor.TYPE_GROUP: + decoder.ReadGroupInto(field_number, message) + else: + raise message_mod.DecodeError('Unexpected field type: %d' % field_type) + + +def _DeserializeScalarFromDecoder(field_type, decoder): + """Deserializes a scalar of the requested type from decoder. field_type must + be a scalar (non-group, non-message) FieldDescriptor.FIELD_* constant. + """ + try: + method = type_checkers.TYPE_TO_DESERIALIZE_METHOD[field_type] + return method(decoder) + except KeyError: + raise message_mod.DecodeError('Unrecognized field type: %d' % field_type) + + +def _SkipField(field_number, wire_type, decoder): + """Skips a field with the specified wire type. + + Args: + field_number: Tag number of the field to skip. + wire_type: Wire type of the field to skip. + decoder: Decoder used to deserialize the messsage. It must be positioned + just after reading the the tag and wire type of the field. + """ + if wire_type == wire_format.WIRETYPE_VARINT: + decoder.ReadInt32() + elif wire_type == wire_format.WIRETYPE_FIXED64: + decoder.ReadFixed64() + elif wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + decoder.SkipBytes(decoder.ReadInt32()) + elif wire_type == wire_format.WIRETYPE_START_GROUP: + _SkipGroup(field_number, decoder) + elif wire_type == wire_format.WIRETYPE_END_GROUP: + pass + elif wire_type == wire_format.WIRETYPE_FIXED32: + decoder.ReadFixed32() + else: + raise message_mod.DecodeError('Unexpected wire type: %d' % wire_type) + + +def _SkipGroup(group_number, decoder): + """Skips a nested group from the decoder. + + Args: + group_number: Tag number of the group to skip. + decoder: Decoder used to deserialize the message. It must be positioned + exactly at the beginning of the message that should be skipped. + """ + while True: + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if (wire_type == wire_format.WIRETYPE_END_GROUP and + field_number == group_number): + return + _SkipField(field_number, wire_type, decoder) + + +def _DeserializeMessageSetItem(message, decoder): + """Deserializes a message using the message set wire format. + + Args: + message: Message to be parsed to. + decoder: The decoder to be used to deserialize encoded data. Note that the + decoder should be positioned just after reading the START_GROUP tag that + began the messageset item. + """ + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_VARINT or field_number != 2: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + type_id = decoder.ReadInt32() + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_LENGTH_DELIMITED or field_number != 3: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + extension_dict = message.Extensions + extensions_by_number = extension_dict._AllExtensionsByNumber() + if type_id not in extensions_by_number: + _SkipField(field_number, wire_type, decoder) + return + + field_descriptor = extensions_by_number[type_id] + value = extension_dict[field_descriptor] + decoder.ReadMessageInto(value) + # Read the END_GROUP tag. + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + if wire_type != wire_format.WIRETYPE_END_GROUP or field_number != 1: + raise message_mod.DecodeError( + 'Incorrect message set wire format. ' + 'wire_type: %d, field_number: %d' % (wire_type, field_number)) + + +def _DeserializeOneEntity(message_descriptor, message, decoder): + """Deserializes the next wire entity from decoder into message. + The next wire entity is either a scalar or a nested message, + and may also be an element in a repeated field (the wire encoding + is the same). + + Args: + message_descriptor: A Descriptor instance describing all fields + in message. + message: The Message instance into which we're decoding our fields. + decoder: The Decoder we're using to deserialize encoded data. + + Returns: The number of bytes read from decoder during this method. + """ + initial_position = decoder.Position() + field_number, wire_type = decoder.ReadFieldNumberAndWireType() + extension_dict = message.Extensions + extensions_by_number = extension_dict._AllExtensionsByNumber() + if field_number in message_descriptor.fields_by_number: + # Non-extension field. + field_descriptor = message_descriptor.fields_by_number[field_number] + value = getattr(message, _PropertyName(field_descriptor.name)) + def nonextension_setter_fn(scalar): + setattr(message, _PropertyName(field_descriptor.name), scalar) + scalar_setter_fn = nonextension_setter_fn + elif field_number in extensions_by_number: + # Extension field. + field_descriptor = extensions_by_number[field_number] + value = extension_dict[field_descriptor] + def extension_setter_fn(scalar): + extension_dict[field_descriptor] = scalar + scalar_setter_fn = extension_setter_fn + elif wire_type == wire_format.WIRETYPE_END_GROUP: + # We assume we're being parsed as the group that's ended. + return 0 + elif (wire_type == wire_format.WIRETYPE_START_GROUP and + field_number == 1 and + message_descriptor.GetOptions().message_set_wire_format): + # A Message Set item. + _DeserializeMessageSetItem(message, decoder) + return decoder.Position() - initial_position + else: + _SkipField(field_number, wire_type, decoder) + return decoder.Position() - initial_position + + # If we reach this point, we've identified the field as either + # hardcoded or extension, and set |field_descriptor|, |scalar_setter_fn|, + # and |value| appropriately. Now actually deserialize the thing. + # + # field_descriptor: Describes the field we're deserializing. + # value: The value currently stored in the field to deserialize. + # Used only if the field is composite and/or repeated. + # scalar_setter_fn: A function F such that F(scalar) will + # set a nonrepeated scalar value for this field. Used only + # if this field is a nonrepeated scalar. + + field_number = field_descriptor.number + field_type = field_descriptor.type + expected_wire_type = _WireTypeForFieldType(field_type) + if wire_type != expected_wire_type: + # Need to fill in uninterpreted_bytes. Work for the next CL. + raise RuntimeError('TODO(robinson): Wiretype mismatches not handled.') + + property_name = _PropertyName(field_descriptor.name) + label = field_descriptor.label + cpp_type = field_descriptor.cpp_type + + # Nonrepeated scalar. Just set the field directly. + if (label != _FieldDescriptor.LABEL_REPEATED + and cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + scalar_setter_fn(_DeserializeScalarFromDecoder(field_type, decoder)) + return decoder.Position() - initial_position + + # Nonrepeated composite. Recursively deserialize. + if label != _FieldDescriptor.LABEL_REPEATED: + composite = value + _RecursivelyMerge(field_number, field_type, decoder, composite) + return decoder.Position() - initial_position + + # Now we know we're dealing with a repeated field of some kind. + element_list = value + + if cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE: + # Repeated scalar. + element_list.append(_DeserializeScalarFromDecoder(field_type, decoder)) + return decoder.Position() - initial_position + else: + # Repeated composite. + composite = element_list.add() + _RecursivelyMerge(field_number, field_type, decoder, composite) + return decoder.Position() - initial_position + + +def _FieldOrExtensionValues(message, field_or_extension): + """Retrieves the list of values for the specified field or extension. + + The target field or extension can be optional, required or repeated, but it + must have value(s) set. The assumption is that the target field or extension + is set (e.g. _HasFieldOrExtension holds true). + + Args: + message: Message which contains the target field or extension. + field_or_extension: Field or extension for which the list of values is + required. Must be an instance of FieldDescriptor. + + Returns: + A list of values for the specified field or extension. This list will only + contain a single element if the field is non-repeated. + """ + if field_or_extension.is_extension: + value = message.Extensions[field_or_extension] + else: + value = getattr(message, _ValueFieldName(field_or_extension.name)) + if field_or_extension.label != _FieldDescriptor.LABEL_REPEATED: + return [value] + else: + # In this case value is a list or repeated values. + return value + + +def _HasFieldOrExtension(message, field_or_extension): + """Checks if a message has the specified field or extension set. + + The field or extension specified can be optional, required or repeated. If + it is repeated, this function returns True. Otherwise it checks the has bit + of the field or extension. + + Args: + message: Message which contains the target field or extension. + field_or_extension: Field or extension to check. This must be a + FieldDescriptor instance. + + Returns: + True if the message has a value set for the specified field or extension, + or if the field or extension is repeated. + """ + if field_or_extension.label == _FieldDescriptor.LABEL_REPEATED: + return True + if field_or_extension.is_extension: + return message.HasExtension(field_or_extension) + else: + return message.HasField(field_or_extension.name) + + +def _IsFieldOrExtensionInitialized(message, field, errors=None): + """Checks if a message field or extension is initialized. + + Args: + message: The message which contains the field or extension. + field: Field or extension to check. This must be a FieldDescriptor instance. + errors: Errors will be appended to it, if set to a meaningful value. + + Returns: + True if the field/extension can be considered initialized. + """ + # If the field is required and is not set, it isn't initialized. + if field.label == _FieldDescriptor.LABEL_REQUIRED: + if not _HasFieldOrExtension(message, field): + if errors is not None: + errors.append('Required field %s is not set.' % field.full_name) + return False + + # If the field is optional and is not set, or if it + # isn't a submessage then the field is initialized. + if field.label == _FieldDescriptor.LABEL_OPTIONAL: + if not _HasFieldOrExtension(message, field): + return True + if field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE: + return True + + # The field is set and is either a single or a repeated submessage. + messages = _FieldOrExtensionValues(message, field) + # If all submessages in this field are initialized, the field is + # considered initialized. + for message in messages: + if not _InternalIsInitialized(message, errors): + return False + return True + + +def _InternalIsInitialized(message, errors=None): + """Checks if all required fields of a message are set. + + Args: + message: The message to check. + errors: If set, initialization errors will be appended to it. + + Returns: + True iff the specified message has all required fields set. + """ + fields_and_extensions = [] + fields_and_extensions.extend(message.DESCRIPTOR.fields) + fields_and_extensions.extend( + [extension[0] for extension in message.Extensions._ListSetExtensions()]) + for field_or_extension in fields_and_extensions: + if not _IsFieldOrExtensionInitialized(message, field_or_extension, errors): + return False + return True + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + Decoder = decoder.Decoder + def MergeFromString(self, serialized): + decoder = Decoder(serialized) + byte_count = 0 + while not decoder.EndOfStream(): + bytes_read = _DeserializeOneEntity(message_descriptor, self, decoder) + if not bytes_read: + break + byte_count += bytes_read + return byte_count + cls.MergeFromString = MergeFromString + + +def _AddIsInitializedMethod(cls): + """Adds the IsInitialized method to the protocol message class.""" + cls.IsInitialized = _InternalIsInitialized + + +def _MergeFieldOrExtension(destination_msg, field, value): + """Merges a specified message field into another message.""" + property_name = _PropertyName(field.name) + is_extension = field.is_extension + + if not is_extension: + destination = getattr(destination_msg, property_name) + elif (field.label == _FieldDescriptor.LABEL_REPEATED or + field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE): + destination = destination_msg.Extensions[field] + + # Case 1 - a composite field. + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.label == _FieldDescriptor.LABEL_REPEATED: + for v in value: + destination.add().MergeFrom(v) + else: + destination.MergeFrom(value) + return + + # Case 2 - a repeated field. + if field.label == _FieldDescriptor.LABEL_REPEATED: + for v in value: + destination.append(v) + return + + # Case 3 - a singular field. + if is_extension: + destination_msg.Extensions[field] = value + else: + setattr(destination_msg, property_name, value) + + +def _AddMergeFromMethod(cls): + def MergeFrom(self, msg): + assert msg is not self + for field in msg.ListFields(): + _MergeFieldOrExtension(self, field[0], field[1]) + cls.MergeFrom = MergeFrom + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(cls) + _AddClearFieldMethod(cls) + _AddClearExtensionMethod(cls) + _AddClearMethod(cls) + _AddHasExtensionMethod(cls) + _AddEqualsMethod(message_descriptor, cls) + _AddSetListenerMethod(cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddSerializePartialToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(cls) + _AddMergeFromMethod(cls) + + +def _AddPrivateHelperMethods(cls): + """Adds implementation of private helper methods to cls.""" + + def MaybeCallTransitionToNonemptyCallback(self): + """Calls self._listener.TransitionToNonempty() the first time this + method is called. On all subsequent calls, this is a no-op. + """ + if not self._called_transition_to_nonempty: + self._listener.TransitionToNonempty() + self._called_transition_to_nonempty = True + cls._MaybeCallTransitionToNonemptyCallback = ( + MaybeCallTransitionToNonemptyCallback) + + def MarkByteSizeDirty(self): + """Sets the _cached_byte_size_dirty bit to true, + and propagates this to our listener iff this was a state change. + """ + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener.ByteSizeDirty() + cls._MarkByteSizeDirty = MarkByteSizeDirty + + +class _Listener(object): + + """MessageListener implementation that a parent message registers with its + child message. + + In order to support semantics like: + + foo.bar.baz = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message, has_field_name): + """Args: + parent_message: The message whose _MaybeCallTransitionToNonemptyCallback() + and _MarkByteSizeDirty() methods we should call when we receive + TransitionToNonempty() and ByteSizeDirty() messages. + has_field_name: The name of the "has" field that we should set in + the parent message when we receive a TransitionToNonempty message, + or None if there's no "has" field to set. (This will be the case + for child objects in "repeated" fields). + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + self._has_field_name = has_field_name + + def TransitionToNonempty(self): + try: + if self._has_field_name is not None: + setattr(self._parent_message_weakref, self._has_field_name, True) + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._MaybeCallTransitionToNonemptyCallback() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + def ByteSizeDirty(self): + try: + self._parent_message_weakref._MarkByteSizeDirty() + except ReferenceError: + # Same as above. + pass + + +# TODO(robinson): Move elsewhere? +# TODO(robinson): Provide a clear() method here in addition to ClearField()? +class _RepeatedScalarFieldContainer(object): + + """Simple, type-checked, list-like container for holding repeated scalars. + """ + + def __init__(self, message_listener, type_checker): + """ + Args: + message_listener: A MessageListener implementation. + The _RepeatedScalarFieldContaininer will call this object's + TransitionToNonempty() method when it transitions from being empty to + being nonempty. + type_checker: A _ValueChecker instance to run on elements inserted + into this container. + """ + self._message_listener = message_listener + self._type_checker = type_checker + self._values = [] + + def append(self, elem): + self._type_checker.CheckValue(elem) + self._values.append(elem) + self._message_listener.ByteSizeDirty() + if len(self._values) == 1: + self._message_listener.TransitionToNonempty() + + # List-like __getitem__() support also makes us iterable (via "iter(foo)" + # or implicitly via "for i in mylist:") for free. + def __getitem__(self, key): + return self._values[key] + + def __setitem__(self, key, value): + # No need to call TransitionToNonempty(), since if we're able to + # set the element at this index, we were already nonempty before + # this method was called. + self._message_listener.ByteSizeDirty() + self._type_checker.CheckValue(value) + self._values[key] = value + + def __len__(self): + return len(self._values) + + def __eq__(self, other): + if self is other: + return True + # Special case for the same type which should be common and fast. + if isinstance(other, self.__class__): + return other._values == self._values + # We are presumably comparing against some other sequence type. + return other == self._values + + def __ne__(self, other): + # Can't use != here since it would infinitely recurse. + return not self == other + + +# TODO(robinson): Move elsewhere? +# TODO(robinson): Provide a clear() method here in addition to ClearField()? +# TODO(robinson): Unify common functionality with +# _RepeatedScalarFieldContaininer? +class _RepeatedCompositeFieldContainer(object): + + """Simple, list-like container for holding repeated composite fields. + """ + + def __init__(self, message_listener, message_descriptor): + """Note that we pass in a descriptor instead of the generated directly, + since at the time we construct a _RepeatedCompositeFieldContainer we + haven't yet necessarily initialized the type that will be contained in the + container. + + Args: + message_listener: A MessageListener implementation. + The _RepeatedCompositeFieldContainer will call this object's + TransitionToNonempty() method when it transitions from being empty to + being nonempty. + message_descriptor: A Descriptor instance describing the protocol type + that should be present in this container. We'll use the + _concrete_class field of this descriptor when the client calls add(). + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._values = [] + + def add(self): + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values.append(new_element) + self._message_listener.ByteSizeDirty() + self._message_listener.TransitionToNonempty() + return new_element + + # List-like __getitem__() support also makes us iterable (via "iter(foo)" + # or implicitly via "for i in mylist:") for free. + def __getitem__(self, key): + return self._values[key] + + def __len__(self): + return len(self._values) + + def __eq__(self, other): + if self is other: + return True + if not isinstance(other, self.__class__): + raise TypeError('Can only compare repeated composite fields against ' + 'other repeated composite fields.') + return self._values == other._values + + def __ne__(self, other): + # Can't use != here since it would infinitely recurse. + return not self == other + + # TODO(robinson): Implement, document, and test slicing support. + + +# TODO(robinson): Move elsewhere? This file is getting pretty ridiculous... +# TODO(robinson): Unify error handling of "unknown extension" crap. +# TODO(robinson): There's so much similarity between the way that +# extensions behave and the way that normal fields behave that it would +# be really nice to unify more code. It's not immediately obvious +# how to do this, though, and I'd rather get the full functionality +# implemented (and, crucially, get all the tests and specs fleshed out +# and passing), and then come back to this thorny unification problem. +# TODO(robinson): Support iteritems()-style iteration over all +# extensions with the "has" bits turned on? +class _ExtensionDict(object): + + """Dict-like container for supporting an indexable "Extensions" + field on proto instances. + + Note that in all cases we expect extension handles to be + FieldDescriptors. + """ + + class _ExtensionListener(object): + + """Adapts an _ExtensionDict to behave as a MessageListener.""" + + def __init__(self, extension_dict, handle_id): + self._extension_dict = extension_dict + self._handle_id = handle_id + + def TransitionToNonempty(self): + self._extension_dict._SubmessageTransitionedToNonempty(self._handle_id) + + def ByteSizeDirty(self): + self._extension_dict._SubmessageByteSizeBecameDirty() + + # TODO(robinson): Somewhere, we need to blow up if people + # try to register two extensions with the same field number. + # (And we need a test for this of course). + + def __init__(self, extended_message, known_extensions): + """extended_message: Message instance for which we are the Extensions dict. + known_extensions: Iterable of known extension handles. + These must be FieldDescriptors. + """ + # We keep a weak reference to extended_message, since + # it has a reference to this instance in turn. + self._extended_message = weakref.proxy(extended_message) + # We make a deep copy of known_extensions to avoid any + # thread-safety concerns, since the argument passed in + # is the global (class-level) dict of known extensions for + # this type of message, which could be modified at any time + # via a RegisterExtension() call. + # + # This dict maps from handle id to handle (a FieldDescriptor). + # + # XXX + # TODO(robinson): This isn't good enough. The client could + # instantiate an object in module A, then afterward import + # module B and pass the instance to B.Foo(). If B imports + # an extender of this proto and then tries to use it, B + # will get a KeyError, even though the extension *is* registered + # at the time of use. + # XXX + self._known_extensions = dict((id(e), e) for e in known_extensions) + # Read lock around self._values, which may be modified by multiple + # concurrent readers in the conceptually "const" __getitem__ method. + # So, we grab this lock in every "read-only" method to ensure + # that concurrent read access is safe without external locking. + self._lock = threading.Lock() + # Maps from extension handle ID to current value of that extension. + self._values = {} + # Maps from extension handle ID to a boolean "has" bit, but only + # for non-repeated extension fields. + keys = (id for id, extension in self._known_extensions.iteritems() + if extension.label != _FieldDescriptor.LABEL_REPEATED) + self._has_bits = dict.fromkeys(keys, False) + + def __getitem__(self, extension_handle): + """Returns the current value of the given extension handle.""" + # We don't care as much about keeping critical sections short in the + # extension support, since it's presumably much less of a common case. + self._lock.acquire() + try: + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + if handle_id not in self._values: + self._AddMissingHandle(extension_handle, handle_id) + return self._values[handle_id] + finally: + self._lock.release() + + def __eq__(self, other): + # We have to grab read locks since we're accessing _values + # in a "const" method. See the comment in the constructor. + if self is other: + return True + self._lock.acquire() + try: + other._lock.acquire() + try: + if self._has_bits != other._has_bits: + return False + # If there's a "has" bit, then only compare values where it is true. + for k, v in self._values.iteritems(): + if self._has_bits.get(k, False) and v != other._values[k]: + return False + return True + finally: + other._lock.release() + finally: + self._lock.release() + + def __ne__(self, other): + return not self == other + + # Note that this is only meaningful for non-repeated, scalar extension + # fields. Note also that we may have to call + # MaybeCallTransitionToNonemptyCallback() when we do successfully set a field + # this way, to set any necssary "has" bits in the ancestors of the extended + # message. + def __setitem__(self, extension_handle, value): + """If extension_handle specifies a non-repeated, scalar extension + field, sets the value of that field. + """ + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + field = extension_handle # Just shorten the name. + if (field.label == _FieldDescriptor.LABEL_OPTIONAL + and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + # It's slightly wasteful to lookup the type checker each time, + # but we expect this to be a vanishingly uncommon case anyway. + type_checker = type_checkers.VALUE_CHECKERS[field.cpp_type] + type_checker.CheckValue(value) + self._values[handle_id] = value + self._has_bits[handle_id] = True + self._extended_message._MarkByteSizeDirty() + self._extended_message._MaybeCallTransitionToNonemptyCallback() + else: + raise TypeError('Extension is repeated and/or a composite type.') + + def _AddMissingHandle(self, extension_handle, handle_id): + """Helper internal to ExtensionDict.""" + # Special handling for non-repeated message extensions, which (like + # normal fields of this kind) are initialized lazily. + # REQUIRES: _lock already held. + cpp_type = extension_handle.cpp_type + label = extension_handle.label + if (cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + and label != _FieldDescriptor.LABEL_REPEATED): + self._AddMissingNonRepeatedCompositeHandle(extension_handle, handle_id) + else: + self._values[handle_id] = _DefaultValueForField( + self._extended_message, extension_handle) + + def _AddMissingNonRepeatedCompositeHandle(self, extension_handle, handle_id): + """Helper internal to ExtensionDict.""" + # REQUIRES: _lock already held. + value = extension_handle.message_type._concrete_class() + value._SetListener(_ExtensionDict._ExtensionListener(self, handle_id)) + self._values[handle_id] = value + + def _SubmessageTransitionedToNonempty(self, handle_id): + """Called when a submessage with a given handle id first transitions to + being nonempty. Called by _ExtensionListener. + """ + assert handle_id in self._has_bits + self._has_bits[handle_id] = True + self._extended_message._MaybeCallTransitionToNonemptyCallback() + + def _SubmessageByteSizeBecameDirty(self): + """Called whenever a submessage's cached byte size becomes invalid + (goes from being "clean" to being "dirty"). Called by _ExtensionListener. + """ + self._extended_message._MarkByteSizeDirty() + + # We may wish to widen the public interface of Message.Extensions + # to expose some of this private functionality in the future. + # For now, we make all this functionality module-private and just + # implement what we need for serialization/deserialization, + # HasField()/ClearField(), etc. + + def _HasExtension(self, extension_handle): + """Method for internal use by this module. + Returns true iff we "have" this extension in the sense of the + "has" bit being set. + """ + handle_id = id(extension_handle) + # Note that this is different from the other checks. + if handle_id not in self._has_bits: + raise KeyError('Extension not known to this class, or is repeated field.') + return self._has_bits[handle_id] + + # Intentionally pretty similar to ClearField() above. + def _ClearExtension(self, extension_handle): + """Method for internal use by this module. + Clears the specified extension, unsetting its "has" bit. + """ + handle_id = id(extension_handle) + if handle_id not in self._known_extensions: + raise KeyError('Extension not known to this class') + default_value = _DefaultValueForField(self._extended_message, + extension_handle) + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + self._extended_message._MarkByteSizeDirty() + else: + cpp_type = extension_handle.cpp_type + if cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if handle_id in self._values: + # Future modifications to this object shouldn't set any + # "has" bits here. + self._values[handle_id]._SetListener(None) + if self._has_bits[handle_id]: + self._has_bits[handle_id] = False + self._extended_message._MarkByteSizeDirty() + if handle_id in self._values: + del self._values[handle_id] + + def _ListSetExtensions(self): + """Method for internal use by this module. + + Returns an sequence of all extensions that are currently "set" + in this extension dict. A "set" extension is a repeated extension, + or a non-repeated extension with its "has" bit set. + + The returned sequence contains (field_descriptor, value) pairs, + where value is the current value of the extension with the given + field descriptor. + + The sequence values are in arbitrary order. + """ + self._lock.acquire() # Read-only methods must lock around self._values. + try: + set_extensions = [] + for handle_id, value in self._values.iteritems(): + handle = self._known_extensions[handle_id] + if (handle.label == _FieldDescriptor.LABEL_REPEATED + or self._has_bits[handle_id]): + set_extensions.append((handle, value)) + return set_extensions + finally: + self._lock.release() + + def _AllExtensionsByNumber(self): + """Method for internal use by this module. + + Returns: A dict mapping field_number to (handle, field_descriptor), + for *all* registered extensions for this dict. + """ + # TODO(robinson): Precompute and store this away. Note that we'll have to + # be careful when we move away from having _known_extensions as a + # deep-copied member of this object. + return dict((f.number, f) for f in self._known_extensions.itervalues()) diff --git a/python/google/protobuf/service.py b/python/google/protobuf/service.py new file mode 100755 index 00000000..5d343957 --- /dev/null +++ b/python/google/protobuf/service.py @@ -0,0 +1,194 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Declares the RPC service interfaces. + +This module declares the abstract interfaces underlying proto2 RPC +services. These are intented to be independent of any particular RPC +implementation, so that proto2 services can be used on top of a variety +of implementations. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class Service(object): + + """Abstract base interface for protocol-buffer-based RPC services. + + Services themselves are abstract classes (implemented either by servers or as + stubs), but they subclass this base interface. The methods of this + interface can be used to call the methods of the service without knowing + its exact type at compile time (analogous to the Message interface). + """ + + def GetDescriptor(self): + """Retrieves this service's descriptor.""" + raise NotImplementedError + + def CallMethod(self, method_descriptor, rpc_controller, + request, done): + """Calls a method of the service specified by method_descriptor. + + Preconditions: + * method_descriptor.service == GetDescriptor + * request is of the exact same classes as returned by + GetRequestClass(method). + * After the call has started, the request must not be modified. + * "rpc_controller" is of the correct type for the RPC implementation being + used by this Service. For stubs, the "correct type" depends on the + RpcChannel which the stub is using. + + Postconditions: + * "done" will be called when the method is complete. This may be + before CallMethod() returns or it may be at some point in the future. + """ + raise NotImplementedError + + def GetRequestClass(self, method_descriptor): + """Returns the class of the request message for the specified method. + + CallMethod() requires that the request is of a particular subclass of + Message. GetRequestClass() gets the default instance of this required + type. + + Example: + method = service.GetDescriptor().FindMethodByName("Foo") + request = stub.GetRequestClass(method)() + request.ParseFromString(input) + service.CallMethod(method, request, callback) + """ + raise NotImplementedError + + def GetResponseClass(self, method_descriptor): + """Returns the class of the response message for the specified method. + + This method isn't really needed, as the RpcChannel's CallMethod constructs + the response protocol message. It's provided anyway in case it is useful + for the caller to know the response type in advance. + """ + raise NotImplementedError + + +class RpcController(object): + + """An RpcController mediates a single method call. + + The primary purpose of the controller is to provide a way to manipulate + settings specific to the RPC implementation and to find out about RPC-level + errors. The methods provided by the RpcController interface are intended + to be a "least common denominator" set of features which we expect all + implementations to support. Specific implementations may provide more + advanced features (e.g. deadline propagation). + """ + + # Client-side methods below + + def Reset(self): + """Resets the RpcController to its initial state. + + After the RpcController has been reset, it may be reused in + a new call. Must not be called while an RPC is in progress. + """ + raise NotImplementedError + + def Failed(self): + """Returns true if the call failed. + + After a call has finished, returns true if the call failed. The possible + reasons for failure depend on the RPC implementation. Failed() must not + be called before a call has finished. If Failed() returns true, the + contents of the response message are undefined. + """ + raise NotImplementedError + + def ErrorText(self): + """If Failed is true, returns a human-readable description of the error.""" + raise NotImplementedError + + def StartCancel(self): + """Initiate cancellation. + + Advises the RPC system that the caller desires that the RPC call be + canceled. The RPC system may cancel it immediately, may wait awhile and + then cancel it, or may not even cancel the call at all. If the call is + canceled, the "done" callback will still be called and the RpcController + will indicate that the call failed at that time. + """ + raise NotImplementedError + + # Server-side methods below + + def SetFailed(self, reason): + """Sets a failure reason. + + Causes Failed() to return true on the client side. "reason" will be + incorporated into the message returned by ErrorText(). If you find + you need to return machine-readable information about failures, you + should incorporate it into your response protocol buffer and should + NOT call SetFailed(). + """ + raise NotImplementedError + + def IsCanceled(self): + """Checks if the client cancelled the RPC. + + If true, indicates that the client canceled the RPC, so the server may + as well give up on replying to it. The server should still call the + final "done" callback. + """ + raise NotImplementedError + + def NotifyOnCancel(self, callback): + """Sets a callback to invoke on cancel. + + Asks that the given callback be called when the RPC is canceled. The + callback will always be called exactly once. If the RPC completes without + being canceled, the callback will be called after completion. If the RPC + has already been canceled when NotifyOnCancel() is called, the callback + will be called immediately. + + NotifyOnCancel() must be called no more than once per request. + """ + raise NotImplementedError + + +class RpcChannel(object): + + """Abstract interface for an RPC channel. + + An RpcChannel represents a communication line to a service which can be used + to call that service's methods. The service may be running on another + machine. Normally, you should not use an RpcChannel directly, but instead + construct a stub {@link Service} wrapping it. Example: + + Example: + RpcChannel channel = rpcImpl.Channel("remotehost.example.com:1234") + RpcController controller = rpcImpl.Controller() + MyService service = MyService_Stub(channel) + service.MyMethod(controller, request, callback) + """ + + def CallMethod(self, method_descriptor, rpc_controller, + request, response_class, done): + """Calls the method identified by the descriptor. + + Call the given method of the remote service. The signature of this + procedure looks the same as Service.CallMethod(), but the requirements + are less strict in one important way: the request object doesn't have to + be of any specific class as long as its descriptor is method.input_type. + """ + raise NotImplementedError diff --git a/python/google/protobuf/service_reflection.py b/python/google/protobuf/service_reflection.py new file mode 100755 index 00000000..6439eaa4 --- /dev/null +++ b/python/google/protobuf/service_reflection.py @@ -0,0 +1,275 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Contains metaclasses used to create protocol service and service stub +classes from ServiceDescriptor objects at runtime. + +The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to +inject all useful functionality into the classes output by the protocol +compiler at compile-time. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class GeneratedServiceType(type): + + """Metaclass for service classes created at runtime from ServiceDescriptors. + + Implementations for all methods described in the Service class are added here + by this class. We also create properties to allow getting/setting all fields + in the protocol message. + + The protocol compiler currently uses this metaclass to create protocol service + classes at runtime. Clients can also manually create their own classes at + runtime, as in this example: + + mydescriptor = ServiceDescriptor(.....) + class MyProtoService(service.Service): + __metaclass__ = GeneratedServiceType + DESCRIPTOR = mydescriptor + myservice_instance = MyProtoService() + ... + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service class. + + Args: + name: Name of the class (ignored, but required by the metaclass + protocol). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service class is subclassed. + if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary: + return + descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY] + service_builder = _ServiceBuilder(descriptor) + service_builder.BuildService(cls) + + +class GeneratedServiceStubType(GeneratedServiceType): + + """Metaclass for service stubs created at runtime from ServiceDescriptors. + + This class has similar responsibilities as GeneratedServiceType, except that + it creates the service stub classes. + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service stub class. + + Args: + name: Name of the class (ignored, here). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary) + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service stub is subclassed. + if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary: + return + descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY] + service_stub_builder = _ServiceStubBuilder(descriptor) + service_stub_builder.BuildServiceStub(cls) + + +class _ServiceBuilder(object): + + """This class constructs a protocol service class using a service descriptor. + + Given a service descriptor, this class constructs a class that represents + the specified service descriptor. One service builder instance constructs + exactly one service class. That means all instances of that class share the + same builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + service class. + """ + self.descriptor = service_descriptor + + def BuildService(self, cls): + """Constructs the service class. + + Args: + cls: The class that will be constructed. + """ + + # CallMethod needs to operate with an instance of the Service class. This + # internal wrapper function exists only to be able to pass the service + # instance to the method that does the real CallMethod work. + def _WrapCallMethod(srvc, method_descriptor, + rpc_controller, request, callback): + self._CallMethod(srvc, method_descriptor, + rpc_controller, request, callback) + self.cls = cls + cls.CallMethod = _WrapCallMethod + cls.GetDescriptor = self._GetDescriptor + cls.GetRequestClass = self._GetRequestClass + cls.GetResponseClass = self._GetResponseClass + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateNonImplementedMethod(method)) + + def _GetDescriptor(self): + """Retrieves the service descriptor. + + Returns: + The descriptor of the service (of type ServiceDescriptor). + """ + return self.descriptor + + def _CallMethod(self, srvc, method_descriptor, + rpc_controller, request, callback): + """Calls the method described by a given method descriptor. + + Args: + srvc: Instance of the service for which this method is called. + method_descriptor: Descriptor that represent the method to call. + rpc_controller: RPC controller to use for this method's execution. + request: Request protocol message. + callback: A callback to invoke after the method has completed. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'CallMethod() given method descriptor for wrong service type.') + method = getattr(srvc, method_descriptor.name) + method(rpc_controller, request, callback) + + def _GetRequestClass(self, method_descriptor): + """Returns the class of the request protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + request protocol message class. + + Returns: + A class that represents the input protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetRequestClass() given method descriptor for wrong service type.') + return method_descriptor.input_type._concrete_class + + def _GetResponseClass(self, method_descriptor): + """Returns the class of the response protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + response protocol message class. + + Returns: + A class that represents the output protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetResponseClass() given method descriptor for wrong service type.') + return method_descriptor.output_type._concrete_class + + def _GenerateNonImplementedMethod(self, method): + """Generates and returns a method that can be set for a service methods. + + Args: + method: Descriptor of the service method for which a method is to be + generated. + + Returns: + A method that can be added to the service class. + """ + return lambda inst, rpc_controller, request, callback: ( + self._NonImplementedMethod(method.name, rpc_controller, callback)) + + def _NonImplementedMethod(self, method_name, rpc_controller, callback): + """The body of all methods in the generated service class. + + Args: + method_name: Name of the method being executed. + rpc_controller: RPC controller used to execute this method. + callback: A callback which will be invoked when the method finishes. + """ + rpc_controller.SetFailed('Method %s not implemented.' % method_name) + callback(None) + + +class _ServiceStubBuilder(object): + + """Constructs a protocol service stub class using a service descriptor. + + Given a service descriptor, this class constructs a suitable stub class. + A stub is just a type-safe wrapper around an RpcChannel which emulates a + local implementation of the service. + + One service stub builder instance constructs exactly one class. It means all + instances of that class share the same service stub builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service stub class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + stub class. + """ + self.descriptor = service_descriptor + + def BuildServiceStub(self, cls): + """Constructs the stub class. + + Args: + cls: The class that will be constructed. + """ + + def _ServiceStubInit(stub, rpc_channel): + stub.rpc_channel = rpc_channel + self.cls = cls + cls.__init__ = _ServiceStubInit + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateStubMethod(method)) + + def _GenerateStubMethod(self, method): + return lambda inst, rpc_controller, request, callback: self._StubMethod( + inst, method, rpc_controller, request, callback) + + def _StubMethod(self, stub, method_descriptor, + rpc_controller, request, callback): + """The body of all service methods in the generated stub class. + + Args: + stub: Stub instance. + method_descriptor: Descriptor of the invoked method. + rpc_controller: Rpc controller to execute the method. + request: Request protocol message. + callback: A callback to execute when the method finishes. + """ + stub.rpc_channel.CallMethod( + method_descriptor, rpc_controller, request, + method_descriptor.output_type._concrete_class, callback) diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py new file mode 100755 index 00000000..428b4c0a --- /dev/null +++ b/python/google/protobuf/text_format.py @@ -0,0 +1,111 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. +# http://code.google.com/p/protobuf/ +# +# 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. + +"""Contains routines for printing protocol messages in text format.""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import cStringIO + +from google.protobuf import descriptor + +__all__ = [ 'MessageToString', 'PrintMessage', 'PrintField', 'PrintFieldValue' ] + +def MessageToString(message): + out = cStringIO.StringIO() + PrintMessage(message, out) + result = out.getvalue() + out.close() + return result + +def PrintMessage(message, out, indent = 0): + for field, value in message.ListFields(): + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + for element in value: + PrintField(field, element, out, indent) + else: + PrintField(field, value, out, indent) + +def PrintField(field, value, out, indent = 0): + """Print a single field name/value pair. For repeated fields, the value + should be a single element.""" + + out.write(' ' * indent); + if field.is_extension: + out.write('[') + if (field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type == field.extension_scope and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # The colon is optional in this case, but our cross-language golden files + # don't include it. + out.write(': ') + + PrintFieldValue(field, value, out, indent) + out.write('\n') + +def PrintFieldValue(field, value, out, indent = 0): + """Print a single field value (not including name). For repeated fields, + the value should be a single element.""" + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + out.write(' {\n') + PrintMessage(value, out, indent + 2) + out.write(' ' * indent + '}') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + out.write(field.enum_type.values_by_number[value].name) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('\"') + out.write(_CEscape(value)) + out.write('\"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write("true") + else: + out.write("false") + else: + out.write(str(value)) + +# text.encode('string_escape') does not seem to satisfy our needs as it +# encodes unprintable characters using two-digit hex escapes whereas our +# C++ unescaping function allows hex escapes to be any length. So, +# "\0011".encode('string_escape') ends up being "\\x011", which will be +# decoded in C++ as a single-character string with char code 0x11. +def _CEscape(text): + def escape(c): + o = ord(c) + if o == 10: return r"\n" # optional escape + if o == 13: return r"\r" # optional escape + if o == 9: return r"\t" # optional escape + if o == 39: return r"\'" # optional escape + + if o == 34: return r'\"' # necessary escape + if o == 92: return r"\\" # necessary escape + + if o >= 127 or o < 32: return "\\%03o" % o # necessary escapes + return c + return "".join([escape(c) for c in text]) diff --git a/python/mox.py b/python/mox.py new file mode 100755 index 00000000..ce80ba50 --- /dev/null +++ b/python/mox.py @@ -0,0 +1,1401 @@ +#!/usr/bin/python2.4 +# +# Copyright 2008 Google Inc. +# +# 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. + +# This file is used for testing. The original is at: +# http://code.google.com/p/pymox/ + +"""Mox, an object-mocking framework for Python. + +Mox works in the record-replay-verify paradigm. When you first create +a mock object, it is in record mode. You then programmatically set +the expected behavior of the mock object (what methods are to be +called on it, with what parameters, what they should return, and in +what order). + +Once you have set up the expected mock behavior, you put it in replay +mode. Now the mock responds to method calls just as you told it to. +If an unexpected method (or an expected method with unexpected +parameters) is called, then an exception will be raised. + +Once you are done interacting with the mock, you need to verify that +all the expected interactions occured. (Maybe your code exited +prematurely without calling some cleanup method!) The verify phase +ensures that every expected method was called; otherwise, an exception +will be raised. + +Suggested usage / workflow: + + # Create Mox factory + my_mox = Mox() + + # Create a mock data access object + mock_dao = my_mox.CreateMock(DAOClass) + + # Set up expected behavior + mock_dao.RetrievePersonWithIdentifier('1').AndReturn(person) + mock_dao.DeletePerson(person) + + # Put mocks in replay mode + my_mox.ReplayAll() + + # Inject mock object and run test + controller.SetDao(mock_dao) + controller.DeletePersonById('1') + + # Verify all methods were called as expected + my_mox.VerifyAll() +""" + +from collections import deque +import re +import types +import unittest + +import stubout + +class Error(AssertionError): + """Base exception for this module.""" + + pass + + +class ExpectedMethodCallsError(Error): + """Raised when Verify() is called before all expected methods have been called + """ + + def __init__(self, expected_methods): + """Init exception. + + Args: + # expected_methods: A sequence of MockMethod objects that should have been + # called. + expected_methods: [MockMethod] + + Raises: + ValueError: if expected_methods contains no methods. + """ + + if not expected_methods: + raise ValueError("There must be at least one expected method") + Error.__init__(self) + self._expected_methods = expected_methods + + def __str__(self): + calls = "\n".join(["%3d. %s" % (i, m) + for i, m in enumerate(self._expected_methods)]) + return "Verify: Expected methods never called:\n%s" % (calls,) + + +class UnexpectedMethodCallError(Error): + """Raised when an unexpected method is called. + + This can occur if a method is called with incorrect parameters, or out of the + specified order. + """ + + def __init__(self, unexpected_method, expected): + """Init exception. + + Args: + # unexpected_method: MockMethod that was called but was not at the head of + # the expected_method queue. + # expected: MockMethod or UnorderedGroup the method should have + # been in. + unexpected_method: MockMethod + expected: MockMethod or UnorderedGroup + """ + + Error.__init__(self) + self._unexpected_method = unexpected_method + self._expected = expected + + def __str__(self): + return "Unexpected method call: %s. Expecting: %s" % \ + (self._unexpected_method, self._expected) + + +class UnknownMethodCallError(Error): + """Raised if an unknown method is requested of the mock object.""" + + def __init__(self, unknown_method_name): + """Init exception. + + Args: + # unknown_method_name: Method call that is not part of the mocked class's + # public interface. + unknown_method_name: str + """ + + Error.__init__(self) + self._unknown_method_name = unknown_method_name + + def __str__(self): + return "Method called is not a member of the object: %s" % \ + self._unknown_method_name + + +class Mox(object): + """Mox: a factory for creating mock objects.""" + + # A list of types that should be stubbed out with MockObjects (as + # opposed to MockAnythings). + _USE_MOCK_OBJECT = [types.ClassType, types.InstanceType, types.ModuleType, + types.ObjectType, types.TypeType] + + def __init__(self): + """Initialize a new Mox.""" + + self._mock_objects = [] + self.stubs = stubout.StubOutForTesting() + + def CreateMock(self, class_to_mock): + """Create a new mock object. + + Args: + # class_to_mock: the class to be mocked + class_to_mock: class + + Returns: + MockObject that can be used as the class_to_mock would be. + """ + + new_mock = MockObject(class_to_mock) + self._mock_objects.append(new_mock) + return new_mock + + def CreateMockAnything(self): + """Create a mock that will accept any method calls. + + This does not enforce an interface. + """ + + new_mock = MockAnything() + self._mock_objects.append(new_mock) + return new_mock + + def ReplayAll(self): + """Set all mock objects to replay mode.""" + + for mock_obj in self._mock_objects: + mock_obj._Replay() + + + def VerifyAll(self): + """Call verify on all mock objects created.""" + + for mock_obj in self._mock_objects: + mock_obj._Verify() + + def ResetAll(self): + """Call reset on all mock objects. This does not unset stubs.""" + + for mock_obj in self._mock_objects: + mock_obj._Reset() + + def StubOutWithMock(self, obj, attr_name, use_mock_anything=False): + """Replace a method, attribute, etc. with a Mock. + + This will replace a class or module with a MockObject, and everything else + (method, function, etc) with a MockAnything. This can be overridden to + always use a MockAnything by setting use_mock_anything to True. + + Args: + obj: A Python object (class, module, instance, callable). + attr_name: str. The name of the attribute to replace with a mock. + use_mock_anything: bool. True if a MockAnything should be used regardless + of the type of attribute. + """ + + attr_to_replace = getattr(obj, attr_name) + if type(attr_to_replace) in self._USE_MOCK_OBJECT and not use_mock_anything: + stub = self.CreateMock(attr_to_replace) + else: + stub = self.CreateMockAnything() + + self.stubs.Set(obj, attr_name, stub) + + def UnsetStubs(self): + """Restore stubs to their original state.""" + + self.stubs.UnsetAll() + +def Replay(*args): + """Put mocks into Replay mode. + + Args: + # args is any number of mocks to put into replay mode. + """ + + for mock in args: + mock._Replay() + + +def Verify(*args): + """Verify mocks. + + Args: + # args is any number of mocks to be verified. + """ + + for mock in args: + mock._Verify() + + +def Reset(*args): + """Reset mocks. + + Args: + # args is any number of mocks to be reset. + """ + + for mock in args: + mock._Reset() + + +class MockAnything: + """A mock that can be used to mock anything. + + This is helpful for mocking classes that do not provide a public interface. + """ + + def __init__(self): + """ """ + self._Reset() + + def __getattr__(self, method_name): + """Intercept method calls on this object. + + A new MockMethod is returned that is aware of the MockAnything's + state (record or replay). The call will be recorded or replayed + by the MockMethod's __call__. + + Args: + # method name: the name of the method being called. + method_name: str + + Returns: + A new MockMethod aware of MockAnything's state (record or replay). + """ + + return self._CreateMockMethod(method_name) + + def _CreateMockMethod(self, method_name): + """Create a new mock method call and return it. + + Args: + # method name: the name of the method being called. + method_name: str + + Returns: + A new MockMethod aware of MockAnything's state (record or replay). + """ + + return MockMethod(method_name, self._expected_calls_queue, + self._replay_mode) + + def __nonzero__(self): + """Return 1 for nonzero so the mock can be used as a conditional.""" + + return 1 + + def __eq__(self, rhs): + """Provide custom logic to compare objects.""" + + return (isinstance(rhs, MockAnything) and + self._replay_mode == rhs._replay_mode and + self._expected_calls_queue == rhs._expected_calls_queue) + + def __ne__(self, rhs): + """Provide custom logic to compare objects.""" + + return not self == rhs + + def _Replay(self): + """Start replaying expected method calls.""" + + self._replay_mode = True + + def _Verify(self): + """Verify that all of the expected calls have been made. + + Raises: + ExpectedMethodCallsError: if there are still more method calls in the + expected queue. + """ + + # If the list of expected calls is not empty, raise an exception + if self._expected_calls_queue: + # The last MultipleTimesGroup is not popped from the queue. + if (len(self._expected_calls_queue) == 1 and + isinstance(self._expected_calls_queue[0], MultipleTimesGroup) and + self._expected_calls_queue[0].IsSatisfied()): + pass + else: + raise ExpectedMethodCallsError(self._expected_calls_queue) + + def _Reset(self): + """Reset the state of this mock to record mode with an empty queue.""" + + # Maintain a list of method calls we are expecting + self._expected_calls_queue = deque() + + # Make sure we are in setup mode, not replay mode + self._replay_mode = False + + +class MockObject(MockAnything, object): + """A mock object that simulates the public/protected interface of a class.""" + + def __init__(self, class_to_mock): + """Initialize a mock object. + + This determines the methods and properties of the class and stores them. + + Args: + # class_to_mock: class to be mocked + class_to_mock: class + """ + + # This is used to hack around the mixin/inheritance of MockAnything, which + # is not a proper object (it can be anything. :-) + MockAnything.__dict__['__init__'](self) + + # Get a list of all the public and special methods we should mock. + self._known_methods = set() + self._known_vars = set() + self._class_to_mock = class_to_mock + for method in dir(class_to_mock): + if callable(getattr(class_to_mock, method)): + self._known_methods.add(method) + else: + self._known_vars.add(method) + + def __getattr__(self, name): + """Intercept attribute request on this object. + + If the attribute is a public class variable, it will be returned and not + recorded as a call. + + If the attribute is not a variable, it is handled like a method + call. The method name is checked against the set of mockable + methods, and a new MockMethod is returned that is aware of the + MockObject's state (record or replay). The call will be recorded + or replayed by the MockMethod's __call__. + + Args: + # name: the name of the attribute being requested. + name: str + + Returns: + Either a class variable or a new MockMethod that is aware of the state + of the mock (record or replay). + + Raises: + UnknownMethodCallError if the MockObject does not mock the requested + method. + """ + + if name in self._known_vars: + return getattr(self._class_to_mock, name) + + if name in self._known_methods: + return self._CreateMockMethod(name) + + raise UnknownMethodCallError(name) + + def __eq__(self, rhs): + """Provide custom logic to compare objects.""" + + return (isinstance(rhs, MockObject) and + self._class_to_mock == rhs._class_to_mock and + self._replay_mode == rhs._replay_mode and + self._expected_calls_queue == rhs._expected_calls_queue) + + def __setitem__(self, key, value): + """Provide custom logic for mocking classes that support item assignment. + + Args: + key: Key to set the value for. + value: Value to set. + + Returns: + Expected return value in replay mode. A MockMethod object for the + __setitem__ method that has already been called if not in replay mode. + + Raises: + TypeError if the underlying class does not support item assignment. + UnexpectedMethodCallError if the object does not expect the call to + __setitem__. + + """ + setitem = self._class_to_mock.__dict__.get('__setitem__', None) + + # Verify the class supports item assignment. + if setitem is None: + raise TypeError('object does not support item assignment') + + # If we are in replay mode then simply call the mock __setitem__ method. + if self._replay_mode: + return MockMethod('__setitem__', self._expected_calls_queue, + self._replay_mode)(key, value) + + + # Otherwise, create a mock method __setitem__. + return self._CreateMockMethod('__setitem__')(key, value) + + def __getitem__(self, key): + """Provide custom logic for mocking classes that are subscriptable. + + Args: + key: Key to return the value for. + + Returns: + Expected return value in replay mode. A MockMethod object for the + __getitem__ method that has already been called if not in replay mode. + + Raises: + TypeError if the underlying class is not subscriptable. + UnexpectedMethodCallError if the object does not expect the call to + __setitem__. + + """ + getitem = self._class_to_mock.__dict__.get('__getitem__', None) + + # Verify the class supports item assignment. + if getitem is None: + raise TypeError('unsubscriptable object') + + # If we are in replay mode then simply call the mock __getitem__ method. + if self._replay_mode: + return MockMethod('__getitem__', self._expected_calls_queue, + self._replay_mode)(key) + + + # Otherwise, create a mock method __getitem__. + return self._CreateMockMethod('__getitem__')(key) + + def __call__(self, *params, **named_params): + """Provide custom logic for mocking classes that are callable.""" + + # Verify the class we are mocking is callable + callable = self._class_to_mock.__dict__.get('__call__', None) + if callable is None: + raise TypeError('Not callable') + + # Because the call is happening directly on this object instead of a method, + # the call on the mock method is made right here + mock_method = self._CreateMockMethod('__call__') + return mock_method(*params, **named_params) + + @property + def __class__(self): + """Return the class that is being mocked.""" + + return self._class_to_mock + + +class MockMethod(object): + """Callable mock method. + + A MockMethod should act exactly like the method it mocks, accepting parameters + and returning a value, or throwing an exception (as specified). When this + method is called, it can optionally verify whether the called method (name and + signature) matches the expected method. + """ + + def __init__(self, method_name, call_queue, replay_mode): + """Construct a new mock method. + + Args: + # method_name: the name of the method + # call_queue: deque of calls, verify this call against the head, or add + # this call to the queue. + # replay_mode: False if we are recording, True if we are verifying calls + # against the call queue. + method_name: str + call_queue: list or deque + replay_mode: bool + """ + + self._name = method_name + self._call_queue = call_queue + if not isinstance(call_queue, deque): + self._call_queue = deque(self._call_queue) + self._replay_mode = replay_mode + + self._params = None + self._named_params = None + self._return_value = None + self._exception = None + self._side_effects = None + + def __call__(self, *params, **named_params): + """Log parameters and return the specified return value. + + If the Mock(Anything/Object) associated with this call is in record mode, + this MockMethod will be pushed onto the expected call queue. If the mock + is in replay mode, this will pop a MockMethod off the top of the queue and + verify this call is equal to the expected call. + + Raises: + UnexpectedMethodCall if this call is supposed to match an expected method + call and it does not. + """ + + self._params = params + self._named_params = named_params + + if not self._replay_mode: + self._call_queue.append(self) + return self + + expected_method = self._VerifyMethodCall() + + if expected_method._side_effects: + expected_method._side_effects(*params, **named_params) + + if expected_method._exception: + raise expected_method._exception + + return expected_method._return_value + + def __getattr__(self, name): + """Raise an AttributeError with a helpful message.""" + + raise AttributeError('MockMethod has no attribute "%s". ' + 'Did you remember to put your mocks in replay mode?' % name) + + def _PopNextMethod(self): + """Pop the next method from our call queue.""" + try: + return self._call_queue.popleft() + except IndexError: + raise UnexpectedMethodCallError(self, None) + + def _VerifyMethodCall(self): + """Verify the called method is expected. + + This can be an ordered method, or part of an unordered set. + + Returns: + The expected mock method. + + Raises: + UnexpectedMethodCall if the method called was not expected. + """ + + expected = self._PopNextMethod() + + # Loop here, because we might have a MethodGroup followed by another + # group. + while isinstance(expected, MethodGroup): + expected, method = expected.MethodCalled(self) + if method is not None: + return method + + # This is a mock method, so just check equality. + if expected != self: + raise UnexpectedMethodCallError(self, expected) + + return expected + + def __str__(self): + params = ', '.join( + [repr(p) for p in self._params or []] + + ['%s=%r' % x for x in sorted((self._named_params or {}).items())]) + desc = "%s(%s) -> %r" % (self._name, params, self._return_value) + return desc + + def __eq__(self, rhs): + """Test whether this MockMethod is equivalent to another MockMethod. + + Args: + # rhs: the right hand side of the test + rhs: MockMethod + """ + + return (isinstance(rhs, MockMethod) and + self._name == rhs._name and + self._params == rhs._params and + self._named_params == rhs._named_params) + + def __ne__(self, rhs): + """Test whether this MockMethod is not equivalent to another MockMethod. + + Args: + # rhs: the right hand side of the test + rhs: MockMethod + """ + + return not self == rhs + + def GetPossibleGroup(self): + """Returns a possible group from the end of the call queue or None if no + other methods are on the stack. + """ + + # Remove this method from the tail of the queue so we can add it to a group. + this_method = self._call_queue.pop() + assert this_method == self + + # Determine if the tail of the queue is a group, or just a regular ordered + # mock method. + group = None + try: + group = self._call_queue[-1] + except IndexError: + pass + + return group + + def _CheckAndCreateNewGroup(self, group_name, group_class): + """Checks if the last method (a possible group) is an instance of our + group_class. Adds the current method to this group or creates a new one. + + Args: + + group_name: the name of the group. + group_class: the class used to create instance of this new group + """ + group = self.GetPossibleGroup() + + # If this is a group, and it is the correct group, add the method. + if isinstance(group, group_class) and group.group_name() == group_name: + group.AddMethod(self) + return self + + # Create a new group and add the method. + new_group = group_class(group_name) + new_group.AddMethod(self) + self._call_queue.append(new_group) + return self + + def InAnyOrder(self, group_name="default"): + """Move this method into a group of unordered calls. + + A group of unordered calls must be defined together, and must be executed + in full before the next expected method can be called. There can be + multiple groups that are expected serially, if they are given + different group names. The same group name can be reused if there is a + standard method call, or a group with a different name, spliced between + usages. + + Args: + group_name: the name of the unordered group. + + Returns: + self + """ + return self._CheckAndCreateNewGroup(group_name, UnorderedGroup) + + def MultipleTimes(self, group_name="default"): + """Move this method into group of calls which may be called multiple times. + + A group of repeating calls must be defined together, and must be executed in + full before the next expected mehtod can be called. + + Args: + group_name: the name of the unordered group. + + Returns: + self + """ + return self._CheckAndCreateNewGroup(group_name, MultipleTimesGroup) + + def AndReturn(self, return_value): + """Set the value to return when this method is called. + + Args: + # return_value can be anything. + """ + + self._return_value = return_value + return return_value + + def AndRaise(self, exception): + """Set the exception to raise when this method is called. + + Args: + # exception: the exception to raise when this method is called. + exception: Exception + """ + + self._exception = exception + + def WithSideEffects(self, side_effects): + """Set the side effects that are simulated when this method is called. + + Args: + side_effects: A callable which modifies the parameters or other relevant + state which a given test case depends on. + + Returns: + Self for chaining with AndReturn and AndRaise. + """ + self._side_effects = side_effects + return self + +class Comparator: + """Base class for all Mox comparators. + + A Comparator can be used as a parameter to a mocked method when the exact + value is not known. For example, the code you are testing might build up a + long SQL string that is passed to your mock DAO. You're only interested that + the IN clause contains the proper primary keys, so you can set your mock + up as follows: + + mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result) + + Now whatever query is passed in must contain the string 'IN (1, 2, 4, 5)'. + + A Comparator may replace one or more parameters, for example: + # return at most 10 rows + mock_dao.RunQuery(StrContains('SELECT'), 10) + + or + + # Return some non-deterministic number of rows + mock_dao.RunQuery(StrContains('SELECT'), IsA(int)) + """ + + def equals(self, rhs): + """Special equals method that all comparators must implement. + + Args: + rhs: any python object + """ + + raise NotImplementedError, 'method must be implemented by a subclass.' + + def __eq__(self, rhs): + return self.equals(rhs) + + def __ne__(self, rhs): + return not self.equals(rhs) + + +class IsA(Comparator): + """This class wraps a basic Python type or class. It is used to verify + that a parameter is of the given type or class. + + Example: + mock_dao.Connect(IsA(DbConnectInfo)) + """ + + def __init__(self, class_name): + """Initialize IsA + + Args: + class_name: basic python type or a class + """ + + self._class_name = class_name + + def equals(self, rhs): + """Check to see if the RHS is an instance of class_name. + + Args: + # rhs: the right hand side of the test + rhs: object + + Returns: + bool + """ + + try: + return isinstance(rhs, self._class_name) + except TypeError: + # Check raw types if there was a type error. This is helpful for + # things like cStringIO.StringIO. + return type(rhs) == type(self._class_name) + + def __repr__(self): + return str(self._class_name) + +class IsAlmost(Comparator): + """Comparison class used to check whether a parameter is nearly equal + to a given value. Generally useful for floating point numbers. + + Example mock_dao.SetTimeout((IsAlmost(3.9))) + """ + + def __init__(self, float_value, places=7): + """Initialize IsAlmost. + + Args: + float_value: The value for making the comparison. + places: The number of decimal places to round to. + """ + + self._float_value = float_value + self._places = places + + def equals(self, rhs): + """Check to see if RHS is almost equal to float_value + + Args: + rhs: the value to compare to float_value + + Returns: + bool + """ + + try: + return round(rhs-self._float_value, self._places) == 0 + except TypeError: + # This is probably because either float_value or rhs is not a number. + return False + + def __repr__(self): + return str(self._float_value) + +class StrContains(Comparator): + """Comparison class used to check whether a substring exists in a + string parameter. This can be useful in mocking a database with SQL + passed in as a string parameter, for example. + + Example: + mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result) + """ + + def __init__(self, search_string): + """Initialize. + + Args: + # search_string: the string you are searching for + search_string: str + """ + + self._search_string = search_string + + def equals(self, rhs): + """Check to see if the search_string is contained in the rhs string. + + Args: + # rhs: the right hand side of the test + rhs: object + + Returns: + bool + """ + + try: + return rhs.find(self._search_string) > -1 + except Exception: + return False + + def __repr__(self): + return '' % self._search_string + + +class Regex(Comparator): + """Checks if a string matches a regular expression. + + This uses a given regular expression to determine equality. + """ + + def __init__(self, pattern, flags=0): + """Initialize. + + Args: + # pattern is the regular expression to search for + pattern: str + # flags passed to re.compile function as the second argument + flags: int + """ + + self.regex = re.compile(pattern, flags=flags) + + def equals(self, rhs): + """Check to see if rhs matches regular expression pattern. + + Returns: + bool + """ + + return self.regex.search(rhs) is not None + + def __repr__(self): + s = '' % self._key + + +class ContainsKeyValue(Comparator): + """Checks whether a key/value pair is in a dict parameter. + + Example: + mock_dao.UpdateUsers(ContainsKeyValue('stevepm', stevepm_user_info)) + """ + + def __init__(self, key, value): + """Initialize. + + Args: + # key: a key in a dict + # value: the corresponding value + """ + + self._key = key + self._value = value + + def equals(self, rhs): + """Check whether the given key/value pair is in the rhs dict. + + Returns: + bool + """ + + try: + return rhs[self._key] == self._value + except Exception: + return False + + def __repr__(self): + return '' % (self._key, self._value) + + +class SameElementsAs(Comparator): + """Checks whether iterables contain the same elements (ignoring order). + + Example: + mock_dao.ProcessUsers(SameElementsAs('stevepm', 'salomaki')) + """ + + def __init__(self, expected_seq): + """Initialize. + + Args: + expected_seq: a sequence + """ + + self._expected_seq = expected_seq + + def equals(self, actual_seq): + """Check to see whether actual_seq has same elements as expected_seq. + + Args: + actual_seq: sequence + + Returns: + bool + """ + + try: + expected = dict([(element, None) for element in self._expected_seq]) + actual = dict([(element, None) for element in actual_seq]) + except TypeError: + # Fall back to slower list-compare if any of the objects are unhashable. + expected = list(self._expected_seq) + actual = list(actual_seq) + expected.sort() + actual.sort() + return expected == actual + + def __repr__(self): + return '' % self._expected_seq + + +class And(Comparator): + """Evaluates one or more Comparators on RHS and returns an AND of the results. + """ + + def __init__(self, *args): + """Initialize. + + Args: + *args: One or more Comparator + """ + + self._comparators = args + + def equals(self, rhs): + """Checks whether all Comparators are equal to rhs. + + Args: + # rhs: can be anything + + Returns: + bool + """ + + for comparator in self._comparators: + if not comparator.equals(rhs): + return False + + return True + + def __repr__(self): + return '' % str(self._comparators) + + +class Or(Comparator): + """Evaluates one or more Comparators on RHS and returns an OR of the results. + """ + + def __init__(self, *args): + """Initialize. + + Args: + *args: One or more Mox comparators + """ + + self._comparators = args + + def equals(self, rhs): + """Checks whether any Comparator is equal to rhs. + + Args: + # rhs: can be anything + + Returns: + bool + """ + + for comparator in self._comparators: + if comparator.equals(rhs): + return True + + return False + + def __repr__(self): + return '' % str(self._comparators) + + +class Func(Comparator): + """Call a function that should verify the parameter passed in is correct. + + You may need the ability to perform more advanced operations on the parameter + in order to validate it. You can use this to have a callable validate any + parameter. The callable should return either True or False. + + + Example: + + def myParamValidator(param): + # Advanced logic here + return True + + mock_dao.DoSomething(Func(myParamValidator), true) + """ + + def __init__(self, func): + """Initialize. + + Args: + func: callable that takes one parameter and returns a bool + """ + + self._func = func + + def equals(self, rhs): + """Test whether rhs passes the function test. + + rhs is passed into func. + + Args: + rhs: any python object + + Returns: + the result of func(rhs) + """ + + return self._func(rhs) + + def __repr__(self): + return str(self._func) + + +class IgnoreArg(Comparator): + """Ignore an argument. + + This can be used when we don't care about an argument of a method call. + + Example: + # Check if CastMagic is called with 3 as first arg and 'disappear' as third. + mymock.CastMagic(3, IgnoreArg(), 'disappear') + """ + + def equals(self, unused_rhs): + """Ignores arguments and returns True. + + Args: + unused_rhs: any python object + + Returns: + always returns True + """ + + return True + + def __repr__(self): + return '' + + +class MethodGroup(object): + """Base class containing common behaviour for MethodGroups.""" + + def __init__(self, group_name): + self._group_name = group_name + + def group_name(self): + return self._group_name + + def __str__(self): + return '<%s "%s">' % (self.__class__.__name__, self._group_name) + + def AddMethod(self, mock_method): + raise NotImplementedError + + def MethodCalled(self, mock_method): + raise NotImplementedError + + def IsSatisfied(self): + raise NotImplementedError + +class UnorderedGroup(MethodGroup): + """UnorderedGroup holds a set of method calls that may occur in any order. + + This construct is helpful for non-deterministic events, such as iterating + over the keys of a dict. + """ + + def __init__(self, group_name): + super(UnorderedGroup, self).__init__(group_name) + self._methods = [] + + def AddMethod(self, mock_method): + """Add a method to this group. + + Args: + mock_method: A mock method to be added to this group. + """ + + self._methods.append(mock_method) + + def MethodCalled(self, mock_method): + """Remove a method call from the group. + + If the method is not in the set, an UnexpectedMethodCallError will be + raised. + + Args: + mock_method: a mock method that should be equal to a method in the group. + + Returns: + The mock method from the group + + Raises: + UnexpectedMethodCallError if the mock_method was not in the group. + """ + + # Check to see if this method exists, and if so, remove it from the set + # and return it. + for method in self._methods: + if method == mock_method: + # Remove the called mock_method instead of the method in the group. + # The called method will match any comparators when equality is checked + # during removal. The method in the group could pass a comparator to + # another comparator during the equality check. + self._methods.remove(mock_method) + + # If this group is not empty, put it back at the head of the queue. + if not self.IsSatisfied(): + mock_method._call_queue.appendleft(self) + + return self, method + + raise UnexpectedMethodCallError(mock_method, self) + + def IsSatisfied(self): + """Return True if there are not any methods in this group.""" + + return len(self._methods) == 0 + + +class MultipleTimesGroup(MethodGroup): + """MultipleTimesGroup holds methods that may be called any number of times. + + Note: Each method must be called at least once. + + This is helpful, if you don't know or care how many times a method is called. + """ + + def __init__(self, group_name): + super(MultipleTimesGroup, self).__init__(group_name) + self._methods = set() + self._methods_called = set() + + def AddMethod(self, mock_method): + """Add a method to this group. + + Args: + mock_method: A mock method to be added to this group. + """ + + self._methods.add(mock_method) + + def MethodCalled(self, mock_method): + """Remove a method call from the group. + + If the method is not in the set, an UnexpectedMethodCallError will be + raised. + + Args: + mock_method: a mock method that should be equal to a method in the group. + + Returns: + The mock method from the group + + Raises: + UnexpectedMethodCallError if the mock_method was not in the group. + """ + + # Check to see if this method exists, and if so add it to the set of + # called methods. + + for method in self._methods: + if method == mock_method: + self._methods_called.add(mock_method) + # Always put this group back on top of the queue, because we don't know + # when we are done. + mock_method._call_queue.appendleft(self) + return self, method + + if self.IsSatisfied(): + next_method = mock_method._PopNextMethod(); + return next_method, None + else: + raise UnexpectedMethodCallError(mock_method, self) + + def IsSatisfied(self): + """Return True if all methods in this group are called at least once.""" + # NOTE(psycho): We can't use the simple set difference here because we want + # to match different parameters which are considered the same e.g. IsA(str) + # and some string. This solution is O(n^2) but n should be small. + tmp = self._methods.copy() + for called in self._methods_called: + for expected in tmp: + if called == expected: + tmp.remove(expected) + if not tmp: + return True + break + return False + + +class MoxMetaTestBase(type): + """Metaclass to add mox cleanup and verification to every test. + + As the mox unit testing class is being constructed (MoxTestBase or a + subclass), this metaclass will modify all test functions to call the + CleanUpMox method of the test class after they finish. This means that + unstubbing and verifying will happen for every test with no additional code, + and any failures will result in test failures as opposed to errors. + """ + + def __init__(cls, name, bases, d): + type.__init__(cls, name, bases, d) + + # also get all the attributes from the base classes to account + # for a case when test class is not the immediate child of MoxTestBase + for base in bases: + for attr_name in dir(base): + d[attr_name] = getattr(base, attr_name) + + for func_name, func in d.items(): + if func_name.startswith('test') and callable(func): + setattr(cls, func_name, MoxMetaTestBase.CleanUpTest(cls, func)) + + @staticmethod + def CleanUpTest(cls, func): + """Adds Mox cleanup code to any MoxTestBase method. + + Always unsets stubs after a test. Will verify all mocks for tests that + otherwise pass. + + Args: + cls: MoxTestBase or subclass; the class whose test method we are altering. + func: method; the method of the MoxTestBase test class we wish to alter. + + Returns: + The modified method. + """ + def new_method(self, *args, **kwargs): + mox_obj = getattr(self, 'mox', None) + cleanup_mox = False + if mox_obj and isinstance(mox_obj, Mox): + cleanup_mox = True + try: + func(self, *args, **kwargs) + finally: + if cleanup_mox: + mox_obj.UnsetStubs() + if cleanup_mox: + mox_obj.VerifyAll() + new_method.__name__ = func.__name__ + new_method.__doc__ = func.__doc__ + new_method.__module__ = func.__module__ + return new_method + + +class MoxTestBase(unittest.TestCase): + """Convenience test class to make stubbing easier. + + Sets up a "mox" attribute which is an instance of Mox - any mox tests will + want this. Also automatically unsets any stubs and verifies that all mock + methods have been called at the end of each test, eliminating boilerplate + code. + """ + + __metaclass__ = MoxMetaTestBase + + def setUp(self): + self.mox = Mox() diff --git a/python/setup.py b/python/setup.py new file mode 100755 index 00000000..eda25749 --- /dev/null +++ b/python/setup.py @@ -0,0 +1,126 @@ +#! /usr/bin/python +# +# See README for usage instructions. + +# We must use setuptools, not distutils, because we need to use the +# namespace_packages option for the "google" package. +from ez_setup import use_setuptools +use_setuptools() + +from setuptools import setup +from distutils.spawn import find_executable +import sys +import os + +maintainer_email = "protobuf@googlegroups.com" + +# Find the Protocol Compiler. +if os.path.exists("../src/protoc"): + protoc = "../src/protoc" +else: + protoc = find_executable("protoc") + +def generate_proto(source): + """Invokes the Protocol Compiler to generate a _pb2.py from the given + .proto file. Does nothing if the output already exists and is newer than + the input.""" + + output = source.replace(".proto", "_pb2.py").replace("../src/", "") + + if not os.path.exists(source): + print "Can't find required file: " + source + sys.exit(-1) + + if (not os.path.exists(output) or + (os.path.exists(source) and + os.path.getmtime(source) > os.path.getmtime(output))): + print "Generating %s..." % output + + if protoc == None: + sys.stderr.write( + "protoc is not installed nor found in ../src. Please compile it " + "or install the binary package.\n") + sys.exit(-1) + + protoc_command = protoc + " -I../src -I. --python_out=. " + source + if os.system(protoc_command) != 0: + sys.exit(-1) + +def MakeTestSuite(): + generate_proto("../src/google/protobuf/unittest.proto") + generate_proto("../src/google/protobuf/unittest_import.proto") + generate_proto("../src/google/protobuf/unittest_mset.proto") + generate_proto("google/protobuf/internal/more_extensions.proto") + generate_proto("google/protobuf/internal/more_messages.proto") + + import unittest + import google.protobuf.internal.generator_test as generator_test + import google.protobuf.internal.decoder_test as decoder_test + import google.protobuf.internal.descriptor_test as descriptor_test + import google.protobuf.internal.encoder_test as encoder_test + import google.protobuf.internal.input_stream_test as input_stream_test + import google.protobuf.internal.output_stream_test as output_stream_test + import google.protobuf.internal.reflection_test as reflection_test + import google.protobuf.internal.service_reflection_test \ + as service_reflection_test + import google.protobuf.internal.text_format_test as text_format_test + import google.protobuf.internal.wire_format_test as wire_format_test + + loader = unittest.defaultTestLoader + suite = unittest.TestSuite() + for test in [ generator_test, + decoder_test, + descriptor_test, + encoder_test, + input_stream_test, + output_stream_test, + reflection_test, + service_reflection_test, + text_format_test, + wire_format_test ]: + suite.addTest(loader.loadTestsFromModule(test)) + + return suite + +if __name__ == '__main__': + # TODO(kenton): Integrate this into setuptools somehow? + if len(sys.argv) >= 2 and sys.argv[1] == "clean": + # Delete generated _pb2.py files and .pyc files in the code tree. + for (dirpath, dirnames, filenames) in os.walk("."): + for filename in filenames: + filepath = os.path.join(dirpath, filename) + if filepath.endswith("_pb2.py") or filepath.endswith(".pyc"): + os.remove(filepath) + else: + # Generate necessary .proto file if it doesn't exist. + # TODO(kenton): Maybe we should hook this into a distutils command? + generate_proto("../src/google/protobuf/descriptor.proto") + + setup(name = 'protobuf', + version = '2.0.1-SNAPSHOT', + packages = [ 'google' ], + namespace_packages = [ 'google' ], + test_suite = 'setup.MakeTestSuite', + # Must list modules explicitly so that we don't install tests. + py_modules = [ + 'google.protobuf.internal.decoder', + 'google.protobuf.internal.encoder', + 'google.protobuf.internal.input_stream', + 'google.protobuf.internal.message_listener', + 'google.protobuf.internal.output_stream', + 'google.protobuf.internal.wire_format', + 'google.protobuf.descriptor', + 'google.protobuf.descriptor_pb2', + 'google.protobuf.message', + 'google.protobuf.reflection', + 'google.protobuf.service', + 'google.protobuf.service_reflection', + 'google.protobuf.text_format'], + url = 'http://code.google.com/p/protobuf/', + maintainer = maintainer_email, + maintainer_email = 'protobuf@googlegroups.com', + license = 'Apache License, Version 2.0', + description = 'Protocol Buffers', + long_description = + "Protocol Buffers are Google's data interchange format.", + ) diff --git a/python/stubout.py b/python/stubout.py new file mode 100755 index 00000000..aee4f2da --- /dev/null +++ b/python/stubout.py @@ -0,0 +1,140 @@ +#!/usr/bin/python2.4 +# +# Copyright 2008 Google Inc. +# +# 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. + +# This file is used for testing. The original is at: +# http://code.google.com/p/pymox/ + +class StubOutForTesting: + """Sample Usage: + You want os.path.exists() to always return true during testing. + + stubs = StubOutForTesting() + stubs.Set(os.path, 'exists', lambda x: 1) + ... + stubs.UnsetAll() + + The above changes os.path.exists into a lambda that returns 1. Once + the ... part of the code finishes, the UnsetAll() looks up the old value + of os.path.exists and restores it. + + """ + def __init__(self): + self.cache = [] + self.stubs = [] + + def __del__(self): + self.SmartUnsetAll() + self.UnsetAll() + + def SmartSet(self, obj, attr_name, new_attr): + """Replace obj.attr_name with new_attr. This method is smart and works + at the module, class, and instance level while preserving proper + inheritance. It will not stub out C types however unless that has been + explicitly allowed by the type. + + This method supports the case where attr_name is a staticmethod or a + classmethod of obj. + + Notes: + - If obj is an instance, then it is its class that will actually be + stubbed. Note that the method Set() does not do that: if obj is + an instance, it (and not its class) will be stubbed. + - The stubbing is using the builtin getattr and setattr. So, the __get__ + and __set__ will be called when stubbing (TODO: A better idea would + probably be to manipulate obj.__dict__ instead of getattr() and + setattr()). + + Raises AttributeError if the attribute cannot be found. + """ + if (inspect.ismodule(obj) or + (not inspect.isclass(obj) and obj.__dict__.has_key(attr_name))): + orig_obj = obj + orig_attr = getattr(obj, attr_name) + + else: + if not inspect.isclass(obj): + mro = list(inspect.getmro(obj.__class__)) + else: + mro = list(inspect.getmro(obj)) + + mro.reverse() + + orig_attr = None + + for cls in mro: + try: + orig_obj = cls + orig_attr = getattr(obj, attr_name) + except AttributeError: + continue + + if orig_attr is None: + raise AttributeError("Attribute not found.") + + # Calling getattr() on a staticmethod transforms it to a 'normal' function. + # We need to ensure that we put it back as a staticmethod. + old_attribute = obj.__dict__.get(attr_name) + if old_attribute is not None and isinstance(old_attribute, staticmethod): + orig_attr = staticmethod(orig_attr) + + self.stubs.append((orig_obj, attr_name, orig_attr)) + setattr(orig_obj, attr_name, new_attr) + + def SmartUnsetAll(self): + """Reverses all the SmartSet() calls, restoring things to their original + definition. Its okay to call SmartUnsetAll() repeatedly, as later calls + have no effect if no SmartSet() calls have been made. + + """ + self.stubs.reverse() + + for args in self.stubs: + setattr(*args) + + self.stubs = [] + + def Set(self, parent, child_name, new_child): + """Replace child_name's old definition with new_child, in the context + of the given parent. The parent could be a module when the child is a + function at module scope. Or the parent could be a class when a class' + method is being replaced. The named child is set to new_child, while + the prior definition is saved away for later, when UnsetAll() is called. + + This method supports the case where child_name is a staticmethod or a + classmethod of parent. + """ + old_child = getattr(parent, child_name) + + old_attribute = parent.__dict__.get(child_name) + if old_attribute is not None and isinstance(old_attribute, staticmethod): + old_child = staticmethod(old_child) + + self.cache.append((parent, old_child, child_name)) + setattr(parent, child_name, new_child) + + def UnsetAll(self): + """Reverses all the Set() calls, restoring things to their original + definition. Its okay to call UnsetAll() repeatedly, as later calls have + no effect if no Set() calls have been made. + + """ + # Undo calls to Set() in reverse order, in case Set() was called on the + # same arguments repeatedly (want the original call to be last one undone) + self.cache.reverse() + + for (parent, old_child, child_name) in self.cache: + setattr(parent, child_name, old_child) + self.cache = [] diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 00000000..2997cdd5 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,255 @@ +## Process this file with automake to produce Makefile.in + +if GCC +# These are good warnings to turn on by default +AM_CXXFLAGS = $(PTHREAD_CFLAGS) -Wall -Wwrite-strings -Woverloaded-virtual -Wno-sign-compare +else +AM_CXXFLAGS = $(PTHREAD_CFLAGS) +endif + +AM_LDFLAGS = $(PTHREAD_CFLAGS) + +# If I say "dist_include_DATA", automake complains that $(includedir) is not +# a "legitimate" directory for DATA. Screw you, automake. +protodir = $(includedir) +nobase_dist_proto_DATA = google/protobuf/descriptor.proto + +# Not sure why these don't get cleaned automatically. +clean-local: + rm -f *.loT + +CLEANFILES = $(protoc_outputs) unittest_proto_middleman + +MAINTAINERCLEANFILES = \ + Makefile.in + +nobase_include_HEADERS = \ + google/protobuf/stubs/common.h \ + google/protobuf/descriptor.h \ + google/protobuf/descriptor.pb.h \ + google/protobuf/descriptor_database.h \ + google/protobuf/dynamic_message.h \ + google/protobuf/extension_set.h \ + google/protobuf/generated_message_reflection.h \ + google/protobuf/message.h \ + google/protobuf/reflection_ops.h \ + google/protobuf/repeated_field.h \ + google/protobuf/service.h \ + google/protobuf/text_format.h \ + google/protobuf/unknown_field_set.h \ + google/protobuf/wire_format.h \ + google/protobuf/wire_format_inl.h \ + google/protobuf/io/coded_stream.h \ + google/protobuf/io/printer.h \ + google/protobuf/io/tokenizer.h \ + google/protobuf/io/zero_copy_stream.h \ + google/protobuf/io/zero_copy_stream_impl.h \ + google/protobuf/compiler/code_generator.h \ + google/protobuf/compiler/command_line_interface.h \ + google/protobuf/compiler/importer.h \ + google/protobuf/compiler/parser.h \ + google/protobuf/compiler/cpp/cpp_generator.h \ + google/protobuf/compiler/java/java_generator.h \ + google/protobuf/compiler/python/python_generator.h + +lib_LTLIBRARIES = libprotobuf.la libprotoc.la + +libprotobuf_la_LIBADD = $(PTHREAD_LIBS) +libprotobuf_la_LDFLAGS = -version-info 0:0:0 +libprotobuf_la_SOURCES = \ + google/protobuf/stubs/common.cc \ + google/protobuf/stubs/hash.cc \ + google/protobuf/stubs/hash.h \ + google/protobuf/stubs/map-util.cc \ + google/protobuf/stubs/map-util.h \ + google/protobuf/stubs/stl_util-inl.cc \ + google/protobuf/stubs/stl_util-inl.h \ + google/protobuf/stubs/substitute.cc \ + google/protobuf/stubs/substitute.h \ + google/protobuf/stubs/strutil.cc \ + google/protobuf/stubs/strutil.h \ + google/protobuf/descriptor.cc \ + google/protobuf/descriptor.pb.cc \ + google/protobuf/descriptor_database.cc \ + google/protobuf/dynamic_message.cc \ + google/protobuf/extension_set.cc \ + google/protobuf/generated_message_reflection.cc \ + google/protobuf/message.cc \ + google/protobuf/reflection_ops.cc \ + google/protobuf/repeated_field.cc \ + google/protobuf/service.cc \ + google/protobuf/text_format.cc \ + google/protobuf/unknown_field_set.cc \ + google/protobuf/wire_format.cc \ + google/protobuf/io/coded_stream.cc \ + google/protobuf/io/printer.cc \ + google/protobuf/io/tokenizer.cc \ + google/protobuf/io/zero_copy_stream.cc \ + google/protobuf/io/zero_copy_stream_impl.cc \ + google/protobuf/compiler/importer.cc \ + google/protobuf/compiler/parser.cc + +libprotoc_la_LIBADD = $(PTHREAD_LIBS) libprotobuf.la +libprotoc_la_LDFLAGS = -version-info 0:0:0 +libprotoc_la_SOURCES = \ + google/protobuf/compiler/code_generator.cc \ + google/protobuf/compiler/command_line_interface.cc \ + google/protobuf/compiler/cpp/cpp_enum.cc \ + google/protobuf/compiler/cpp/cpp_enum.h \ + google/protobuf/compiler/cpp/cpp_enum_field.cc \ + google/protobuf/compiler/cpp/cpp_enum_field.h \ + google/protobuf/compiler/cpp/cpp_extension.cc \ + google/protobuf/compiler/cpp/cpp_extension.h \ + google/protobuf/compiler/cpp/cpp_field.cc \ + google/protobuf/compiler/cpp/cpp_field.h \ + google/protobuf/compiler/cpp/cpp_file.cc \ + google/protobuf/compiler/cpp/cpp_file.h \ + google/protobuf/compiler/cpp/cpp_generator.cc \ + google/protobuf/compiler/cpp/cpp_helpers.cc \ + google/protobuf/compiler/cpp/cpp_helpers.h \ + google/protobuf/compiler/cpp/cpp_message.cc \ + google/protobuf/compiler/cpp/cpp_message.h \ + google/protobuf/compiler/cpp/cpp_message_field.cc \ + google/protobuf/compiler/cpp/cpp_message_field.h \ + google/protobuf/compiler/cpp/cpp_primitive_field.cc \ + google/protobuf/compiler/cpp/cpp_primitive_field.h \ + google/protobuf/compiler/cpp/cpp_service.cc \ + google/protobuf/compiler/cpp/cpp_service.h \ + google/protobuf/compiler/cpp/cpp_string_field.cc \ + google/protobuf/compiler/cpp/cpp_string_field.h \ + google/protobuf/compiler/java/java_enum.cc \ + google/protobuf/compiler/java/java_enum.h \ + google/protobuf/compiler/java/java_enum_field.cc \ + google/protobuf/compiler/java/java_enum_field.h \ + google/protobuf/compiler/java/java_extension.cc \ + google/protobuf/compiler/java/java_extension.h \ + google/protobuf/compiler/java/java_field.cc \ + google/protobuf/compiler/java/java_field.h \ + google/protobuf/compiler/java/java_file.cc \ + google/protobuf/compiler/java/java_file.h \ + google/protobuf/compiler/java/java_generator.cc \ + google/protobuf/compiler/java/java_helpers.cc \ + google/protobuf/compiler/java/java_helpers.h \ + google/protobuf/compiler/java/java_message.cc \ + google/protobuf/compiler/java/java_message.h \ + google/protobuf/compiler/java/java_message_field.cc \ + google/protobuf/compiler/java/java_message_field.h \ + google/protobuf/compiler/java/java_primitive_field.cc \ + google/protobuf/compiler/java/java_primitive_field.h \ + google/protobuf/compiler/java/java_service.cc \ + google/protobuf/compiler/java/java_service.h \ + google/protobuf/compiler/python/python_generator.cc + +bin_PROGRAMS = protoc +protoc_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la +protoc_SOURCES = google/protobuf/compiler/main.cc + +# Tests ============================================================== + +protoc_inputs = \ + google/protobuf/unittest.proto \ + google/protobuf/unittest_import.proto \ + google/protobuf/unittest_mset.proto \ + google/protobuf/unittest_optimize_for.proto \ + google/protobuf/unittest_embed_optimize_for.proto \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto + +EXTRA_DIST = \ + $(protoc_inputs) \ + solaris/libstdc++.la \ + google/protobuf/testdata/golden_message \ + google/protobuf/testdata/text_format_unittest_data.txt \ + google/protobuf/testdata/text_format_unittest_extensions_data.txt \ + google/protobuf/package_info.h \ + google/protobuf/io/package_info.h \ + google/protobuf/compiler/package_info.h \ + gtest/CHANGES \ + gtest/CONTRIBUTORS \ + gtest/COPYING \ + gtest/README \ + gtest/gen_gtest_pred_impl.py + +protoc_outputs = \ + google/protobuf/unittest.pb.cc \ + google/protobuf/unittest.pb.h \ + google/protobuf/unittest_import.pb.cc \ + google/protobuf/unittest_import.pb.h \ + google/protobuf/unittest_mset.pb.cc \ + google/protobuf/unittest_mset.pb.h \ + google/protobuf/unittest_optimize_for.pb.cc \ + google/protobuf/unittest_optimize_for.pb.h \ + google/protobuf/unittest_embed_optimize_for.pb.cc \ + google/protobuf/unittest_embed_optimize_for.pb.h \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.cc \ + google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h + +BUILT_SOURCES = $(protoc_outputs) + +# This rule is a little weird. The first prereq is the protoc executable +# and the rest are its inputs. Therefore, $^ -- which expands to the +# list of prereqs -- is actually a valid command. We have to place "./" in +# front of it in case protoc is in the current directory. protoc allows +# flags to appear after input file names, so we happily stick the flags on +# the end. +# +# For reference, if we didn't have to worry about VPATH (i.e., building from +# a directory other than the package root), we could have just written this: +# ./protoc$(EXEEXT) -I$(srcdir) --cpp_out=. $(protoc_inputs) +unittest_proto_middleman: protoc$(EXEEXT) $(protoc_inputs) + ./$^ -I$(srcdir) --cpp_out=. + touch unittest_proto_middleman + +$(protoc_outputs): unittest_proto_middleman + +noinst_PROGRAMS = protobuf-test +protobuf_test_LDADD = $(PTHREAD_LIBS) libprotobuf.la libprotoc.la +protobuf_test_SOURCES = \ + google/protobuf/stubs/common_unittest.cc \ + google/protobuf/stubs/strutil_unittest.cc \ + google/protobuf/descriptor_database_unittest.cc \ + google/protobuf/descriptor_unittest.cc \ + google/protobuf/dynamic_message_unittest.cc \ + google/protobuf/extension_set_unittest.cc \ + google/protobuf/generated_message_reflection_unittest.cc \ + google/protobuf/message_unittest.cc \ + google/protobuf/reflection_ops_unittest.cc \ + google/protobuf/repeated_field_unittest.cc \ + google/protobuf/text_format_unittest.cc \ + google/protobuf/unknown_field_set_unittest.cc \ + google/protobuf/wire_format_unittest.cc \ + google/protobuf/io/coded_stream_unittest.cc \ + google/protobuf/io/printer_unittest.cc \ + google/protobuf/io/tokenizer_unittest.cc \ + google/protobuf/io/zero_copy_stream_unittest.cc \ + google/protobuf/compiler/command_line_interface_unittest.cc \ + google/protobuf/compiler/importer_unittest.cc \ + google/protobuf/compiler/parser_unittest.cc \ + google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc \ + google/protobuf/compiler/cpp/cpp_unittest.cc \ + google/protobuf/test_util.cc \ + google/protobuf/test_util.h \ + google/protobuf/testing/googletest.cc \ + google/protobuf/testing/googletest.h \ + google/protobuf/testing/file.cc \ + google/protobuf/testing/file.h \ + gtest/gtest.cc \ + gtest/gtest.h \ + gtest/gtest-death-test.cc \ + gtest/gtest-death-test.h \ + gtest/gtest-filepath.cc \ + gtest/gtest-internal-inl.h \ + gtest/gtest-message.h \ + gtest/gtest-port.cc \ + gtest/gtest-spi.h \ + gtest/gtest_main.cc \ + gtest/gtest_pred_impl.h \ + gtest/gtest_prod.h \ + gtest/internal/gtest-death-test-internal.h \ + gtest/internal/gtest-filepath.h \ + gtest/internal/gtest-internal.h \ + gtest/internal/gtest-port.h \ + gtest/internal/gtest-string.h + +nodist_protobuf_test_SOURCES = $(protoc_outputs) + +TESTS = protobuf-test diff --git a/src/google/protobuf/compiler/code_generator.cc b/src/google/protobuf/compiler/code_generator.cc new file mode 100644 index 00000000..d3a051d0 --- /dev/null +++ b/src/google/protobuf/compiler/code_generator.cc @@ -0,0 +1,32 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { +namespace compiler { + +CodeGenerator::~CodeGenerator() {} +OutputDirectory::~OutputDirectory() {} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/code_generator.h b/src/google/protobuf/compiler/code_generator.h new file mode 100644 index 00000000..8f9938e3 --- /dev/null +++ b/src/google/protobuf/compiler/code_generator.h @@ -0,0 +1,98 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Defines the abstract interface implemented by each of the language-specific +// code generators. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { + +namespace io { class ZeroCopyOutputStream; } +class FileDescriptor; + +namespace compiler { + +// Defined in this file. +class CodeGenerator; +class OutputDirectory; + +// The abstract interface to a class which generates code implementing a +// particular proto file in a particular language. A number of these may +// be registered with CommandLineInterface to support various languages. +class LIBPROTOC_EXPORT CodeGenerator { + public: + inline CodeGenerator() {} + virtual ~CodeGenerator(); + + // Generates code for the given proto file, generating one or more files in + // the given output directory. + // + // A parameter to be passed to the generator can be specified on the + // command line. This is intended to be used by Java and similar languages + // to specify which specific class from the proto file is to be generated, + // though it could have other uses as well. It is empty if no parameter was + // given. + // + // Returns true if successful. Otherwise, sets *error to a description of + // the problem (e.g. "invalid parameter") and returns false. + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodeGenerator); +}; + +// CodeGenerators generate one or more files in a given directory. This +// abstract interface represents the directory to which the CodeGenerator is +// to write. +class LIBPROTOC_EXPORT OutputDirectory { + public: + inline OutputDirectory() {} + virtual ~OutputDirectory(); + + // Opens the given file, truncating it if it exists, and returns a + // ZeroCopyOutputStream that writes to the file. The caller takes ownership + // of the returned object. This method never fails (a dummy stream will be + // returned instead). + // + // The filename given should be relative to the root of the source tree. + // E.g. the C++ generator, when generating code for "foo/bar.proto", will + // generate the files "foo/bar.pb2.h" and "foo/bar.pb2.cc"; note that + // "foo/" is included in these filenames. The filename is not allowed to + // contain "." or ".." components. + virtual io::ZeroCopyOutputStream* Open(const string& filename) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OutputDirectory); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CODE_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc new file mode 100644 index 00000000..d58fc3b8 --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -0,0 +1,863 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace compiler { + +#if defined(_WIN32) +#define mkdir(name, mode) mkdir(name) +#ifndef W_OK +#define W_OK 02 // not defined by MSVC for whatever reason +#endif +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +namespace { +#if defined(_WIN32) && !defined(__CYGWIN__) +static const char* kPathSeparator = ";"; +#else +static const char* kPathSeparator = ":"; +#endif + +// Returns true if the text looks like a Windows-style absolute path, starting +// with a drive letter. Example: "C:\foo". +static bool IsWindowsAbsolutePath(const string& text) { +#if defined(_WIN32) || defined(__CYGWIN__) + return text.size() >= 3 && text[1] == ':' && + isalpha(text[0]) && + (text[2] == '/' || text[2] == '\\') && + text.find_last_of(':') == 1; +#else + return false; +#endif +} + +void SetFdToTextMode(int fd) { +#ifdef _WIN32 + if (_setmode(fd, _O_TEXT) == -1) { + // This should never happen, I think. + GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_TEXT): " << strerror(errno); + } +#endif + // (Text and binary are the same on non-Windows platforms.) +} + +void SetFdToBinaryMode(int fd) { +#ifdef _WIN32 + if (_setmode(fd, _O_BINARY) == -1) { + // This should never happen, I think. + GOOGLE_LOG(WARNING) << "_setmode(" << fd << ", _O_BINARY): " << strerror(errno); + } +#endif + // (Text and binary are the same on non-Windows platforms.) +} + +} // namespace + +// A MultiFileErrorCollector that prints errors to stderr. +class CommandLineInterface::ErrorPrinter : public MultiFileErrorCollector, + public io::ErrorCollector { + public: + ErrorPrinter() {} + ~ErrorPrinter() {} + + // implements MultiFileErrorCollector ------------------------------ + void AddError(const string& filename, int line, int column, + const string& message) { + // Users typically expect 1-based line/column numbers, so we add 1 + // to each here. + cerr << filename; + if (line != -1) { + cerr << ":" << (line + 1) << ":" << (column + 1); + } + cerr << ": " << message << endl; + } + + // implements io::ErrorCollector ----------------------------------- + void AddError(int line, int column, const string& message) { + AddError("input", line, column, message); + } +}; + +// ------------------------------------------------------------------- + +// An OutputDirectory implementation that writes to disk. +class CommandLineInterface::DiskOutputDirectory : public OutputDirectory { + public: + DiskOutputDirectory(const string& root); + ~DiskOutputDirectory(); + + bool VerifyExistence(); + + inline bool had_error() { return had_error_; } + inline void set_had_error(bool value) { had_error_ = value; } + + // implements OutputDirectory -------------------------------------- + io::ZeroCopyOutputStream* Open(const string& filename); + + private: + string root_; + bool had_error_; +}; + +// A FileOutputStream that checks for errors in the destructor and reports +// them. We extend FileOutputStream via wrapping rather than inheritance +// for two reasons: +// 1) Implementation inheritance is evil. +// 2) We need to close the file descriptor *after* the FileOutputStream's +// destructor is run to make sure it flushes the file contents. +class CommandLineInterface::ErrorReportingFileOutput + : public io::ZeroCopyOutputStream { + public: + ErrorReportingFileOutput(int file_descriptor, + const string& filename, + DiskOutputDirectory* directory); + ~ErrorReportingFileOutput(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size) { return file_stream_->Next(data, size); } + void BackUp(int count) { file_stream_->BackUp(count); } + int64 ByteCount() const { return file_stream_->ByteCount(); } + + private: + scoped_ptr file_stream_; + int file_descriptor_; + string filename_; + DiskOutputDirectory* directory_; +}; + +// ------------------------------------------------------------------- + +CommandLineInterface::DiskOutputDirectory::DiskOutputDirectory( + const string& root) + : root_(root), had_error_(false) { + // Add a '/' to the end if it doesn't already have one. But don't add a + // '/' to an empty string since this probably means the current directory. + if (!root_.empty() && root[root_.size() - 1] != '/') { + root_ += '/'; + } +} + +CommandLineInterface::DiskOutputDirectory::~DiskOutputDirectory() { +} + +bool CommandLineInterface::DiskOutputDirectory::VerifyExistence() { + if (!root_.empty()) { + // Make sure the directory exists. If it isn't a directory, this will fail + // because we added a '/' to the end of the name in the constructor. + if (access(root_.c_str(), W_OK) == -1) { + cerr << root_ << ": " << strerror(errno) << endl; + return false; + } + } + + return true; +} + +io::ZeroCopyOutputStream* CommandLineInterface::DiskOutputDirectory::Open( + const string& filename) { + // Recursively create parent directories to the output file. + vector parts; + SplitStringUsing(filename, "/", &parts); + string path_so_far = root_; + for (int i = 0; i < parts.size() - 1; i++) { + path_so_far += parts[i]; + if (mkdir(path_so_far.c_str(), 0777) != 0) { + if (errno != EEXIST) { + cerr << filename << ": while trying to create directory " + << path_so_far << ": " << strerror(errno) << endl; + had_error_ = true; + // Return a dummy stream. + return new io::ArrayOutputStream(NULL, 0); + } + } + path_so_far += '/'; + } + + // Create the output file. + int file_descriptor; + do { + file_descriptor = + open((root_ + filename).c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + } while (file_descriptor < 0 && errno == EINTR); + + if (file_descriptor < 0) { + // Failed to open. + cerr << filename << ": " << strerror(errno) << endl; + had_error_ = true; + // Return a dummy stream. + return new io::ArrayOutputStream(NULL, 0); + } + + return new ErrorReportingFileOutput(file_descriptor, filename, this); +} + +CommandLineInterface::ErrorReportingFileOutput::ErrorReportingFileOutput( + int file_descriptor, + const string& filename, + DiskOutputDirectory* directory) + : file_stream_(new io::FileOutputStream(file_descriptor)), + file_descriptor_(file_descriptor), + filename_(filename), + directory_(directory) {} + +CommandLineInterface::ErrorReportingFileOutput::~ErrorReportingFileOutput() { + // Check if we had any errors while writing. + if (file_stream_->GetErrno() != 0) { + cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl; + directory_->set_had_error(true); + } + + // Close the file stream. + if (!file_stream_->Close()) { + cerr << filename_ << ": " << strerror(file_stream_->GetErrno()) << endl; + directory_->set_had_error(true); + } +} + +// =================================================================== + +CommandLineInterface::CommandLineInterface() + : mode_(MODE_COMPILE), + imports_in_descriptor_set_(false), + disallow_services_(false), + inputs_are_proto_path_relative_(false) {} +CommandLineInterface::~CommandLineInterface() {} + +void CommandLineInterface::RegisterGenerator(const string& flag_name, + CodeGenerator* generator, + const string& help_text) { + GeneratorInfo info; + info.generator = generator; + info.help_text = help_text; + generators_[flag_name] = info; +} + +int CommandLineInterface::Run(int argc, const char* const argv[]) { + Clear(); + if (!ParseArguments(argc, argv)) return 1; + + // Set up the source tree. + DiskSourceTree source_tree; + for (int i = 0; i < proto_path_.size(); i++) { + source_tree.MapPath(proto_path_[i].first, proto_path_[i].second); + } + + // Map input files to virtual paths if necessary. + if (!inputs_are_proto_path_relative_) { + if (!MakeInputsBeProtoPathRelative(&source_tree)) { + return 1; + } + } + + // Allocate the Importer. + ErrorPrinter error_collector; + Importer importer(&source_tree, &error_collector); + + vector parsed_files; + + // Parse each file and generate output. + for (int i = 0; i < input_files_.size(); i++) { + // Import the file. + const FileDescriptor* parsed_file = importer.Import(input_files_[i]); + if (parsed_file == NULL) return 1; + parsed_files.push_back(parsed_file); + + // Enforce --disallow_services. + if (disallow_services_ && parsed_file->service_count() > 0) { + cerr << parsed_file->name() << ": This file contains services, but " + "--disallow_services was used." << endl; + return 1; + } + + if (mode_ == MODE_COMPILE) { + // Generate output files. + for (int i = 0; i < output_directives_.size(); i++) { + if (!GenerateOutput(parsed_file, output_directives_[i])) { + return 1; + } + } + } + } + + if (!descriptor_set_name_.empty()) { + if (!WriteDescriptorSet(parsed_files)) { + return 1; + } + } + + if (mode_ == MODE_ENCODE || mode_ == MODE_DECODE) { + if (codec_type_.empty()) { + // HACK: Define an EmptyMessage type to use for decoding. + DescriptorPool pool; + FileDescriptorProto file; + file.set_name("empty_message.proto"); + file.add_message_type()->set_name("EmptyMessage"); + GOOGLE_CHECK(pool.BuildFile(file) != NULL); + codec_type_ = "EmptyMessage"; + if (!EncodeOrDecode(&pool)) { + return 1; + } + } else { + if (!EncodeOrDecode(importer.pool())) { + return 1; + } + } + } + + return 0; +} + +void CommandLineInterface::Clear() { + // Clear all members that are set by Run(). Note that we must not clear + // members which are set by other methods before Run() is called. + executable_name_.clear(); + proto_path_.clear(); + input_files_.clear(); + output_directives_.clear(); + codec_type_.clear(); + descriptor_set_name_.clear(); + + mode_ = MODE_COMPILE; + imports_in_descriptor_set_ = false; + disallow_services_ = false; +} + +bool CommandLineInterface::MakeInputsBeProtoPathRelative( + DiskSourceTree* source_tree) { + for (int i = 0; i < input_files_.size(); i++) { + string virtual_file, shadowing_disk_file; + switch (source_tree->DiskFileToVirtualFile( + input_files_[i], &virtual_file, &shadowing_disk_file)) { + case DiskSourceTree::SUCCESS: + input_files_[i] = virtual_file; + break; + case DiskSourceTree::SHADOWED: + cerr << input_files_[i] << ": Input is shadowed in the --proto_path " + "by \"" << shadowing_disk_file << "\". Either use the latter " + "file as your input or reorder the --proto_path so that the " + "former file's location comes first." << endl; + return false; + case DiskSourceTree::CANNOT_OPEN: + cerr << input_files_[i] << ": " << strerror(errno) << endl; + return false; + case DiskSourceTree::NO_MAPPING: + // First check if the file exists at all. + if (access(input_files_[i].c_str(), F_OK) < 0) { + // File does not even exist. + cerr << input_files_[i] << ": " << strerror(ENOENT) << endl; + } else { + cerr << input_files_[i] << ": File does not reside within any path " + "specified using --proto_path (or -I). You must specify a " + "--proto_path which encompasses this file." << endl; + } + return false; + } + } + + return true; +} + +bool CommandLineInterface::ParseArguments(int argc, const char* const argv[]) { + executable_name_ = argv[0]; + + // Iterate through all arguments and parse them. + for (int i = 1; i < argc; i++) { + string name, value; + + if (ParseArgument(argv[i], &name, &value)) { + // Returned true => Use the next argument as the flag value. + if (i + 1 == argc || argv[i+1][0] == '-') { + cerr << "Missing value for flag: " << name << endl; + if (name == "--decode") { + cerr << "To decode an unknown message, use --decode_raw." << endl; + } + return false; + } else { + ++i; + value = argv[i]; + } + } + + if (!InterpretArgument(name, value)) return false; + } + + // If no --proto_path was given, use the current working directory. + if (proto_path_.empty()) { + proto_path_.push_back(make_pair("", ".")); + } + + // Check some errror cases. + bool decoding_raw = (mode_ == MODE_DECODE) && codec_type_.empty(); + if (decoding_raw && !input_files_.empty()) { + cerr << "When using --decode_raw, no input files should be given." << endl; + return false; + } else if (!decoding_raw && input_files_.empty()) { + cerr << "Missing input file." << endl; + return false; + } + if (mode_ == MODE_COMPILE && output_directives_.empty() && + descriptor_set_name_.empty()) { + cerr << "Missing output directives." << endl; + return false; + } + if (imports_in_descriptor_set_ && descriptor_set_name_.empty()) { + cerr << "--include_imports only makes sense when combined with " + "--descriptor_set_name." << endl; + } + + return true; +} + +bool CommandLineInterface::ParseArgument(const char* arg, + string* name, string* value) { + bool parsed_value = false; + + if (arg[0] != '-') { + // Not a flag. + name->clear(); + parsed_value = true; + *value = arg; + } else if (arg[1] == '-') { + // Two dashes: Multi-character name, with '=' separating name and + // value. + const char* equals_pos = strchr(arg, '='); + if (equals_pos != NULL) { + *name = string(arg, equals_pos - arg); + *value = equals_pos + 1; + parsed_value = true; + } else { + *name = arg; + } + } else { + // One dash: One-character name, all subsequent characters are the + // value. + if (arg[1] == '\0') { + // arg is just "-". We treat this as an input file, except that at + // present this will just lead to a "file not found" error. + name->clear(); + *value = arg; + parsed_value = true; + } else { + *name = string(arg, 2); + *value = arg + 2; + parsed_value = !value->empty(); + } + } + + // Need to return true iff the next arg should be used as the value for this + // one, false otherwise. + + if (parsed_value) { + // We already parsed a value for this flag. + return false; + } + + if (*name == "-h" || *name == "--help" || + *name == "--disallow_services" || + *name == "--include_imports" || + *name == "--version" || + *name == "--decode_raw") { + // HACK: These are the only flags that don't take a value. + // They probably should not be hard-coded like this but for now it's + // not worth doing better. + return false; + } + + // Next argument is the flag value. + return true; +} + +bool CommandLineInterface::InterpretArgument(const string& name, + const string& value) { + if (name.empty()) { + // Not a flag. Just a filename. + if (value.empty()) { + cerr << "You seem to have passed an empty string as one of the " + "arguments to " << executable_name_ << ". This is actually " + "sort of hard to do. Congrats. Unfortunately it is not valid " + "input so the program is going to die now." << endl; + return false; + } + + input_files_.push_back(value); + + } else if (name == "-I" || name == "--proto_path") { + // Java's -classpath (and some other languages) delimits path components + // with colons. Let's accept that syntax too just to make things more + // intuitive. + vector parts; + SplitStringUsing(value, kPathSeparator, &parts); + + for (int i = 0; i < parts.size(); i++) { + string virtual_path; + string disk_path; + + int equals_pos = parts[i].find_first_of('='); + if (equals_pos == string::npos) { + virtual_path = ""; + disk_path = parts[i]; + } else { + virtual_path = parts[i].substr(0, equals_pos); + disk_path = parts[i].substr(equals_pos + 1); + } + + if (disk_path.empty()) { + cerr << "--proto_path passed empty directory name. (Use \".\" for " + "current directory.)" << endl; + return false; + } + + // Make sure disk path exists, warn otherwise. + if (access(disk_path.c_str(), F_OK) < 0) { + cerr << disk_path << ": warning: directory does not exist." << endl; + } + + proto_path_.push_back(make_pair(virtual_path, disk_path)); + } + + } else if (name == "-o" || name == "--descriptor_set_out") { + if (!descriptor_set_name_.empty()) { + cerr << name << " may only be passed once." << endl; + return false; + } + if (value.empty()) { + cerr << name << " requires a non-empty value." << endl; + return false; + } + if (mode_ != MODE_COMPILE) { + cerr << "Cannot use --encode or --decode and generate descriptors at the " + "same time." << endl; + return false; + } + descriptor_set_name_ = value; + + } else if (name == "--include_imports") { + if (imports_in_descriptor_set_) { + cerr << name << " may only be passed once." << endl; + return false; + } + imports_in_descriptor_set_ = true; + + } else if (name == "-h" || name == "--help") { + PrintHelpText(); + return false; // Exit without running compiler. + + } else if (name == "--version") { + if (!version_info_.empty()) { + cout << version_info_ << endl; + } + cout << "libprotoc " + << protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION) + << endl; + return false; // Exit without running compiler. + + } else if (name == "--disallow_services") { + disallow_services_ = true; + + } else if (name == "--encode" || name == "--decode" || + name == "--decode_raw") { + if (mode_ != MODE_COMPILE) { + cerr << "Only one of --encode and --decode can be specified." << endl; + return false; + } + if (!output_directives_.empty() || !descriptor_set_name_.empty()) { + cerr << "Cannot use " << name + << " and generate code or descriptors at the same time." << endl; + return false; + } + + mode_ = (name == "--encode") ? MODE_ENCODE : MODE_DECODE; + + if (value.empty() && name != "--decode_raw") { + cerr << "Type name for " << name << " cannot be blank." << endl; + if (name == "--decode") { + cerr << "To decode an unknown message, use --decode_raw." << endl; + } + return false; + } else if (!value.empty() && name == "--decode_raw") { + cerr << "--decode_raw does not take a parameter." << endl; + return false; + } + + codec_type_ = value; + + } else { + // Some other flag. Look it up in the generators list. + GeneratorMap::const_iterator iter = generators_.find(name); + if (iter == generators_.end()) { + cerr << "Unknown flag: " << name << endl; + return false; + } + + // It's an output flag. Add it to the output directives. + if (mode_ != MODE_COMPILE) { + cerr << "Cannot use --encode or --decode and generate code at the " + "same time." << endl; + return false; + } + + OutputDirective directive; + directive.name = name; + directive.generator = iter->second.generator; + + // Split value at ':' to separate the generator parameter from the + // filename. However, avoid doing this if the colon is part of a valid + // Windows-style absolute path. + string::size_type colon_pos = value.find_first_of(':'); + if (colon_pos == string::npos || IsWindowsAbsolutePath(value)) { + directive.output_location = value; + } else { + directive.parameter = value.substr(0, colon_pos); + directive.output_location = value.substr(colon_pos + 1); + } + + output_directives_.push_back(directive); + } + + return true; +} + +void CommandLineInterface::PrintHelpText() { + // Sorry for indentation here; line wrapping would be uglier. + cerr << +"Usage: " << executable_name_ << " [OPTION] PROTO_FILES\n" +"Parse PROTO_FILES and generate output based on the options given:\n" +" -IPATH, --proto_path=PATH Specify the directory in which to search for\n" +" imports. May be specified multiple times;\n" +" directories will be searched in order. If not\n" +" given, the current working directory is used.\n" +" --version Show version info and exit.\n" +" -h, --help Show this text and exit.\n" +" --encode=MESSAGE_TYPE Read a text-format message of the given type\n" +" from standard input and write it in binary\n" +" to standard output. The message type must\n" +" be defined in PROTO_FILES or their imports.\n" +" --decode=MESSAGE_TYPE Read a binary message of the given type from\n" +" standard input and write it in text format\n" +" to standard output. The message type must\n" +" be defined in PROTO_FILES or their imports.\n" +" --decode_raw Read an arbitrary protocol message from\n" +" standard input and write the raw tag/value\n" +" pairs in text format to standard output. No\n" +" PROTO_FILES should be given when using this\n" +" flag.\n" +" -oFILE, Writes a FileDescriptorSet (a protocol buffer,\n" +" --descriptor_set_out=FILE defined in descriptor.proto) containing all of\n" +" the input files to FILE.\n" +" --include_imports When using --descriptor_set_out, also include\n" +" all dependencies of the input files in the\n" +" set, so that the set is self-contained." << endl; + + for (GeneratorMap::iterator iter = generators_.begin(); + iter != generators_.end(); ++iter) { + // FIXME(kenton): If the text is long enough it will wrap, which is ugly, + // but fixing this nicely (e.g. splitting on spaces) is probably more + // trouble than it's worth. + cerr << " " << iter->first << "=OUT_DIR " + << string(19 - iter->first.size(), ' ') // Spaces for alignment. + << iter->second.help_text << endl; + } +} + +bool CommandLineInterface::GenerateOutput( + const FileDescriptor* parsed_file, + const OutputDirective& output_directive) { + // Create the output directory. + DiskOutputDirectory output_directory(output_directive.output_location); + if (!output_directory.VerifyExistence()) { + return false; + } + + // Opened successfully. Write it. + + // Call the generator. + string error; + if (!output_directive.generator->Generate( + parsed_file, output_directive.parameter, &output_directory, &error)) { + // Generator returned an error. + cerr << output_directive.name << ": " << error << endl; + return false; + } + + // Check for write errors. + if (output_directory.had_error()) { + return false; + } + + return true; +} + +bool CommandLineInterface::EncodeOrDecode(const DescriptorPool* pool) { + // Look up the type. + const Descriptor* type = pool->FindMessageTypeByName(codec_type_); + if (type == NULL) { + cerr << "Type not defined: " << codec_type_ << endl; + return false; + } + + DynamicMessageFactory dynamic_factory(pool); + scoped_ptr message(dynamic_factory.GetPrototype(type)->New()); + + if (mode_ == MODE_ENCODE) { + SetFdToTextMode(STDIN_FILENO); + SetFdToBinaryMode(STDOUT_FILENO); + } else { + SetFdToBinaryMode(STDIN_FILENO); + SetFdToTextMode(STDOUT_FILENO); + } + + io::FileInputStream in(STDIN_FILENO); + io::FileOutputStream out(STDOUT_FILENO); + + if (mode_ == MODE_ENCODE) { + // Input is text. + ErrorPrinter error_collector; + TextFormat::Parser parser; + parser.RecordErrorsTo(&error_collector); + parser.AllowPartialMessage(true); + + if (!parser.Parse(&in, message.get())) { + cerr << "Failed to parse input." << endl; + return false; + } + } else { + // Input is binary. + if (!message->ParsePartialFromZeroCopyStream(&in)) { + cerr << "Failed to parse input." << endl; + return false; + } + } + + if (!message->IsInitialized()) { + cerr << "warning: Input message is missing required fields: " + << message->InitializationErrorString() << endl; + } + + if (mode_ == MODE_ENCODE) { + // Output is binary. + if (!message->SerializePartialToZeroCopyStream(&out)) { + cerr << "output: I/O error." << endl; + return false; + } + } else { + // Output is text. + if (!TextFormat::Print(*message, &out)) { + cerr << "output: I/O error." << endl; + return false; + } + } + + return true; +} + +bool CommandLineInterface::WriteDescriptorSet( + const vector parsed_files) { + FileDescriptorSet file_set; + set already_added; + vector to_add(parsed_files); + + while (!to_add.empty()) { + const FileDescriptor* file = to_add.back(); + to_add.pop_back(); + if (already_added.insert(file).second) { + // This file was not already in the set. + file->CopyTo(file_set.add_file()); + + if (imports_in_descriptor_set_) { + // Add all of this file's dependencies. + for (int i = 0; i < file->dependency_count(); i++) { + to_add.push_back(file->dependency(i)); + } + } + } + } + + int fd; + do { + fd = open(descriptor_set_name_.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0666); + } while (fd < 0 && errno == EINTR); + + if (fd < 0) { + perror(descriptor_set_name_.c_str()); + return false; + } + + io::FileOutputStream out(fd); + if (!file_set.SerializeToZeroCopyStream(&out)) { + cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl; + out.Close(); + return false; + } + if (!out.Close()) { + cerr << descriptor_set_name_ << ": " << strerror(out.GetErrno()) << endl; + return false; + } + + return true; +} + + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/command_line_interface.h b/src/google/protobuf/compiler/command_line_interface.h new file mode 100644 index 00000000..9185e47a --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface.h @@ -0,0 +1,238 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Implements the Protocol Compiler front-end such that it may be reused by +// custom compilers written to support other languages. + +#ifndef GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ +#define GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +class FileDescriptor; // descriptor.h +class DescriptorPool; // descriptor.h + +namespace compiler { + +class CodeGenerator; // code_generator.h +class DiskSourceTree; // importer.h + +// This class implements the command-line interface to the protocol compiler. +// It is designed to make it very easy to create a custom protocol compiler +// supporting the languages of your choice. For example, if you wanted to +// create a custom protocol compiler binary which includes both the regular +// C++ support plus support for your own custom output "Foo", you would +// write a class "FooGenerator" which implements the CodeGenerator interface, +// then write a main() procedure like this: +// +// int main(int argc, char* argv[]) { +// google::protobuf::compiler::CommandLineInterface cli; +// +// // Support generation of C++ source and headers. +// google::protobuf::compiler::cpp::CppGenerator cpp_generator; +// cli.RegisterGenerator("--cpp_out", &cpp_generator, +// "Generate C++ source and header."); +// +// // Support generation of Foo code. +// FooGenerator foo_generator; +// cli.RegisterGenerator("--foo_out", &foo_generator, +// "Generate Foo file."); +// +// return cli.Run(argc, argv); +// } +// +// The compiler is invoked with syntax like: +// protoc --cpp_out=outdir --foo_out=outdir --proto_path=src foo.proto +// +// For a full description of the command-line syntax, invoke it with --help. +class LIBPROTOC_EXPORT CommandLineInterface { + public: + CommandLineInterface(); + ~CommandLineInterface(); + + // Register a code generator for a language. + // + // Parameters: + // * flag_name: The command-line flag used to specify an output file of + // this type. The name must start with a '-'. If the name is longer + // than one letter, it must start with two '-'s. + // * generator: The CodeGenerator which will be called to generate files + // of this type. + // * help_text: Text describing this flag in the --help output. + // + // Some generators accept extra parameters. You can specify this parameter + // on the command-line by placing it before the output directory, separated + // by a colon: + // protoc --foo_out=enable_bar:outdir + // The text before the colon is passed to CodeGenerator::Generate() as the + // "parameter". + void RegisterGenerator(const string& flag_name, + CodeGenerator* generator, + const string& help_text); + + // Run the Protocol Compiler with the given command-line parameters. + // Returns the error code which should be returned by main(). + // + // It may not be safe to call Run() in a multi-threaded environment because + // it calls strerror(). I'm not sure why you'd want to do this anyway. + int Run(int argc, const char* const argv[]); + + // Call SetInputsAreCwdRelative(true) if the input files given on the command + // line should be interpreted relative to the proto import path specified + // using --proto_path or -I flags. Otherwise, input file names will be + // interpreted relative to the current working directory (or as absolute + // paths if they start with '/'), though they must still reside inside + // a directory given by --proto_path or the compiler will fail. The latter + // mode is generally more intuitive and easier to use, especially e.g. when + // defining implicit rules in Makefiles. + void SetInputsAreProtoPathRelative(bool enable) { + inputs_are_proto_path_relative_ = enable; + } + + // Provides some text which will be printed when the --version flag is + // used. The version of libprotoc will also be printed on the next line + // after this text. + void SetVersionInfo(const string& text) { + version_info_ = text; + } + + + private: + // ----------------------------------------------------------------- + + class ErrorPrinter; + class DiskOutputDirectory; + class ErrorReportingFileOutput; + + // Clear state from previous Run(). + void Clear(); + + // Remaps each file in input_files_ so that it is relative to one of the + // directories in proto_path_. Returns false if an error occurred. This + // is only used if inputs_are_proto_path_relative_ is false. + bool MakeInputsBeProtoPathRelative( + DiskSourceTree* source_tree); + + // Parse all command-line arguments. + bool ParseArguments(int argc, const char* const argv[]); + + // Parses a command-line argument into a name/value pair. Returns + // true if the next argument in the argv should be used as the value, + // false otherwise. + // + // Exmaples: + // "-Isrc/protos" -> + // name = "-I", value = "src/protos" + // "--cpp_out=src/foo.pb2.cc" -> + // name = "--cpp_out", value = "src/foo.pb2.cc" + // "foo.proto" -> + // name = "", value = "foo.proto" + bool ParseArgument(const char* arg, string* name, string* value); + + // Interprets arguments parsed with ParseArgument. + bool InterpretArgument(const string& name, const string& value); + + // Print the --help text to stderr. + void PrintHelpText(); + + // Generate the given output file from the given input. + struct OutputDirective; // see below + bool GenerateOutput(const FileDescriptor* proto_file, + const OutputDirective& output_directive); + + // Implements --encode and --decode. + bool EncodeOrDecode(const DescriptorPool* pool); + + // Implements the --descriptor_set_out option. + bool WriteDescriptorSet(const vector parsed_files); + + // ----------------------------------------------------------------- + + // The name of the executable as invoked (i.e. argv[0]). + string executable_name_; + + // Version info set with SetVersionInfo(). + string version_info_; + + // Map from flag names to registered generators. + struct GeneratorInfo { + CodeGenerator* generator; + string help_text; + }; + typedef map GeneratorMap; + GeneratorMap generators_; + + // Stuff parsed from command line. + enum Mode { + MODE_COMPILE, // Normal mode: parse .proto files and compile them. + MODE_ENCODE, // --encode: read text from stdin, write binary to stdout. + MODE_DECODE // --decode: read binary from stdin, write text to stdout. + }; + + Mode mode_; + + vector > proto_path_; // Search path for proto files. + vector input_files_; // Names of the input proto files. + + // output_directives_ lists all the files we are supposed to output and what + // generator to use for each. + struct OutputDirective { + string name; + CodeGenerator* generator; + string parameter; + string output_location; + }; + vector output_directives_; + + // When using --encode or --decode, this names the type we are encoding or + // decoding. (Empty string indicates --decode_raw.) + string codec_type_; + + // If --descriptor_set_out was given, this is the filename to which the + // FileDescriptorSet should be written. Otherwise, empty. + string descriptor_set_name_; + + // True if --include_imports was given, meaning that we should + // write all transitive dependencies to the DescriptorSet. Otherwise, only + // the .proto files listed on the command-line are added. + bool imports_in_descriptor_set_; + + // Was the --disallow_services flag used? + bool disallow_services_; + + // See SetInputsAreProtoPathRelative(). + bool inputs_are_proto_path_relative_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CommandLineInterface); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_COMMAND_LINE_INTERFACE_H__ diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc new file mode 100644 index 00000000..d67cbe0d --- /dev/null +++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc @@ -0,0 +1,1278 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +#if defined(_WIN32) +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#endif + +namespace { + +class CommandLineInterfaceTest : public testing::Test { + protected: + virtual void SetUp(); + virtual void TearDown(); + + // Runs the CommandLineInterface with the given command line. The + // command is automatically split on spaces, and the string "$tmpdir" + // is replaced with TestTempDir(). + void Run(const string& command); + + // ----------------------------------------------------------------- + // Methods to set up the test (called before Run()). + + class MockCodeGenerator; + class NullCodeGenerator; + + // Registers a MockCodeGenerator with the given name. + MockCodeGenerator* RegisterGenerator(const string& generator_name, + const string& flag_name, + const string& filename, + const string& help_text); + MockCodeGenerator* RegisterErrorGenerator(const string& generator_name, + const string& error_text, + const string& flag_name, + const string& filename, + const string& help_text); + + // Registers a CodeGenerator which will not actually generate anything, + // but records the parameter passed to the generator. + NullCodeGenerator* RegisterNullGenerator(const string& flag_name); + + // Create a temp file within temp_directory_ with the given name. + // The containing directory is also created if necessary. + void CreateTempFile(const string& name, const string& contents); + + void SetInputsAreProtoPathRelative(bool enable) { + cli_.SetInputsAreProtoPathRelative(enable); + } + + // ----------------------------------------------------------------- + // Methods to check the test results (called after Run()). + + // Checks that no text was written to stderr during Run(), and Run() + // returned 0. + void ExpectNoErrors(); + + // Checks that Run() returned non-zero and the stderr output is exactly + // the text given. expected_test may contain references to "$tmpdir", + // which will be replaced by the temporary directory path. + void ExpectErrorText(const string& expected_text); + + // Checks that Run() returned non-zero and the stderr contains the given + // substring. + void ExpectErrorSubstring(const string& expected_substring); + + // Returns true if ExpectErrorSubstring(expected_substring) would pass, but + // does not fail otherwise. + bool HasAlternateErrorSubstring(const string& expected_substring); + + // Checks that MockCodeGenerator::Generate() was called in the given + // context. That is, this tests if the generator with the given name + // was called with the given parameter and proto file and produced the + // given output file. This is checked by reading the output file and + // checking that it contains the content that MockCodeGenerator would + // generate given these inputs. message_name is the name of the first + // message that appeared in the proto file; this is just to make extra + // sure that the correct file was parsed. + void ExpectGenerated(const string& generator_name, + const string& parameter, + const string& proto_name, + const string& message_name, + const string& output_file); + + void ReadDescriptorSet(const string& filename, + FileDescriptorSet* descriptor_set); + + private: + // The object we are testing. + CommandLineInterface cli_; + + // We create a directory within TestTempDir() in order to add extra + // protection against accidentally deleting user files (since we recursively + // delete this directory during the test). This is the full path of that + // directory. + string temp_directory_; + + // The result of Run(). + int return_code_; + + // The captured stderr output. + string error_text_; + + // Pointers which need to be deleted later. + vector mock_generators_to_delete_; +}; + +// A mock CodeGenerator which outputs information about the context in which +// it was called, which can then be checked. Output is written to a filename +// constructed by concatenating the filename_prefix (given to the constructor) +// with the proto file name, separated by a '.'. +class CommandLineInterfaceTest::MockCodeGenerator : public CodeGenerator { + public: + // Create a MockCodeGenerator whose Generate() method returns true. + MockCodeGenerator(const string& name, const string& filename_prefix); + + // Create a MockCodeGenerator whose Generate() method returns false + // and sets the error string to the given string. + MockCodeGenerator(const string& name, const string& filename_prefix, + const string& error); + + ~MockCodeGenerator(); + + void set_expect_write_error(bool value) { + expect_write_error_ = value; + } + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + string name_; + string filename_prefix_; + bool return_error_; + string error_; + bool expect_write_error_; +}; + +class CommandLineInterfaceTest::NullCodeGenerator : public CodeGenerator { + public: + NullCodeGenerator() : called_(false) {} + ~NullCodeGenerator() {} + + mutable bool called_; + mutable string parameter_; + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + called_ = true; + parameter_ = parameter; + return true; + } +}; + +// =================================================================== + +void CommandLineInterfaceTest::SetUp() { + // Most of these tests were written before this option was added, so we + // run with the option on (which used to be the only way) except in certain + // tests where we turn it off. + cli_.SetInputsAreProtoPathRelative(true); + + temp_directory_ = TestTempDir() + "/proto2_cli_test_temp"; + + // If the temp directory already exists, it must be left over from a + // previous run. Delete it. + if (File::Exists(temp_directory_)) { + File::DeleteRecursively(temp_directory_, NULL, NULL); + } + + // Create the temp directory. + GOOGLE_CHECK(File::CreateDir(temp_directory_.c_str(), DEFAULT_FILE_MODE)); +} + +void CommandLineInterfaceTest::TearDown() { + // Delete the temp directory. + File::DeleteRecursively(temp_directory_, NULL, NULL); + + // Delete all the MockCodeGenerators. + for (int i = 0; i < mock_generators_to_delete_.size(); i++) { + delete mock_generators_to_delete_[i]; + } + mock_generators_to_delete_.clear(); +} + +void CommandLineInterfaceTest::Run(const string& command) { + vector args; + SplitStringUsing(command, " ", &args); + + scoped_array argv(new const char*[args.size()]); + + for (int i = 0; i < args.size(); i++) { + args[i] = StringReplace(args[i], "$tmpdir", temp_directory_, true); + argv[i] = args[i].c_str(); + } + + CaptureTestStderr(); + + return_code_ = cli_.Run(args.size(), argv.get()); + + error_text_ = GetCapturedTestStderr(); +} + +// ------------------------------------------------------------------- + +CommandLineInterfaceTest::MockCodeGenerator* +CommandLineInterfaceTest::RegisterGenerator( + const string& generator_name, + const string& flag_name, + const string& filename, + const string& help_text) { + MockCodeGenerator* generator = + new MockCodeGenerator(generator_name, filename); + mock_generators_to_delete_.push_back(generator); + + cli_.RegisterGenerator(flag_name, generator, help_text); + return generator; +} + +CommandLineInterfaceTest::MockCodeGenerator* +CommandLineInterfaceTest::RegisterErrorGenerator( + const string& generator_name, + const string& error_text, + const string& flag_name, + const string& filename_prefix, + const string& help_text) { + MockCodeGenerator* generator = + new MockCodeGenerator(generator_name, filename_prefix, error_text); + mock_generators_to_delete_.push_back(generator); + + cli_.RegisterGenerator(flag_name, generator, help_text); + return generator; +} + +CommandLineInterfaceTest::NullCodeGenerator* +CommandLineInterfaceTest::RegisterNullGenerator( + const string& flag_name) { + NullCodeGenerator* generator = new NullCodeGenerator; + mock_generators_to_delete_.push_back(generator); + cli_.RegisterGenerator(flag_name, generator, ""); + return generator; +} + +void CommandLineInterfaceTest::CreateTempFile( + const string& name, + const string& contents) { + // Create parent directory, if necessary. + string::size_type slash_pos = name.find_last_of('/'); + if (slash_pos != string::npos) { + string dir = name.substr(0, slash_pos); + File::RecursivelyCreateDir(temp_directory_ + "/" + dir, 0777); + } + + // Write file. + string full_name = temp_directory_ + "/" + name; + File::WriteStringToFileOrDie(contents, full_name); +} + +// ------------------------------------------------------------------- + +void CommandLineInterfaceTest::ExpectNoErrors() { + EXPECT_EQ(0, return_code_); + EXPECT_EQ("", error_text_); +} + +void CommandLineInterfaceTest::ExpectErrorText(const string& expected_text) { + EXPECT_NE(0, return_code_); + EXPECT_EQ(StringReplace(expected_text, "$tmpdir", temp_directory_, true), + error_text_); +} + +void CommandLineInterfaceTest::ExpectErrorSubstring( + const string& expected_substring) { + EXPECT_NE(0, return_code_); + EXPECT_PRED_FORMAT2(testing::IsSubstring, expected_substring, error_text_); +} + +bool CommandLineInterfaceTest::HasAlternateErrorSubstring( + const string& expected_substring) { + EXPECT_NE(0, return_code_); + return error_text_.find(expected_substring) != string::npos; +} + +void CommandLineInterfaceTest::ExpectGenerated( + const string& generator_name, + const string& parameter, + const string& proto_name, + const string& message_name, + const string& output_file_prefix) { + // Open and read the file. + string output_file = output_file_prefix + "." + proto_name; + string file_contents; + ASSERT_TRUE(File::ReadFileToString(temp_directory_ + "/" + output_file, + &file_contents)) + << "Failed to open file: " + output_file; + + // Check that the contents are as we expect. + string expected_contents = + generator_name + ": " + parameter + ", " + proto_name + ", " + + message_name + "\n"; + EXPECT_EQ(expected_contents, file_contents) + << "Output file did not have expected contents: " + output_file; +} + +void CommandLineInterfaceTest::ReadDescriptorSet( + const string& filename, FileDescriptorSet* descriptor_set) { + string path = temp_directory_ + "/" + filename; + string file_contents; + if (!File::ReadFileToString(path, &file_contents)) { + FAIL() << "File not found: " << path; + } + if (!descriptor_set->ParseFromString(file_contents)) { + FAIL() << "Could not parse file contents: " << path; + } +} + +// =================================================================== + +CommandLineInterfaceTest::MockCodeGenerator::MockCodeGenerator( + const string& name, const string& filename_prefix) + : name_(name), + filename_prefix_(filename_prefix), + return_error_(false), + expect_write_error_(false) { +} + +CommandLineInterfaceTest::MockCodeGenerator::MockCodeGenerator( + const string& name, const string& filename_prefix, const string& error) + : name_(name), + filename_prefix_(filename_prefix), + return_error_(true), + error_(error), + expect_write_error_(false) { +} + +CommandLineInterfaceTest::MockCodeGenerator::~MockCodeGenerator() {} + +bool CommandLineInterfaceTest::MockCodeGenerator::Generate( + const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + scoped_ptr output( + output_directory->Open(filename_prefix_ + "." + file->name())); + io::Printer printer(output.get(), '$'); + map vars; + vars["name"] = name_; + vars["parameter"] = parameter; + vars["proto_name"] = file->name(); + vars["message_name"] = file->message_type_count() > 0 ? + file->message_type(0)->full_name().c_str() : "(none)"; + + printer.Print(vars, "$name$: $parameter$, $proto_name$, $message_name$\n"); + + if (expect_write_error_) { + EXPECT_TRUE(printer.failed()); + } else { + EXPECT_FALSE(printer.failed()); + } + + *error = error_; + return !return_error_; +} + +// =================================================================== + +TEST_F(CommandLineInterfaceTest, BasicOutput) { + // Test that the common case works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MultipleInputs) { + // Test parsing multiple input files. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto bar.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); + ExpectGenerated("test_generator", "", "bar.proto", "Bar", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, CreateDirectory) { + // Test that when we output to a sub-directory, it is created. + + RegisterGenerator("test_generator", "--test_out", + "bar/baz/output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", + "foo.proto", "Foo", "bar/baz/output.test"); +} + +TEST_F(CommandLineInterfaceTest, GeneratorParameters) { + // Test that generator parameters are correctly parsed from the command line. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=TestParameter:$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "TestParameter", + "foo.proto", "Foo", "output.test"); +} + +#if defined(_WIN32) || defined(__CYGWIN__) + +TEST_F(CommandLineInterfaceTest, WindowsOutputPath) { + // Test that the output path can be a Windows-style path. + + NullCodeGenerator* generator = RegisterNullGenerator("--test_out"); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n"); + + Run("protocol_compiler --test_out=C:\\ " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + EXPECT_TRUE(generator->called_); + EXPECT_EQ("", generator->parameter_); +} + +TEST_F(CommandLineInterfaceTest, WindowsOutputPathAndParameter) { + // Test that we can have a windows-style output path and a parameter. + + NullCodeGenerator* generator = RegisterNullGenerator("--test_out"); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n"); + + Run("protocol_compiler --test_out=bar:C:\\ " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + EXPECT_TRUE(generator->called_); + EXPECT_EQ("bar", generator->parameter_); +} + +#endif // defined(_WIN32) || defined(__CYGWIN__) + +TEST_F(CommandLineInterfaceTest, PathLookup) { + // Test that specifying multiple directories in the proto search path works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("b/bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + CreateTempFile("a/foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar a = 1;\n" + "}\n"); + CreateTempFile("b/foo.proto", "this should not be parsed\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/a --proto_path=$tmpdir/b foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ColonDelimitedPath) { + // Same as PathLookup, but we provide the proto_path in a single flag. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("b/bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + CreateTempFile("a/foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar a = 1;\n" + "}\n"); + CreateTempFile("b/foo.proto", "this should not be parsed\n"); + +#undef PATH_SEPARATOR +#if defined(_WIN32) +#define PATH_SEPARATOR ";" +#else +#define PATH_SEPARATOR ":" +#endif + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/a"PATH_SEPARATOR"$tmpdir/b foo.proto"); + +#undef PATH_SEPARATOR + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, NonRootMapping) { + // Test setting up a search path mapping a directory to a non-root location. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=bar=$tmpdir bar/foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "bar/foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MultipleGenerators) { + // Test that we can have multiple generators and use both in one invocation, + // each with a different output directory. + + RegisterGenerator("test_generator_1", "--test1_out", + "output1.test", "Test output 1."); + RegisterGenerator("test_generator_2", "--test2_out", + "output2.test", "Test output 2."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + // Create the "a" and "b" sub-directories. + CreateTempFile("a/dummy", ""); + CreateTempFile("b/dummy", ""); + + Run("protocol_compiler " + "--test1_out=$tmpdir/a " + "--test2_out=$tmpdir/b " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator_1", "", "foo.proto", "Foo", "a/output1.test"); + ExpectGenerated("test_generator_2", "", "foo.proto", "Foo", "b/output2.test"); +} + +TEST_F(CommandLineInterfaceTest, DisallowServicesNoServices) { + // Test that --disallow_services doesn't cause a problem when there are no + // services. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --disallow_services --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, DisallowServicesHasService) { + // Test that --disallow_services produces an error when there are services. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n" + "service Bar {}\n"); + + Run("protocol_compiler --disallow_services --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("foo.proto: This file contains services"); +} + +TEST_F(CommandLineInterfaceTest, AllowServicesHasService) { + // Test that services work fine as long as --disallow_services is not used. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n" + "service Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputs) { + // Test that we can accept working-directory-relative input files. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir $tmpdir/foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, WriteDescriptorSet) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "import \"foo.proto\";\n" + "message Bar {\n" + " optional Foo foo = 1;\n" + "}\n"); + + Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " + "--proto_path=$tmpdir bar.proto"); + + ExpectNoErrors(); + + FileDescriptorSet descriptor_set; + ReadDescriptorSet("descriptor_set", &descriptor_set); + if (HasFatalFailure()) return; + ASSERT_EQ(1, descriptor_set.file_size()); + EXPECT_EQ("bar.proto", descriptor_set.file(0).name()); +} + +TEST_F(CommandLineInterfaceTest, WriteTransitiveDescriptorSet) { + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "import \"foo.proto\";\n" + "message Bar {\n" + " optional Foo foo = 1;\n" + "}\n"); + + Run("protocol_compiler --descriptor_set_out=$tmpdir/descriptor_set " + "--include_imports --proto_path=$tmpdir bar.proto"); + + ExpectNoErrors(); + + FileDescriptorSet descriptor_set; + ReadDescriptorSet("descriptor_set", &descriptor_set); + if (HasFatalFailure()) return; + ASSERT_EQ(2, descriptor_set.file_size()); + if (descriptor_set.file(0).name() == "bar.proto") { + swap(descriptor_set.mutable_file()->mutable_data()[0], + descriptor_set.mutable_file()->mutable_data()[1]); + } + EXPECT_EQ("foo.proto", descriptor_set.file(0).name()); + EXPECT_EQ("bar.proto", descriptor_set.file(1).name()); +} + +// ------------------------------------------------------------------- + +TEST_F(CommandLineInterfaceTest, ParseErrors) { + // Test that parse errors are reported. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "foo.proto:2:1: Expected top-level statement (e.g. \"message\").\n"); +} + +TEST_F(CommandLineInterfaceTest, ParseErrorsMultipleFiles) { + // Test that parse errors are reported from multiple files. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + // We set up files such that foo.proto actually depends on bar.proto in + // two ways: Directly and through baz.proto. bar.proto's errors should + // only be reported once. + CreateTempFile("bar.proto", + "syntax = \"proto2\";\n" + "badsyntax\n"); + CreateTempFile("baz.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n"); + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "import \"baz.proto\";\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "bar.proto:2:1: Expected top-level statement (e.g. \"message\").\n" + "baz.proto: Import \"bar.proto\" was not found or had errors.\n" + "foo.proto: Import \"bar.proto\" was not found or had errors.\n" + "foo.proto: Import \"baz.proto\" was not found or had errors.\n"); +} + +TEST_F(CommandLineInterfaceTest, InputNotFoundError) { + // Test what happens if the input file is not found. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorText( + "foo.proto: File not found.\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundError) { + // Test what happens when a working-directory-relative input file is not + // found. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: No such file or directory\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotMappedError) { + // Test what happens when a working-directory-relative input file is not + // mapped to a virtual path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + // Create a directory called "bar" so that we can point --proto_path at it. + CreateTempFile("bar/dummy", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: File does not reside within any path " + "specified using --proto_path (or -I). You must specify a " + "--proto_path which encompasses this file.\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputNotFoundAndNotMappedError) { + // Check what happens if the input file is not found *and* is not mapped + // in the proto_path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + // Create a directory called "bar" so that we can point --proto_path at it. + CreateTempFile("bar/dummy", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/bar $tmpdir/foo.proto"); + + ExpectErrorText( + "$tmpdir/foo.proto: No such file or directory\n"); +} + +TEST_F(CommandLineInterfaceTest, CwdRelativeInputShadowedError) { + // Test what happens when a working-directory-relative input file is shadowed + // by another file in the virtual path. + + SetInputsAreProtoPathRelative(false); + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo/foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + CreateTempFile("bar/foo.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/foo --proto_path=$tmpdir/bar " + "$tmpdir/bar/foo.proto"); + + ExpectErrorText( + "$tmpdir/bar/foo.proto: Input is shadowed in the --proto_path " + "by \"$tmpdir/foo/foo.proto\". Either use the latter " + "file as your input or reorder the --proto_path so that the " + "former file's location comes first.\n"); +} + +TEST_F(CommandLineInterfaceTest, ProtoPathNotFoundError) { + // Test what happens if the input file is not found. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir/foo foo.proto"); + + ExpectErrorText( + "$tmpdir/foo: warning: directory does not exist.\n" + "foo.proto: File not found.\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingInputError) { + // Test that we get an error if no inputs are given. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir"); + + ExpectErrorText("Missing input file.\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingOutputError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --proto_path=$tmpdir foo.proto"); + + ExpectErrorText("Missing output directives.\n"); +} + +TEST_F(CommandLineInterfaceTest, OutputWriteError) { + MockCodeGenerator* generator = + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + generator->set_expect_write_error(true); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + // Create a directory blocking our output location. + CreateTempFile("output.test.foo.proto/foo", ""); + + Run("protocol_compiler --test_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + +#if defined(_WIN32) && !defined(__CYGWIN__) + // Windows with MSVCRT.dll produces EPERM instead of EISDIR. + if (HasAlternateErrorSubstring("output.test.foo.proto: Permission denied")) { + return; + } +#endif + + ExpectErrorSubstring("output.test.foo.proto: Is a directory"); +} + +TEST_F(CommandLineInterfaceTest, OutputDirectoryNotFoundError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir/nosuchdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("nosuchdir/: " + "No such file or directory"); +} + +TEST_F(CommandLineInterfaceTest, OutputDirectoryIsFileError) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out=$tmpdir/foo.proto " + "--proto_path=$tmpdir foo.proto"); + +#if defined(_WIN32) && !defined(__CYGWIN__) + // Windows with MSVCRT.dll produces EINVAL instead of ENOTDIR. + if (HasAlternateErrorSubstring("foo.proto/: Invalid argument")) { + return; + } +#endif + + ExpectErrorSubstring("foo.proto/: Not a directory"); +} + +TEST_F(CommandLineInterfaceTest, GeneratorError) { + RegisterErrorGenerator("error_generator", "Test error message.", + "--error_out", "output.test", "Test error output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --error_out=$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectErrorSubstring("--error_out: Test error message."); +} + +TEST_F(CommandLineInterfaceTest, HelpText) { + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + RegisterErrorGenerator("error_generator", "Test error message.", + "--error_out", "output.test", "Test error output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("test_exec_name --help"); + + ExpectErrorSubstring("Usage: test_exec_name "); + ExpectErrorSubstring("--test_out=OUT_DIR"); + ExpectErrorSubstring("Test output."); + ExpectErrorSubstring("--error_out=OUT_DIR"); + ExpectErrorSubstring("Test error output."); +} + +// ------------------------------------------------------------------- +// Flag parsing tests + +TEST_F(CommandLineInterfaceTest, ParseSingleCharacterFlag) { + // Test that a single-character flag works. + + RegisterGenerator("test_generator", "-t", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler -t$tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ParseSpaceDelimitedValue) { + // Test that separating the flag value with a space works. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler --test_out $tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, ParseSingleCharacterSpaceDelimitedValue) { + // Test that separating the flag value with a space works for + // single-character flags. + + RegisterGenerator("test_generator", "-t", + "output.test", "Test output."); + + CreateTempFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + Run("protocol_compiler -t $tmpdir " + "--proto_path=$tmpdir foo.proto"); + + ExpectNoErrors(); + ExpectGenerated("test_generator", "", "foo.proto", "Foo", "output.test"); +} + +TEST_F(CommandLineInterfaceTest, MissingValueError) { + // Test that we get an error if a flag is missing its value. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out --proto_path=$tmpdir foo.proto"); + + ExpectErrorText("Missing value for flag: --test_out\n"); +} + +TEST_F(CommandLineInterfaceTest, MissingValueAtEndError) { + // Test that we get an error if the last argument is a flag requiring a + // value. + + RegisterGenerator("test_generator", "--test_out", + "output.test", "Test output."); + + Run("protocol_compiler --test_out"); + + ExpectErrorText("Missing value for flag: --test_out\n"); +} + +// =================================================================== + +// Test for --encode and --decode. Note that it would be easier to do this +// test as a shell script, but we'd like to be able to run the test on +// platforms that don't have a Bourne-compatible shell available (especially +// Windows/MSVC). +class EncodeDecodeTest : public testing::Test { + protected: + virtual void SetUp() { + duped_stdin_ = dup(STDIN_FILENO); + } + + virtual void TearDown() { + dup2(duped_stdin_, STDIN_FILENO); + close(duped_stdin_); + } + + void RedirectStdinFromText(const string& input) { + string filename = TestTempDir() + "/test_stdin"; + File::WriteStringToFileOrDie(input, filename); + GOOGLE_CHECK(RedirectStdinFromFile(filename)); + } + + bool RedirectStdinFromFile(const string& filename) { + int fd = open(filename.c_str(), O_RDONLY); + if (fd < 0) return false; + dup2(fd, STDIN_FILENO); + close(fd); + return true; + } + + // Remove '\r' characters from text. + string StripCR(const string& text) { + string result; + + for (int i = 0; i < text.size(); i++) { + if (text[i] != '\r') { + result.push_back(text[i]); + } + } + + return result; + } + + enum Type { TEXT, BINARY }; + enum ReturnCode { SUCCESS, ERROR }; + + bool Run(const string& command) { + vector args; + args.push_back("protoc"); + SplitStringUsing(command, " ", &args); + args.push_back("--proto_path=" + TestSourceDir()); + + scoped_array argv(new const char*[args.size()]); + for (int i = 0; i < args.size(); i++) { + argv[i] = args[i].c_str(); + } + + CommandLineInterface cli; + cli.SetInputsAreProtoPathRelative(true); + + CaptureTestStdout(); + CaptureTestStderr(); + + int result = cli.Run(args.size(), argv.get()); + + captured_stdout_ = GetCapturedTestStdout(); + captured_stderr_ = GetCapturedTestStderr(); + + return result == 0; + } + + void ExpectStdoutMatchesBinaryFile(const string& filename) { + string expected_output; + ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); + + // Don't use EXPECT_EQ because we don't want to print raw binary data to + // stdout on failure. + EXPECT_TRUE(captured_stdout_ == expected_output); + } + + void ExpectStdoutMatchesTextFile(const string& filename) { + string expected_output; + ASSERT_TRUE(File::ReadFileToString(filename, &expected_output)); + + ExpectStdoutMatchesText(expected_output); + } + + void ExpectStdoutMatchesText(const string& expected_text) { + EXPECT_EQ(StripCR(expected_text), StripCR(captured_stdout_)); + } + + void ExpectStderrMatchesText(const string& expected_text) { + EXPECT_EQ(StripCR(expected_text), StripCR(captured_stderr_)); + } + + private: + int duped_stdin_; + string captured_stdout_; + string captured_stderr_; +}; + +TEST_F(EncodeDecodeTest, Encode) { + RedirectStdinFromFile(TestSourceDir() + + "/google/protobuf/testdata/text_format_unittest_data.txt"); + EXPECT_TRUE(Run("google/protobuf/unittest.proto " + "--encode=protobuf_unittest.TestAllTypes")); + ExpectStdoutMatchesBinaryFile(TestSourceDir() + + "/google/protobuf/testdata/golden_message"); + ExpectStderrMatchesText(""); +} + +TEST_F(EncodeDecodeTest, Decode) { + RedirectStdinFromFile(TestSourceDir() + + "/google/protobuf/testdata/golden_message"); + EXPECT_TRUE(Run("google/protobuf/unittest.proto " + "--decode=protobuf_unittest.TestAllTypes")); + ExpectStdoutMatchesTextFile(TestSourceDir() + + "/google/protobuf/testdata/text_format_unittest_data.txt"); + ExpectStderrMatchesText(""); +} + +TEST_F(EncodeDecodeTest, Partial) { + RedirectStdinFromText(""); + EXPECT_TRUE(Run("google/protobuf/unittest.proto " + "--encode=protobuf_unittest.TestRequired")); + ExpectStdoutMatchesText(""); + ExpectStderrMatchesText( + "warning: Input message is missing required fields: a, b, c\n"); +} + +TEST_F(EncodeDecodeTest, DecodeRaw) { + protobuf_unittest::TestAllTypes message; + message.set_optional_int32(123); + message.set_optional_string("foo"); + string data; + message.SerializeToString(&data); + + RedirectStdinFromText(data); + EXPECT_TRUE(Run("--decode_raw")); + ExpectStdoutMatchesText("1: 123\n" + "14: \"foo\"\n"); + ExpectStderrMatchesText(""); +} + +TEST_F(EncodeDecodeTest, UnknownType) { + EXPECT_FALSE(Run("google/protobuf/unittest.proto " + "--encode=NoSuchType")); + ExpectStdoutMatchesText(""); + ExpectStderrMatchesText("Type not defined: NoSuchType\n"); +} + +TEST_F(EncodeDecodeTest, ProtoParseError) { + EXPECT_FALSE(Run("google/protobuf/no_such_file.proto " + "--encode=NoSuchType")); + ExpectStdoutMatchesText(""); + ExpectStderrMatchesText( + "google/protobuf/no_such_file.proto: File not found.\n"); +} + +} // anonymous namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc new file mode 100644 index 00000000..daa66c8c --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_bootstrap_unittest.cc @@ -0,0 +1,135 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This test insures that google/protobuf/descriptor.pb.{h,cc} match exactly +// what would be generated by the protocol compiler. These files are not +// generated automatically at build time because they are compiled into the +// protocol compiler itself. So, if they were auto-generated, you'd have a +// chicken-and-egg problem. +// +// If this test fails, run the script +// "generate_descriptor_proto.sh" and add +// descriptor.pb.{h,cc} to your changelist. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +class MockOutputDirectory : public OutputDirectory { + public: + MockOutputDirectory() {} + ~MockOutputDirectory() { + STLDeleteValues(&files_); + } + + void ExpectFileMatches(const string& virtual_filename, + const string& physical_filename) { + string* expected_contents = FindPtrOrNull(files_, virtual_filename); + ASSERT_TRUE(expected_contents != NULL) + << "Generator failed to generate file: " << virtual_filename; + + string actual_contents; + File::ReadFileToStringOrDie( + TestSourceDir() + "/" + physical_filename, + &actual_contents); + EXPECT_TRUE(actual_contents == *expected_contents) + << physical_filename << " needs to be regenerated. Please run " + "generate_descriptor_proto.sh and add this file " + "to your CL."; + } + + // implements OutputDirectory -------------------------------------- + + virtual io::ZeroCopyOutputStream* Open(const string& filename) { + string** map_slot = &files_[filename]; + if (*map_slot != NULL) delete *map_slot; + *map_slot = new string; + + return new io::StringOutputStream(*map_slot); + } + + private: + map files_; +}; + +TEST(BootstrapTest, GeneratedDescriptorMatches) { + MockErrorCollector error_collector; + DiskSourceTree source_tree; + source_tree.MapPath("", TestSourceDir()); + Importer importer(&source_tree, &error_collector); + const FileDescriptor* proto_file = + importer.Import("google/protobuf/descriptor.proto"); + EXPECT_EQ("", error_collector.text_); + ASSERT_TRUE(proto_file != NULL); + + CppGenerator generator; + MockOutputDirectory output_directory; + string error; + string parameter; + parameter = "dllexport_decl=LIBPROTOBUF_EXPORT"; + ASSERT_TRUE(generator.Generate(proto_file, parameter, + &output_directory, &error)); + + output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.h", + "google/protobuf/descriptor.pb.h"); + output_directory.ExpectFileMatches("google/protobuf/descriptor.pb.cc", + "google/protobuf/descriptor.pb.cc"); +} + +} // namespace + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc new file mode 100644 index 00000000..f78d60d8 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -0,0 +1,196 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + classname_(ClassName(descriptor, false)), + dllexport_decl_(dllexport_decl) { +} + +EnumGenerator::~EnumGenerator() {} + +void EnumGenerator::GenerateDefinition(io::Printer* printer) { + map vars; + vars["classname"] = classname_; + vars["short_name"] = descriptor_->name(); + + printer->Print(vars, "enum $classname$ {\n"); + printer->Indent(); + + const EnumValueDescriptor* min_value = descriptor_->value(0); + const EnumValueDescriptor* max_value = descriptor_->value(0); + + for (int i = 0; i < descriptor_->value_count(); i++) { + vars["name"] = descriptor_->value(i)->name(); + vars["number"] = SimpleItoa(descriptor_->value(i)->number()); + vars["prefix"] = (descriptor_->containing_type() == NULL) ? + "" : classname_ + "_"; + + printer->Print(vars, "$prefix$$name$ = $number$,\n"); + + if (descriptor_->value(i)->number() < min_value->number()) { + min_value = descriptor_->value(i); + } + if (descriptor_->value(i)->number() > max_value->number()) { + max_value = descriptor_->value(i); + } + } + + printer->Outdent(); + printer->Print("};\n"); + + vars["min_name"] = min_value->name(); + vars["max_name"] = max_value->name(); + + if (dllexport_decl_.empty()) { + vars["dllexport"] = ""; + } else { + vars["dllexport"] = dllexport_decl_ + " "; + } + + printer->Print(vars, + "$dllexport$const ::google::protobuf::EnumDescriptor* $classname$_descriptor();\n" + "$dllexport$bool $classname$_IsValid(int value);\n" + "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n" + "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n" + "\n"); +} + +void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { + map vars; + vars["nested_name"] = descriptor_->name(); + vars["classname"] = classname_; + printer->Print(vars, "typedef $classname$ $nested_name$;\n"); + + for (int j = 0; j < descriptor_->value_count(); j++) { + vars["tag"] = descriptor_->value(j)->name(); + printer->Print(vars, + "static const $nested_name$ $tag$ = $classname$_$tag$;\n"); + } + + printer->Print(vars, + "static inline const ::google::protobuf::EnumDescriptor*\n" + "$nested_name$_descriptor() {\n" + " return $classname$_descriptor();\n" + "}\n" + "static inline bool $nested_name$_IsValid(int value) {\n" + " return $classname$_IsValid(value);\n" + "}\n" + "static const $nested_name$ $nested_name$_MIN =\n" + " $classname$_$nested_name$_MIN;\n" + "static const $nested_name$ $nested_name$_MAX =\n" + " $classname$_$nested_name$_MAX;\n"); +} + +void EnumGenerator::GenerateDescriptorInitializer( + io::Printer* printer, int index) { + map vars; + vars["classname"] = classname_; + vars["index"] = SimpleItoa(index); + + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$classname$_descriptor_ = file->enum_type($index$);\n"); + } else { + vars["parent"] = ClassName(descriptor_->containing_type(), false); + printer->Print(vars, + "$classname$_descriptor_ = $parent$_descriptor_->enum_type($index$);\n"); + } +} + +void EnumGenerator::GenerateMethods(io::Printer* printer) { + map vars; + vars["classname"] = classname_; + vars["builddescriptorsname"] = + GlobalBuildDescriptorsName(descriptor_->file()->name()); + + printer->Print(vars, + "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" + " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " return $classname$_descriptor_;\n" + "}\n" + "bool $classname$_IsValid(int value) {\n" + " switch(value) {\n"); + + // Multiple values may have the same number. Make sure we only cover + // each number once by first constructing a set containing all valid + // numbers, then printing a case statement for each element. + + set numbers; + for (int j = 0; j < descriptor_->value_count(); j++) { + const EnumValueDescriptor* value = descriptor_->value(j); + numbers.insert(value->number()); + } + + for (set::iterator iter = numbers.begin(); + iter != numbers.end(); ++iter) { + printer->Print( + " case $number$:\n", + "number", SimpleItoa(*iter)); + } + + printer->Print(vars, + " return true;\n" + " default:\n" + " return false;\n" + " }\n" + "}\n" + "\n"); + + if (descriptor_->containing_type() != NULL) { + // We need to "define" the static constants which were declared in the + // header, to give the linker a place to put them. Or at least the C++ + // standard says we have to. MSVC actually insists tha we do _not_ define + // them again in the .cc file. + printer->Print("#ifndef _MSC_VER\n"); + + vars["parent"] = ClassName(descriptor_->containing_type(), false); + vars["nested_name"] = descriptor_->name(); + for (int i = 0; i < descriptor_->value_count(); i++) { + vars["value"] = descriptor_->value(i)->name(); + printer->Print(vars, + "const $classname$ $parent$::$value$;\n"); + } + printer->Print(vars, + "const $classname$ $parent$::$nested_name$_MIN;\n" + "const $classname$ $parent$::$nested_name$_MAX;\n"); + + printer->Print("#endif // _MSC_VER\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h new file mode 100644 index 00000000..b30997c9 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -0,0 +1,81 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit EnumGenerator(const EnumDescriptor* descriptor, + const string& dllexport_decl); + ~EnumGenerator(); + + // Header stuff. + + // Generate header code defining the enum. This code should be placed + // within the enum's package namespace, but NOT within any class, even for + // nested enums. + void GenerateDefinition(io::Printer* printer); + + // For enums nested within a message, generate code to import all the enum's + // symbols (e.g. the enum type name, all its values, etc.) into the class's + // namespace. This should be placed inside the class definition in the + // header. + void GenerateSymbolImports(io::Printer* printer); + + // Source file stuff. + + // Generate code that initializes the global variable storing the enum's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate non-inline methods related to the enum, such as IsValidValue(). + // Goes in the .cc file. + void GenerateMethods(io::Printer* printer); + + private: + const EnumDescriptor* descriptor_; + string classname_; + string dllexport_decl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc new file mode 100644 index 00000000..e02d7d8a --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -0,0 +1,226 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const FieldDescriptor* descriptor, + map* variables) { + const EnumValueDescriptor* default_value = descriptor->default_value_enum(); + + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = ClassName(descriptor->enum_type(), true); + (*variables)["default"] = SimpleItoa(default_value->number()); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "int $name$_;\n"); +} + +void EnumFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $name$() const;\n" + "inline void set_$name$($type$ value);\n"); +} + +void EnumFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " return static_cast< $type$ >($name$_);\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " _set_bit($index$);\n" + " $name$_ = value;\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void EnumFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_($default$)"); +} + +void EnumFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "int value;\n" + "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + "if ($type$_IsValid(value)) {\n" + " set_$name$(static_cast< $type$ >(value));\n" + "} else {\n" + " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(), output));\n"); +} + +void EnumFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::EnumSize(this->$name$());\n"); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "::google::protobuf::RepeatedField $name$_;\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField& $name$() const;\n" + "inline ::google::protobuf::RepeatedField* mutable_$name$();\n" + "inline $type$ $name$(int index) const;\n" + "inline void set_$name$(int index, $type$ value);\n" + "inline void add_$name$($type$ value);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedField*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline $type$ $classname$::$name$(int index) const {\n" + " return static_cast< $type$ >($name$_.Get(index));\n" + "}\n" + "inline void $classname$::set_$name$(int index, $type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " $name$_.Set(index, value);\n" + "}\n" + "inline void $classname$::add_$name$($type$ value) {\n" + " GOOGLE_DCHECK($type$_IsValid(value));\n" + " $name$_.Add(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedEnumFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "int value;\n" + "DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value));\n" + "if ($type$_IsValid(value)) {\n" + " add_$name$(static_cast< $type$ >(value));\n" + "} else {\n" + " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::EnumSize(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h new file mode 100644 index 00000000..a297e961 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc new file mode 100644 index 00000000..87da63d7 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc @@ -0,0 +1,104 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + dllexport_decl_(dllexport_decl) { + // Construct type_traits_. + if (descriptor_->is_repeated()) { + type_traits_ = "Repeated"; + } + + switch (descriptor_->cpp_type()) { + case FieldDescriptor::CPPTYPE_ENUM: + type_traits_.append("EnumTypeTraits< "); + type_traits_.append(ClassName(descriptor_->enum_type(), true)); + type_traits_.append(" >"); + break; + case FieldDescriptor::CPPTYPE_STRING: + type_traits_.append("StringTypeTraits"); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + type_traits_.append("MessageTypeTraits< "); + type_traits_.append(ClassName(descriptor_->message_type(), true)); + type_traits_.append(" >"); + break; + default: + type_traits_.append("PrimitiveTypeTraits< "); + type_traits_.append(PrimitiveTypeName(descriptor_->cpp_type())); + type_traits_.append(" >"); + break; + } +} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) { + map vars; + vars["extendee" ] = ClassName(descriptor_->containing_type(), true); + vars["type_traits"] = type_traits_; + vars["name" ] = descriptor_->name(); + + // If this is a class member, it needs to be declared "static". Otherwise, + // it needs to be "extern". + vars["qualifier"] = + (descriptor_->extension_scope() == NULL) ? "extern" : "static"; + + if (!dllexport_decl_.empty()) { + vars["qualifier"] = dllexport_decl_ + " " + vars["qualifier"]; + } + + printer->Print(vars, + "$qualifier$ ::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" + " ::google::protobuf::internal::$type_traits$ > $name$;\n"); +} + +void ExtensionGenerator::GenerateDefinition(io::Printer* printer) { + map vars; + vars["extendee" ] = ClassName(descriptor_->containing_type(), true); + vars["number" ] = SimpleItoa(descriptor_->number()); + vars["type_traits"] = type_traits_; + vars["name" ] = descriptor_->name(); + + // If this is a class member, it needs to be declared in its class scope. + vars["scope"] = (descriptor_->extension_scope() == NULL) ? "" : + ClassName(descriptor_->extension_scope(), false) + "::"; + + printer->Print(vars, + "::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" + " ::google::protobuf::internal::$type_traits$ > $scope$$name$($number$);\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h new file mode 100644 index 00000000..149dbca9 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -0,0 +1,68 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_EXTENSION_H__ + +#include +#include + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +// Generates code for an extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ExtensionGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit ExtensionGenerator(const FieldDescriptor* descriptor, + const string& dllexport_decl); + ~ExtensionGenerator(); + + // Header stuff. + void GenerateDeclaration(io::Printer* printer); + + // Source file stuff. + void GenerateDefinition(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + string type_traits_; + string dllexport_decl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc new file mode 100644 index 00000000..2b1041be --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +FieldGenerator::~FieldGenerator() {} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr[descriptor->field_count()]) { + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + } +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { + if (field->is_repeated()) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field); + case FieldDescriptor::CPPTYPE_STRING: + return new RepeatedStringFieldGenerator(field); + case FieldDescriptor::CPPTYPE_ENUM: + return new RepeatedEnumFieldGenerator(field); + default: + return new RepeatedPrimitiveFieldGenerator(field); + } + } else { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_MESSAGE: + return new MessageFieldGenerator(field); + case FieldDescriptor::CPPTYPE_STRING: + return new StringFieldGenerator(field); + case FieldDescriptor::CPPTYPE_ENUM: + return new EnumFieldGenerator(field); + default: + return new PrimitiveFieldGenerator(field); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_field.h b/src/google/protobuf/compiler/cpp/cpp_field.h new file mode 100644 index 00000000..d37eb962 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -0,0 +1,127 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class FieldGenerator { + public: + FieldGenerator() {} + virtual ~FieldGenerator(); + + // Generate lines of code declaring members fields of the message class + // needed to represent this field. These are placed inside the message + // class. + virtual void GeneratePrivateMembers(io::Printer* printer) const = 0; + + // Generate prototypes for all of the accessor functions related to this + // field. These are placed inside the class definition. + virtual void GenerateAccessorDeclarations(io::Printer* printer) const = 0; + + // Generate inline definitions of accessor functions for this field. + // These are placed inside the header after all class definitions. + virtual void GenerateInlineAccessorDefinitions( + io::Printer* printer) const = 0; + + // Generate definitions of accessors that aren't inlined. These are + // placed somewhere in the .cc file. + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateNonInlineAccessorDefinitions( + io::Printer* printer) const {} + + // Generate lines of code (statements, not declarations) which clear the + // field. This is used to define the clear_$name$() method as well as + // the Clear() method for the whole message. + virtual void GenerateClearingCode(io::Printer* printer) const = 0; + + // Generate lines of code (statements, not declarations) which merges the + // contents of the field from the current message to the target message, + // which is stored in the generated code variable "from". + // This is used to fill in the MergeFrom method for the whole message. + // Details of this usage can be found in message.cc under the + // GenerateMergeFrom method. + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + + // Generate any initializers needed for the private members declared by + // GeneratePrivateMembers(). These go into the message class's + // constructor's initializer list. For each initializer, this method + // must print the comma and newline separating it from the *previous* + // initializer, not the *next* initailizer. That is, print a ",\n" first, + // e.g.: + // printer->Print(",\n$name$_($default$)"); + virtual void GenerateInitializer(io::Printer* printer) const = 0; + + // Generate any code that needs to go in the class's destructor. + // Most field types don't need this, so the default implementation is empty. + virtual void GenerateDestructorCode(io::Printer* printer) const {} + + // Generate lines to decode this field, which will be placed inside the + // message's MergeFromCodedStream() method. + virtual void GenerateMergeFromCodedStream(io::Printer* printer) const = 0; + + // Generate lines to serialize this field, which are placed within the + // message's SerializeWithCachedSizes() method. + virtual void GenerateSerializeWithCachedSizes(io::Printer* printer) const = 0; + + // Generate lines to compute the serialized size of this field, which + // are placed in the message's ByteSize() method. + virtual void GenerateByteSize(io::Printer* printer) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + + private: + const Descriptor* descriptor_; + scoped_array > field_generators_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc new file mode 100644 index 00000000..f88d63fc --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -0,0 +1,414 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// =================================================================== + +FileGenerator::FileGenerator(const FileDescriptor* file, + const string& dllexport_decl) + : file_(file), + message_generators_( + new scoped_ptr[file->message_type_count()]), + enum_generators_( + new scoped_ptr[file->enum_type_count()]), + service_generators_( + new scoped_ptr[file->service_count()]), + extension_generators_( + new scoped_ptr[file->extension_count()]) { + + for (int i = 0; i < file->message_type_count(); i++) { + message_generators_[i].reset( + new MessageGenerator(file->message_type(i), dllexport_decl)); + } + + for (int i = 0; i < file->enum_type_count(); i++) { + enum_generators_[i].reset( + new EnumGenerator(file->enum_type(i), dllexport_decl)); + } + + for (int i = 0; i < file->service_count(); i++) { + service_generators_[i].reset( + new ServiceGenerator(file->service(i), dllexport_decl)); + } + + for (int i = 0; i < file->extension_count(); i++) { + extension_generators_[i].reset( + new ExtensionGenerator(file->extension(i), dllexport_decl)); + } + + SplitStringUsing(file_->package(), ".", &package_parts_); +} + +FileGenerator::~FileGenerator() {} + +void FileGenerator::GenerateHeader(io::Printer* printer) { + string filename_identifier = FilenameIdentifier(file_->name()); + + // Generate top of header. + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "#ifndef PROTOBUF_$filename_identifier$__INCLUDED\n" + "#define PROTOBUF_$filename_identifier$__INCLUDED\n" + "\n" + "#include \n" + "\n", + "filename_identifier", filename_identifier); + + printer->Print( + "#include \n" + "\n"); + + // Verify the protobuf library header version is compatible with the protoc + // version before going any further. + printer->Print( + "#if GOOGLE_PROTOBUF_VERSION < $min_header_version$\n" + "#error This file was generated by a newer version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please update\n" + "#error your headers.\n" + "#endif\n" + "#if $protoc_version$ < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION\n" + "#error This file was generated by an older version of protoc which is\n" + "#error incompatible with your Protocol Buffer headers. Please\n" + "#error regenerate this file with a newer version of protoc.\n" + "#endif\n" + "\n", + "min_header_version", + SimpleItoa(protobuf::internal::kMinHeaderVersionForProtoc), + "protoc_version", SimpleItoa(GOOGLE_PROTOBUF_VERSION)); + + // OK, it's now safe to #include other files. + printer->Print( + "#include \n" + "#include \n" + "#include \n"); + + if (file_->service_count() > 0) { + printer->Print( + "#include \n"); + } + + for (int i = 0; i < file_->dependency_count(); i++) { + printer->Print( + "#include \"$dependency$.pb.h\"\n", + "dependency", StripProto(file_->dependency(i)->name())); + } + + // Open namespace. + GenerateNamespaceOpeners(printer); + + // Forward-declare the BuildDescriptors function, so that we can declare it + // to be a friend of each class. + printer->Print( + "\n" + "// Internal implementation detail -- do not call this.\n" + "void $builddescriptorsname$();\n" + "\n", + "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + + // Generate forward declarations of classes. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateForwardDeclaration(printer); + } + + printer->Print("\n"); + + // Generate enum definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateEnumDefinitions(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } + + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate class definitions. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateClassDefinition(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate service definitions. + for (int i = 0; i < file_->service_count(); i++) { + if (i > 0) { + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + service_generators_[i]->GenerateDeclarations(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Declare extension identifiers. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } + + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + // Generate class inline methods. + for (int i = 0; i < file_->message_type_count(); i++) { + if (i > 0) { + printer->Print(kThinSeparator); + printer->Print("\n"); + } + message_generators_[i]->GenerateInlineMethods(printer); + } + + // Close up namespace. + GenerateNamespaceClosers(printer); + + printer->Print( + "#endif // PROTOBUF_$filename_identifier$__INCLUDED\n", + "filename_identifier", filename_identifier); +} + +void FileGenerator::GenerateSource(io::Printer* printer) { + printer->Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "#include \"$basename$.pb.h\"\n" + "#include \n" + "#include \n" + "#include \n" + "#include \n", + "basename", StripProto(file_->name())); + + // For each dependency, write a prototype for that dependency's + // BuildDescriptors() function. We don't expose these in the header because + // they are internal implementation details, and since this is generated code + // we don't have the usual risks involved with declaring external functions + // within a .cc file. + for (int i = 0; i < file_->dependency_count(); i++) { + const FileDescriptor* dependency = file_->dependency(i); + // Open the dependency's namespace. + vector dependency_package_parts; + SplitStringUsing(dependency->package(), ".", &dependency_package_parts); + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print("namespace $name$ { ", + "name", dependency_package_parts[i]); + } + // Declare its BuildDescriptors() function. + printer->Print( + "void $function$();", + "function", GlobalBuildDescriptorsName(dependency->name())); + // Close the namespace. + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print(" }"); + } + printer->Print("\n"); + } + + GenerateNamespaceOpeners(printer); + + printer->Print( + "\n" + "namespace {\n" + "\n"); + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDescriptorDeclarations(printer); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + printer->Print( + "const ::google::protobuf::EnumDescriptor* $name$_descriptor_ = NULL;\n", + "name", ClassName(file_->enum_type(i), false)); + } + for (int i = 0; i < file_->service_count(); i++) { + printer->Print( + "const ::google::protobuf::ServiceDescriptor* $name$_descriptor_ = NULL;\n", + "name", file_->service(i)->name()); + } + + printer->Print( + "\n" + "} // namespace\n" + "\n"); + + // Define our externally-visible BuildDescriptors() function. + GenerateBuildDescriptors(printer); + + // Generate enums. + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateMethods(printer); + } + + // Generate classes. + for (int i = 0; i < file_->message_type_count(); i++) { + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + message_generators_[i]->GenerateClassMethods(printer); + } + + // Generate services. + for (int i = 0; i < file_->service_count(); i++) { + if (i == 0) printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + service_generators_[i]->GenerateImplementation(printer); + } + + // Define extensions. + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateDefinition(printer); + } + + GenerateNamespaceClosers(printer); +} + +void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { + // BuildDescriptors() is a file-level procedure which initializes all of + // the Descriptor objects for this file. It runs the first time one of the + // descriptors is accessed. This will always be at static initialization + // time, because every message has a statically-initialized default instance, + // and the constructor for a message class accesses its descriptor. See the + // constructor and the descriptor() method of message classes. + // + // We also construct the reflection object for each class inside + // BuildDescriptors(). + printer->Print( + "\n" + "void $builddescriptorsname$() {\n" + " static bool already_here = false;\n" + " if (already_here) return;\n" + " already_here = true;\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" + " ::google::protobuf::DescriptorPool* pool =\n" + " ::google::protobuf::DescriptorPool::internal_generated_pool();\n" + "\n", + "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + printer->Indent(); + + // Call the BuildDescriptors() methods for all of our dependencies, to make + // sure they get initialized first. + for (int i = 0; i < file_->dependency_count(); i++) { + const FileDescriptor* dependency = file_->dependency(i); + // Print the namespace prefix for the dependency. + vector dependency_package_parts; + SplitStringUsing(dependency->package(), ".", &dependency_package_parts); + printer->Print("::"); + for (int i = 0; i < dependency_package_parts.size(); i++) { + printer->Print("$name$::", + "name", dependency_package_parts[i]); + } + // Call its BuildDescriptors function. + printer->Print( + "$name$();\n", + "name", GlobalBuildDescriptorsName(dependency->name())); + } + + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "const ::google::protobuf::FileDescriptor* file = pool->InternalBuildGeneratedFile("); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + printer->Print("\n \"$data$\"", + "data", CEscape(file_data.substr(i, kBytesPerLine))); + } + printer->Print( + ", $size$);\n", + "size", SimpleItoa(file_data.size())); + + // Assign all global descriptors. + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + for (int i = 0; i < file_->service_count(); i++) { + service_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "// Force BuildDescriptors() to be called at static initialization time.\n" + "struct StaticDescriptorInitializer_$filename$ {\n" + " StaticDescriptorInitializer_$filename$() {\n" + " $builddescriptorsname$();\n" + " }\n" + "} static_descriptor_initializer_$filename$_;\n" + "\n", + "builddescriptorsname", GlobalBuildDescriptorsName(file_->name()), + "filename", FilenameIdentifier(file_->name())); +} + +void FileGenerator::GenerateNamespaceOpeners(io::Printer* printer) { + if (package_parts_.size() > 0) printer->Print("\n"); + + for (int i = 0; i < package_parts_.size(); i++) { + printer->Print("namespace $part$ {\n", + "part", package_parts_[i]); + } +} + +void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) { + if (package_parts_.size() > 0) printer->Print("\n"); + + for (int i = package_parts_.size() - 1; i >= 0; i--) { + printer->Print("} // namespace $part$\n", + "part", package_parts_[i]); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h new file mode 100644 index 00000000..f2255ee1 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator; // enum.h +class MessageGenerator; // message.h +class ServiceGenerator; // service.h +class ExtensionGenerator; // extension.h + +class FileGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit FileGenerator(const FileDescriptor* file, + const string& dllexport_decl); + ~FileGenerator(); + + void GenerateHeader(io::Printer* printer); + void GenerateSource(io::Printer* printer); + + private: + // Generate the BuildDescriptors() procedure, which builds all descriptors + // for types defined in the file. + void GenerateBuildDescriptors(io::Printer* printer); + + void GenerateNamespaceOpeners(io::Printer* printer); + void GenerateNamespaceClosers(io::Printer* printer); + + const FileDescriptor* file_; + + scoped_array > message_generators_; + scoped_array > enum_generators_; + scoped_array > service_generators_; + scoped_array > extension_generators_; + + // E.g. if the package is foo.bar, package_parts_ is {"foo", "bar"}. + vector package_parts_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc new file mode 100644 index 00000000..200f6358 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -0,0 +1,135 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +// Parses a set of comma-delimited name/value pairs, e.g.: +// "foo=bar,baz,qux=corge" +// parses to the pairs: +// ("foo", "bar"), ("baz", ""), ("qux", "corge") +void ParseOptions(const string& text, vector >* output) { + vector parts; + SplitStringUsing(text, ",", &parts); + + for (int i = 0; i < parts.size(); i++) { + string::size_type equals_pos = parts[i].find_first_of('='); + pair value; + if (equals_pos == string::npos) { + value.first = parts[i]; + value.second = ""; + } else { + value.first = parts[i].substr(0, equals_pos); + value.second = parts[i].substr(equals_pos + 1); + } + output->push_back(value); + } +} + +} // namespace + +CppGenerator::CppGenerator() {} +CppGenerator::~CppGenerator() {} + +bool CppGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + vector > options; + ParseOptions(parameter, &options); + + // ----------------------------------------------------------------- + // parse generator options + + // TODO(kenton): If we ever have more options, we may want to create a + // class that encapsulates them which we can pass down to all the + // generator classes. Currently we pass dllexport_decl down to all of + // them via the constructors, but we don't want to have to add another + // constructor parameter for every option. + + // If the dllexport_decl option is passed to the compiler, we need to write + // it in front of every symbol that should be exported if this .proto is + // compiled into a Windows DLL. E.g., if the user invokes the protocol + // compiler as: + // protoc --cpp_out=dllexport_decl=FOO_EXPORT:outdir foo.proto + // then we'll define classes like this: + // class FOO_EXPORT Foo { + // ... + // } + // FOO_EXPORT is a macro which should expand to __declspec(dllexport) or + // __declspec(dllimport) depending on what is being compiled. + string dllexport_decl; + + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "dllexport_decl") { + dllexport_decl = options[i].second; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + // ----------------------------------------------------------------- + + + string basename = StripProto(file->name()); + basename.append(".pb"); + + FileGenerator file_generator(file, dllexport_decl); + + // Generate header. + { + scoped_ptr output( + output_directory->Open(basename + ".h")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateHeader(&printer); + } + + // Generate cc file. + { + scoped_ptr output( + output_directory->Open(basename + ".cc")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateSource(&printer); + } + + return true; +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.h b/src/google/protobuf/compiler/cpp/cpp_generator.h new file mode 100644 index 00000000..26fb8e97 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_generator.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Generates C++ code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// CodeGenerator implementation which generates a C++ source file and +// header. If you create your own protocol compiler binary and you want +// it to support C++ output, you can do so by registering an instance of this +// CodeGenerator with the CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT CppGenerator : public CodeGenerator { + public: + CppGenerator(); + ~CppGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CppGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc new file mode 100644 index 00000000..6a49f815 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -0,0 +1,197 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +string DotsToUnderscores(const string& name) { + return StringReplace(name, ".", "_", true); +} + +string DotsToColons(const string& name) { + return StringReplace(name, ".", "::", true); +} + +const char* const kKeywordList[] = { + "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", + "catch", "char", "class", "compl", "const", "const_cast", "continue", + "default", "delete", "do", "double", "dynamic_cast", "else", "enum", + "explicit", "extern", "false", "float", "for", "friend", "goto", "if", + "inline", "int", "long", "mutable", "namespace", "new", "not", "not_eq", + "operator", "or", "or_eq", "private", "protected", "public", "register", + "reinterpret_cast", "return", "short", "signed", "sizeof", "static", + "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", + "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while", "xor", "xor_eq" +}; + +hash_set MakeKeywordsMap() { + hash_set result; + for (int i = 0; i < GOOGLE_ARRAYSIZE(kKeywordList); i++) { + result.insert(kKeywordList[i]); + } + return result; +} + +hash_set kKeywords = MakeKeywordsMap(); + +} // namespace + +const char kThickSeparator[] = + "// ===================================================================\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\n"; + +string ClassName(const Descriptor* descriptor, bool qualified) { + // Find "outer", the descriptor of the top-level message in which + // "descriptor" is embedded. + const Descriptor* outer = descriptor; + while (outer->containing_type() != NULL) outer = outer->containing_type(); + + const string& outer_name = outer->full_name(); + string inner_name = descriptor->full_name().substr(outer_name.size()); + + if (qualified) { + return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name); + } else { + return outer->name() + DotsToUnderscores(inner_name); + } +} + +string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { + if (enum_descriptor->containing_type() == NULL) { + if (qualified) { + return DotsToColons(enum_descriptor->full_name()); + } else { + return enum_descriptor->name(); + } + } else { + string result = ClassName(enum_descriptor->containing_type(), qualified); + result += '_'; + result += enum_descriptor->name(); + return result; + } +} + +string FieldName(const FieldDescriptor* field) { + string result = field->name(); + LowerString(&result); + if (kKeywords.count(result) > 0) { + result.append("_"); + } + return result; +} + +string StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +const char* PrimitiveTypeName(FieldDescriptor::CppType type) { + switch (type) { + case FieldDescriptor::CPPTYPE_INT32 : return "::google::protobuf::int32"; + case FieldDescriptor::CPPTYPE_INT64 : return "::google::protobuf::int64"; + case FieldDescriptor::CPPTYPE_UINT32 : return "::google::protobuf::uint32"; + case FieldDescriptor::CPPTYPE_UINT64 : return "::google::protobuf::uint64"; + case FieldDescriptor::CPPTYPE_DOUBLE : return "double"; + case FieldDescriptor::CPPTYPE_FLOAT : return "float"; + case FieldDescriptor::CPPTYPE_BOOL : return "bool"; + case FieldDescriptor::CPPTYPE_ENUM : return "int"; + case FieldDescriptor::CPPTYPE_STRING : return "::std::string"; + case FieldDescriptor::CPPTYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // CppTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { + switch (type) { + case FieldDescriptor::TYPE_INT32 : return "Int32"; + case FieldDescriptor::TYPE_INT64 : return "Int64"; + case FieldDescriptor::TYPE_UINT32 : return "UInt32"; + case FieldDescriptor::TYPE_UINT64 : return "UInt64"; + case FieldDescriptor::TYPE_SINT32 : return "SInt32"; + case FieldDescriptor::TYPE_SINT64 : return "SInt64"; + case FieldDescriptor::TYPE_FIXED32 : return "Fixed32"; + case FieldDescriptor::TYPE_FIXED64 : return "Fixed64"; + case FieldDescriptor::TYPE_SFIXED32: return "SFixed32"; + 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_ENUM : return "Enum"; + + case FieldDescriptor::TYPE_STRING : return "String"; + case FieldDescriptor::TYPE_BYTES : return "Bytes"; + 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 ""; +} + +// Convert a file name into a valid identifier. +string FilenameIdentifier(const string& filename) { + string result; + for (int i = 0; i < filename.size(); i++) { + if (ascii_isalnum(filename[i])) { + result.push_back(filename[i]); + } else { + // Not alphanumeric. To avoid any possibility of name conflicts we + // use the hex code for the character. + result.push_back('_'); + char buffer[kFastToBufferSize]; + result.append(FastHexToBuffer(static_cast(filename[i]), buffer)); + } + } + return result; +} + +// Return the name of the BuildDescriptors() function for a given file. +string GlobalBuildDescriptorsName(const string& filename) { + return "protobuf_BuildDesc_" + FilenameIdentifier(filename); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h new file mode 100644 index 00000000..7f57d694 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// Commonly-used separator comments. Thick is a line of '=', thin is a line +// of '-'. +extern const char kThickSeparator[]; +extern const char kThinSeparator[]; + +// Returns the non-nested type name for the given type. If "qualified" is +// true, prefix the type with the full namespace. For example, if you had: +// package foo.bar; +// message Baz { message Qux {} } +// Then the qualified ClassName for Qux would be: +// ::foo::bar::Baz_Qux +// While the non-qualified version would be: +// Baz_Qux +string ClassName(const Descriptor* descriptor, bool qualified); +string ClassName(const EnumDescriptor* enum_descriptor, bool qualified); + +// Get the (unqualified) name that should be used for this field in C++ code. +// The name is coerced to lower-case to emulate proto1 behavior. People +// should be using lowercase-with-underscores style for proto field names +// anyway, so normally this just returns field->name(). +string FieldName(const FieldDescriptor* field); + +// Returns the scope where the field was defined (for extensions, this is +// different from the message type to which the field applies). +inline const Descriptor* FieldScope(const FieldDescriptor* field) { + return field->is_extension() ? + field->extension_scope() : field->containing_type(); +} + +// Strips ".proto" or ".protodevel" from the end of a filename. +string StripProto(const string& filename); + +// Get the C++ type name for a primitive type (e.g. "double", "::google::protobuf::int32", etc.). +// Note: non-built-in type names will be qualified, meaning they will start +// with a ::. If you are using the type as a template parameter, you will +// need to insure there is a space between the < and the ::, because the +// ridiculous C++ standard defines "<:" to be a synonym for "[". +const char* PrimitiveTypeName(FieldDescriptor::CppType type); + +// Get the declared type name in CamelCase format, as is used e.g. for the +// methods of WireFormat. For example, TYPE_INT32 becomes "Int32". +const char* DeclaredTypeMethodName(FieldDescriptor::Type type); + +// Convert a file name into a valid identifier. +string FilenameIdentifier(const string& filename); + +// Return the name of the BuildDescriptors() function for a given file. +string GlobalBuildDescriptorsName(const string& filename); + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_HELPERS_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc new file mode 100644 index 00000000..afd99314 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -0,0 +1,1466 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // Print the field's proto-syntax definition as a comment. We don't want to + // print group bodies so we cut off after the first line. + string def = field->DebugString(); + printer->Print("// $def$\n", + "def", def.substr(0, def.find_first_of('\n'))); +} + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +const char* kWireTypeNames[] = { + "VARINT", + "FIXED64", + "LENGTH_DELIMITED", + "START_GROUP", + "END_GROUP", + "FIXED32", +}; + +// 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; +} + +// Functor for sorting extension ranges by their "start" field number. +struct ExtensionRangeSorter { + bool operator()(const Descriptor::ExtensionRange* left, + const Descriptor::ExtensionRange* right) const { + return left->start < right->start; + } +}; + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its IsInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +static bool HasRequiredFields( + const Descriptor* type, + hash_set* already_seen) { + if (already_seen->count(type) > 0) { + // Since the first occurrence of a required field causes the whole + // function to return true, we can assume that if the type is already + // in the cache it didn't have any required fields. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +static bool HasRequiredFields(const Descriptor* type) { + hash_set already_seen; + return HasRequiredFields(type, &already_seen); +} + +} + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor), + classname_(ClassName(descriptor, false)), + dllexport_decl_(dllexport_decl), + field_generators_(descriptor), + nested_generators_(new scoped_ptr[ + descriptor->nested_type_count()]), + enum_generators_(new scoped_ptr[ + descriptor->enum_type_count()]), + extension_generators_(new scoped_ptr[ + descriptor->extension_count()]) { + + for (int i = 0; i < descriptor->nested_type_count(); i++) { + nested_generators_[i].reset( + new MessageGenerator(descriptor->nested_type(i), dllexport_decl)); + } + + for (int i = 0; i < descriptor->enum_type_count(); i++) { + enum_generators_[i].reset( + new EnumGenerator(descriptor->enum_type(i), dllexport_decl)); + } + + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset( + new ExtensionGenerator(descriptor->extension(i), dllexport_decl)); + } +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator:: +GenerateForwardDeclaration(io::Printer* printer) { + printer->Print("class $classname$;\n", + "classname", classname_); + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateForwardDeclaration(printer); + } +} + +void MessageGenerator:: +GenerateEnumDefinitions(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateEnumDefinitions(printer); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDefinition(printer); + } +} + +void MessageGenerator:: +GenerateFieldAccessorDeclarations(io::Printer* printer) { + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + vars["name"] = FieldName(field); + + if (field->is_repeated()) { + printer->Print(vars, "inline int $name$_size() const;\n"); + } else { + printer->Print(vars, "inline bool has_$name$() const;\n"); + } + + printer->Print(vars, "inline void clear_$name$();\n"); + + // Generate type-specific accessor declarations. + field_generators_.get(field).GenerateAccessorDeclarations(printer); + + printer->Print("\n"); + } + + if (descriptor_->extension_range_count() > 0) { + // Generate accessors for extensions. + + // Normally I'd generate prototypes here and generate the actual + // definitions of these methods in GenerateFieldAccessorDefinitions, but + // the prototypes for these silly methods are so absurdly complicated that + // it meant way too much repitition. + // + // We use "_proto_TypeTraits" as a type name below because "TypeTraits" + // causes problems if the class has a nested message or enum type with that + // name and "_TypeTraits" is technically reserved for the C++ library since + // it starts with an underscore followed by a capital letter. + printer->Print( + // Has, Size, Clear + "template \n" + "inline bool HasExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _extensions_.Has(id.number());\n" + "}\n" + "\n" + "template \n" + "inline void ClearExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " _extensions_.ClearExtension(id.number());\n" + "}\n" + "\n" + "template \n" + "inline int ExtensionSize(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _extensions_.ExtensionSize(id.number());\n" + "}\n" + "\n" + + // Singular accessors + "template \n" + "inline typename _proto_TypeTraits::ConstType GetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) const {\n" + " return _proto_TypeTraits::Get(id.number(), _extensions_);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " return _proto_TypeTraits::Mutable(id.number(), &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void SetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Set(id.number(), value, &_extensions_);\n" + "}\n" + "\n" + + // Repeated accessors + "template \n" + "inline typename _proto_TypeTraits::ConstType GetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index) const {\n" + " return _proto_TypeTraits::Get(id.number(), _extensions_, index);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index) {\n" + " return _proto_TypeTraits::Mutable(id.number(),index,&_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void SetExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " int index, typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Set(id.number(), index, value, &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline typename _proto_TypeTraits::MutableType AddExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id) {\n" + " return _proto_TypeTraits::Add(id.number(), &_extensions_);\n" + "}\n" + "\n" + "template \n" + "inline void AddExtension(\n" + " const ::google::protobuf::internal::ExtensionIdentifier<\n" + " $classname$, _proto_TypeTraits>& id,\n" + " typename _proto_TypeTraits::ConstType value) {\n" + " _proto_TypeTraits::Add(id.number(), value, &_extensions_);\n" + "}\n", + "classname", classname_); + } +} + +void MessageGenerator:: +GenerateFieldAccessorDefinitions(io::Printer* printer) { + printer->Print("// $classname$\n\n", "classname", classname_); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + PrintFieldComment(printer, field); + + map vars; + vars["name"] = FieldName(field); + vars["index"] = SimpleItoa(field->index()); + vars["classname"] = classname_; + + // Generate has_$name$() or $name$_size(). + if (field->is_repeated()) { + printer->Print(vars, + "inline int $classname$::$name$_size() const {\n" + " return $name$_.size();\n" + "}\n"); + } else { + // Singular field. + printer->Print(vars, + "inline bool $classname$::has_$name$() const {\n" + " return _has_bit($index$);\n" + "}\n"); + } + + // Generate clear_$name$() + printer->Print(vars, + "inline void $classname$::clear_$name$() {\n"); + + printer->Indent(); + field_generators_.get(field).GenerateClearingCode(printer); + printer->Outdent(); + + if (!field->is_repeated()) { + printer->Print(vars, " _clear_bit($index$);\n"); + } + + printer->Print("}\n"); + + // Generate type-specific accessors. + field_generators_.get(field).GenerateInlineAccessorDefinitions(printer); + + printer->Print("\n"); + } +} + +void MessageGenerator:: +GenerateClassDefinition(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateClassDefinition(printer); + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + map vars; + vars["classname"] = classname_; + vars["field_count"] = SimpleItoa(descriptor_->field_count()); + if (dllexport_decl_.empty()) { + vars["dllexport"] = ""; + } else { + vars["dllexport"] = dllexport_decl_ + " "; + } + vars["builddescriptorsname"] = + GlobalBuildDescriptorsName(descriptor_->file()->name()); + + printer->Print(vars, + "class $dllexport$$classname$ : public ::google::protobuf::Message {\n" + " public:\n"); + printer->Indent(); + + printer->Print(vars, + "$classname$();\n" + "virtual ~$classname$();\n" + "\n" + "$classname$(const $classname$& from);\n" + "\n" + "inline $classname$& operator=(const $classname$& from) {\n" + " CopyFrom(from);\n" + " return *this;\n" + "}\n" + "\n" + "inline static const $classname$& default_instance() {\n" + " return default_instance_;\n" + "}\n" + "\n" + "inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {\n" + " return _unknown_fields_;\n" + "}\n" + "\n" + "inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {\n" + " return &_unknown_fields_;\n" + "}\n" + "\n" + "static const ::google::protobuf::Descriptor* descriptor();\n" + "\n" + "// implements Message ----------------------------------------------\n" + "\n" + "$classname$* New() const;\n"); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print(vars, + "void CopyFrom(const ::google::protobuf::Message& from);\n" + "void MergeFrom(const ::google::protobuf::Message& from);\n" + "void CopyFrom(const $classname$& from);\n" + "void MergeFrom(const $classname$& from);\n" + "void Clear();\n" + "bool IsInitialized() const;\n" + "int ByteSize() const;\n" + "\n" + "bool MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input);\n" + "bool SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const;\n"); + } + + printer->Print(vars, + "int GetCachedSize() const { return _cached_size_; }\n" + "private:\n" + "void SetCachedSize(int size) const { _cached_size_ = size; }\n" + "public:\n" + "\n" + "const ::google::protobuf::Descriptor* GetDescriptor() const;\n" + "const ::google::protobuf::Reflection* GetReflection() const;\n" + "\n" + "// nested types ----------------------------------------------------\n" + "\n"); + + // Import all nested message classes into this class's scope with typedefs. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + const Descriptor* nested_type = descriptor_->nested_type(i); + printer->Print("typedef $nested_full_name$ $nested_name$;\n", + "nested_name", nested_type->name(), + "nested_full_name", ClassName(nested_type, false)); + } + + if (descriptor_->nested_type_count() > 0) { + printer->Print("\n"); + } + + // Import all nested enums and their values into this class's scope with + // typedefs and constants. + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateSymbolImports(printer); + printer->Print("\n"); + } + + printer->Print( + "// accessors -------------------------------------------------------\n" + "\n"); + + // Generate accessor methods for all fields. + GenerateFieldAccessorDeclarations(printer); + + // Declare extension identifiers. + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_[i]->GenerateDeclaration(printer); + } + + // Generate private members for fields. + printer->Outdent(); + printer->Print(" private:\n"); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "::google::protobuf::internal::ExtensionSet _extensions_;\n"); + } + + // TODO(kenton): Make _cached_size_ an atomic when C++ supports it. + printer->Print( + "::google::protobuf::UnknownFieldSet _unknown_fields_;\n" + "mutable int _cached_size_;\n" + "\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GeneratePrivateMembers(printer); + } + + // Generate offsets and _has_bits_ boilerplate. + printer->Print(vars, + "friend void $builddescriptorsname$();\n" + "static const $classname$ default_instance_;\n"); + + if (descriptor_->field_count() > 0) { + printer->Print(vars, + "static const int _offsets_[$field_count$];\n" + "\n" + "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n"); + } else { + // Zero-size arrays aren't technically allowed, and MSVC in particular + // doesn't like them. We still need to declare these arrays to make + // other code compile. Since this is an uncommon case, we'll just declare + // them with size 1 and waste some space. Oh well. + printer->Print( + "static const int _offsets_[1];\n" + "\n" + "::google::protobuf::uint32 _has_bits_[1];\n"); + } + + printer->Print( + "\n" + "// WHY DOES & HAVE LOWER PRECEDENCE THAN != !?\n" + "inline bool _has_bit(int index) const {\n" + " return (_has_bits_[index / 32] & (1u << (index % 32))) != 0;\n" + "}\n" + "inline void _set_bit(int index) {\n" + " _has_bits_[index / 32] |= (1u << (index % 32));\n" + "}\n" + "inline void _clear_bit(int index) {\n" + " _has_bits_[index / 32] &= ~(1u << (index % 32));\n" + "}\n"); + + printer->Outdent(); + printer->Print(vars, "};"); +} + +void MessageGenerator:: +GenerateInlineMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateInlineMethods(printer); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + GenerateFieldAccessorDefinitions(printer); +} + +void MessageGenerator:: +GenerateDescriptorDeclarations(io::Printer* printer) { + printer->Print( + "const ::google::protobuf::Descriptor* $name$_descriptor_ = NULL;\n" + "const ::google::protobuf::internal::GeneratedMessageReflection*\n" + " $name$_reflection_ = NULL;\n", + "name", classname_); + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateDescriptorDeclarations(printer); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + printer->Print( + "const ::google::protobuf::EnumDescriptor* $name$_descriptor_ = NULL;\n", + "name", ClassName(descriptor_->enum_type(i), false)); + } +} + +void MessageGenerator:: +GenerateDescriptorInitializer(io::Printer* printer, int index) { + // TODO(kenton): Passing the index to this method is redundant; just use + // descriptor_->index() instead. + map vars; + vars["classname"] = classname_; + vars["index"] = SimpleItoa(index); + + // Obtain the descriptor from the parent's descriptor. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$classname$_descriptor_ = file->message_type($index$);\n"); + } else { + vars["parent"] = ClassName(descriptor_->containing_type(), false); + printer->Print(vars, + "$classname$_descriptor_ = " + "$parent$_descriptor_->nested_type($index$);\n"); + } + + // Construct the reflection object. + printer->Print(vars, + "$classname$_reflection_ =\n" + " new ::google::protobuf::internal::GeneratedMessageReflection(\n" + " $classname$_descriptor_,\n" + " &$classname$::default_instance(),\n" + " $classname$::_offsets_,\n" + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, _has_bits_[0]),\n" + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _unknown_fields_),\n"); + if (descriptor_->extension_range_count() > 0) { + printer->Print(vars, + " GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" + "$classname$, _extensions_),\n"); + } else { + // No extensions. + printer->Print(vars, + " -1,\n"); + } + printer->Print(vars, + " ::google::protobuf::DescriptorPool::generated_pool());\n"); + + // Handle nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateDescriptorInitializer(printer, i); + } + + // Register this message type with the message factory. + printer->Print(vars, + "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" + " $classname$_descriptor_, &$classname$::default_instance());\n"); +} + +void MessageGenerator:: +GenerateClassMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + enum_generators_[i]->GenerateMethods(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateClassMethods(printer); + printer->Print("\n"); + printer->Print(kThinSeparator); + printer->Print("\n"); + } + + printer->Print( + "const $classname$ $classname$::default_instance_;\n" + "\n", + "classname", classname_); + + // Generate non-inline field definitions. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateNonInlineAccessorDefinitions(printer); + printer->Print("\n"); + } + + // Define extension identifiers. + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_[i]->GenerateDefinition(printer); + } + + GenerateOffsets(printer); + printer->Print("\n"); + + GenerateStructors(printer); + printer->Print("\n"); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateClear(printer); + printer->Print("\n"); + + GenerateMergeFromCodedStream(printer); + printer->Print("\n"); + + GenerateSerializeWithCachedSizes(printer); + printer->Print("\n"); + + GenerateByteSize(printer); + printer->Print("\n"); + + GenerateMergeFrom(printer); + printer->Print("\n"); + + GenerateCopyFrom(printer); + printer->Print("\n"); + + GenerateIsInitialized(printer); + printer->Print("\n"); + } + + printer->Print( + "const ::google::protobuf::Descriptor* $classname$::GetDescriptor() const {\n" + " return descriptor();\n" + "}\n" + "\n" + "const ::google::protobuf::Reflection* $classname$::GetReflection() const {\n" + " if ($classname$_reflection_ == NULL) $builddescriptorsname$();\n" + " return $classname$_reflection_;\n" + "}\n", + "classname", classname_, + "builddescriptorsname", + GlobalBuildDescriptorsName(descriptor_->file()->name())); +} + +void MessageGenerator:: +GenerateOffsets(io::Printer* printer) { + printer->Print( + "const int $classname$::_offsets_[$field_count$] = {\n", + "classname", classname_, + "field_count", SimpleItoa(max(1, descriptor_->field_count()))); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + printer->Print( + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n", + "classname", classname_, + "name", FieldName(field)); + } + + printer->Outdent(); + printer->Print("};\n"); +} + +void MessageGenerator:: +GenerateInitializerList(io::Printer* printer) { + printer->Indent(); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "_extensions_(&$classname$_descriptor_,\n" + " ::google::protobuf::DescriptorPool::generated_pool(),\n" + " ::google::protobuf::MessageFactory::generated_factory()),\n", + "classname", classname_); + } + + printer->Print( + "_cached_size_(0)"); + + // Write the initializers for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateInitializer(printer); + } + + printer->Outdent(); + printer->Outdent(); +} + +void MessageGenerator:: +GenerateStructors(io::Printer* printer) { + // Generate the default constructor. + printer->Print( + "$classname$::$classname$()\n" + " : ", + "classname", classname_); + GenerateInitializerList(printer); + printer->Print(" {\n" + " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " if (this == &default_instance_) {\n"); + + // The default instance needs all of its embedded message pointers + // cross-linked to other default instances. + // TODO(kenton): Maybe all message fields (even for non-default messages) + // should be initialized to point at default instances rather than NULL? + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print( + " $name$_ = const_cast< $type$*>(&$type$::default_instance());\n", + "name", FieldName(field), + "type", ClassName(field->message_type(), true)); + } + } + printer->Print( + " }\n" + "}\n" + "\n"); + + // Generate the copy constructor. + printer->Print( + "$classname$::$classname$(const $classname$& from)\n" + " : ", + "classname", classname_); + GenerateInitializerList(printer); + printer->Print(" {\n" + " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " MergeFrom(from);\n" + "}\n" + "\n"); + + // Generate the destructor. + printer->Print( + "$classname$::~$classname$() {\n", + "classname", classname_); + + printer->Indent(); + + // Write the destructors for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDestructorCode(printer); + } + + printer->Print( + "if (this != &default_instance_) {\n"); + + // We need to delete all embedded messages. + // TODO(kenton): If we make unset messages point at default instances + // instead of NULL, then it would make sense to move this code into + // MessageFieldGenerator::GenerateDestructorCode(). + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print(" delete $name$_;\n", + "name", FieldName(field)); + } + } + + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n" + "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" + " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n" + "$classname$* $classname$::New() const {\n" + " return new $classname$;\n" + "}\n", + "classname", classname_, + "builddescriptorsname", + GlobalBuildDescriptorsName(descriptor_->file()->name())); +} + +void MessageGenerator:: +GenerateClear(io::Printer* printer) { + printer->Print("void $classname$::Clear() {\n", + "classname", classname_); + printer->Indent(); + + int last_index = -1; + + if (descriptor_->extension_range_count() > 0) { + printer->Print("_extensions_.Clear();\n"); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + map vars; + vars["index"] = SimpleItoa(field->index()); + + // We can use the fact that _has_bits_ is a giant bitfield to our + // advantage: We can check up to 32 bits at a time for equality to + // zero, and skip the whole range if so. This can improve the speed + // of Clear() for messages which contain a very large number of + // optional fields of which only a few are used at a time. Here, + // we've chosen to check 8 bits at a time rather than 32. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print(vars, + "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Indent(); + } + last_index = i; + + // It's faster to just overwrite primitive types, but we should + // only clear strings and messages if they were set. + // TODO(kenton): Let the CppFieldGenerator decide this somehow. + bool should_check_bit = + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || + field->cpp_type() == FieldDescriptor::CPPTYPE_STRING; + + if (should_check_bit) { + printer->Print(vars, "if (_has_bit($index$)) {\n"); + printer->Indent(); + } + + field_generators_.get(field).GenerateClearingCode(printer); + + if (should_check_bit) { + printer->Outdent(); + printer->Print("}\n"); + } + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + // Repeated fields don't use _has_bits_ so we clear them in a separate + // pass. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + field_generators_.get(field).GenerateClearingCode(printer); + } + } + + printer->Print( + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + "mutable_unknown_fields()->Clear();\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateMergeFrom(io::Printer* printer) { + // Generate the generalized MergeFrom (aka that which takes in the Message + // base class as a parameter). + printer->Print( + "void $classname$::MergeFrom(const ::google::protobuf::Message& from) {\n" + " GOOGLE_CHECK_NE(&from, this);\n", + "classname", classname_); + printer->Indent(); + + if (descriptor_->field_count() > 0) { + // Cast the message to the proper type. If we find that the message is + // *not* of the proper type, we can still call Merge via the reflection + // system, as the GOOGLE_CHECK above ensured that we have the same descriptor + // for each message. + printer->Print( + "const $classname$* source =\n" + " ::google::protobuf::internal::dynamic_cast_if_available(\n" + " &from);\n" + "if (source == NULL) {\n" + " ::google::protobuf::internal::ReflectionOps::Merge(from, this);\n" + "} else {\n" + " MergeFrom(*source);\n" + "}\n", + "classname", classname_); + } + + printer->Outdent(); + printer->Print("}\n\n"); + + // Generate the class-specific MergeFrom, which avoids the GOOGLE_CHECK and cast. + printer->Print( + "void $classname$::MergeFrom(const $classname$& from) {\n" + " GOOGLE_CHECK_NE(&from, this);\n", + "classname", classname_); + printer->Indent(); + + // Merge Repeated fields. These fields do not require a + // check as we can simply iterate over them. + for (int i = 0; i < descriptor_->field_count(); ++i) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + field_generators_.get(field).GenerateMergingCode(printer); + } + } + + // Merge Optional and Required fields (after a _has_bit check). + int last_index = -1; + + for (int i = 0; i < descriptor_->field_count(); ++i) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + map vars; + vars["index"] = SimpleItoa(field->index()); + + // See above in GenerateClear for an explanation of this. + if (i / 8 != last_index / 8 || last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print(vars, + "if (from._has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n"); + printer->Indent(); + } + + last_index = i; + + printer->Print(vars, + "if (from._has_bit($index$)) {\n"); + printer->Indent(); + + field_generators_.get(field).GenerateMergingCode(printer); + + printer->Outdent(); + printer->Print("}\n"); + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print("_extensions_.MergeFrom(from._extensions_);\n"); + } + + printer->Print( + "mutable_unknown_fields()->MergeFrom(from.unknown_fields());\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateCopyFrom(io::Printer* printer) { + // Generate the generalized CopyFrom (aka that which takes in the Message + // base class as a parameter). + printer->Print( + "void $classname$::CopyFrom(const ::google::protobuf::Message& from) {\n", + "classname", classname_); + printer->Indent(); + + printer->Print( + "if (&from == this) return;\n" + "Clear();\n" + "MergeFrom(from);\n"); + + printer->Outdent(); + printer->Print("}\n\n"); + + // Generate the class-specific CopyFrom. + printer->Print( + "void $classname$::CopyFrom(const $classname$& from) {\n", + "classname", classname_); + printer->Indent(); + + printer->Print( + "if (&from == this) return;\n" + "Clear();\n" + "MergeFrom(from);\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) { + if (descriptor_->options().message_set_wire_format()) { + // For message_set_wire_format, we don't generate a parser, for two + // reasons: + // - WireFormat already needs to special-case this, and we'd like to + // avoid having multiple implementations of MessageSet wire format + // lying around the code base. + // - All fields are extensions, and extension parsing falls back to + // reflection anyway, so it wouldn't be any faster. + printer->Print( + "bool $classname$::MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input) {\n" + " return ::google::protobuf::internal::WireFormat::ParseAndMergePartial(\n" + " input, this);\n" + "}\n", + "classname", classname_); + return; + } + + printer->Print( + "bool $classname$::MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input) {\n" + "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n" + " ::google::protobuf::uint32 tag;\n" + " while ((tag = input->ReadTag()) != 0) {\n", + "classname", classname_); + + printer->Indent(); + printer->Indent(); + + if (descriptor_->field_count() > 0) { + // We don't even want to print the switch() if we have no fields because + // MSVC dislikes switch() statements that contain only a default value. + + // Note: If we just switched on the tag rather than the field number, we + // could avoid the need for the if() to check the wire type at the beginning + // of each case. However, this is actually a bit slower in practice as it + // creates a jump table that is 8x larger and sparser, and meanwhile the + // if()s are highly predictable. + printer->Print( + "switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) {\n"); + + printer->Indent(); + + scoped_array ordered_fields( + SortFieldsByNumber(descriptor_)); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = ordered_fields[i]; + + PrintFieldComment(printer, field); + + printer->Print( + "case $number$: {\n" + " if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) !=\n" + " ::google::protobuf::internal::WireFormat::WIRETYPE_$wiretype$) {\n" + " goto handle_uninterpreted;\n" + " }\n", + "number", SimpleItoa(field->number()), + "wiretype", kWireTypeNames[ + WireFormat::WireTypeForFieldType(field->type())]); + + if (i > 0 || field->is_repeated()) { + printer->Print( + " parse_$name$:\n", + "name", field->name()); + } + + printer->Indent(); + + field_generators_.get(field).GenerateMergeFromCodedStream(printer); + + // switch() is slow since it can't be predicted well. Insert some if()s + // here that attempt to predict the next tag. + if (field->is_repeated()) { + // Expect repeats of this field. + printer->Print( + "if (input->ExpectTag($tag$)) goto parse_$name$;\n", + "tag", SimpleItoa(WireFormat::MakeTag(field)), + "name", field->name()); + } + + if (i + 1 < descriptor_->field_count()) { + // Expect the next field in order. + const FieldDescriptor* next_field = ordered_fields[i + 1]; + printer->Print( + "if (input->ExpectTag($next_tag$)) goto parse_$next_name$;\n", + "next_tag", SimpleItoa(WireFormat::MakeTag(next_field)), + "next_name", next_field->name()); + } else { + // Expect EOF. + // TODO(kenton): Expect group end-tag? + printer->Print( + "if (input->ExpectAtEnd()) return true;\n"); + } + + printer->Print( + "break;\n"); + + printer->Outdent(); + printer->Print("}\n\n"); + } + + printer->Print( + "default: {\n" + "handle_uninterpreted:\n"); + printer->Indent(); + } + + // Is this an end-group tag? If so, this must be the end of the message. + printer->Print( + "if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) ==\n" + " ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) {\n" + " return true;\n" + "}\n"); + + // Handle extension ranges. + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if ("); + for (int i = 0; i < descriptor_->extension_range_count(); i++) { + const Descriptor::ExtensionRange* range = + descriptor_->extension_range(i); + if (i > 0) printer->Print(" &&\n "); + + uint32 start_tag = WireFormat::MakeTag( + range->start, static_cast(0)); + uint32 end_tag = WireFormat::MakeTag( + range->end, static_cast(0)); + + if (range->end > FieldDescriptor::kMaxNumber) { + printer->Print( + "($start$u <= tag)", + "start", SimpleItoa(start_tag)); + } else { + printer->Print( + "($start$u <= tag && tag < $end$u)", + "start", SimpleItoa(start_tag), + "end", SimpleItoa(end_tag)); + } + } + printer->Print(") {\n" + " DO_(_extensions_.ParseField(tag, input, this));\n" + " continue;\n" + "}\n"); + } + + // We really don't recognize this tag. Skip it. + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SkipField(\n" + " input, tag, mutable_unknown_fields()));\n"); + + if (descriptor_->field_count() > 0) { + printer->Print("break;\n"); + printer->Outdent(); + printer->Print("}\n"); // default: + printer->Outdent(); + printer->Print("}\n"); // switch + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // while + " return true;\n" + "#undef DO_\n" + "}\n"); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + PrintFieldComment(printer, field); + + if (field->is_repeated()) { + printer->Print( + "for (int i = 0; i < $name$_.size(); i++) {\n", + "name", FieldName(field)); + } else { + printer->Print( + "if (_has_bit($index$)) {\n", + "index", SimpleItoa(field->index())); + } + + printer->Indent(); + + field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +void MessageGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + map vars; + vars["start"] = SimpleItoa(range->start); + vars["end"] = SimpleItoa(range->end); + printer->Print(vars, + "// Extension range [$start$, $end$)\n" + "DO_(_extensions_.SerializeWithCachedSizes(\n" + " $start$, $end$, *this, output));\n\n"); +} + +void MessageGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) { + printer->Print( + "bool $classname$::SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const {\n" + "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n", + "classname", classname_); + printer->Indent(); + + scoped_array ordered_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeSorter()); + + // Merge the fields and the extension ranges, both sorted by field number. + int i, j; + for (i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, ordered_fields[i++]); + } else if (ordered_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, ordered_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + printer->Print("if (!unknown_fields().empty()) {\n"); + printer->Indent(); + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" + " unknown_fields(), output));\n"); + } else { + printer->Print( + "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" + " unknown_fields(), output));\n"); + } + printer->Outdent(); + printer->Print( + "}\n" + "return true;\n"); + + printer->Outdent(); + printer->Print( + "#undef DO_\n" + "}\n"); +} + +void MessageGenerator:: +GenerateByteSize(io::Printer* printer) { + printer->Print( + "int $classname$::ByteSize() const {\n", + "classname", classname_); + printer->Indent(); + printer->Print( + "int total_size = 0;\n" + "\n"); + + int last_index = -1; + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated()) { + // See above in GenerateClear for an explanation of this. + // TODO(kenton): Share code? Unclear how to do so without + // over-engineering. + if ((i / 8) != (last_index / 8) || + last_index < 0) { + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + printer->Print( + "if (_has_bits_[$index$ / 32] & (0xffu << ($index$ % 32))) {\n", + "index", SimpleItoa(field->index())); + printer->Indent(); + } + last_index = i; + + PrintFieldComment(printer, field); + + printer->Print( + "if (has_$name$()) {\n", + "name", FieldName(field)); + printer->Indent(); + + field_generators_.get(field).GenerateByteSize(printer); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + } + } + + if (last_index >= 0) { + printer->Outdent(); + printer->Print("}\n"); + } + + // Repeated fields don't use _has_bits_ so we count them in a separate + // pass. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_repeated()) { + PrintFieldComment(printer, field); + field_generators_.get(field).GenerateByteSize(printer); + printer->Print("\n"); + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "total_size += _extensions_.ByteSize(*this);\n" + "\n"); + } + + printer->Print("if (!unknown_fields().empty()) {\n"); + printer->Indent(); + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownMessageSetItemsSize(\n" + " unknown_fields());\n"); + } else { + printer->Print( + "total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" + " unknown_fields());\n"); + } + printer->Outdent(); + printer->Print("}\n"); + + // We update _cached_size_ even though this is a const method. In theory, + // this is not thread-compatible, because concurrent writes have undefined + // results. In practice, since any concurrent writes will be writing the + // exact same value, it works on all common processors. In a future version + // of C++, _cached_size_ should be made into an atomic. + printer->Print( + "_cached_size_ = total_size;\n" + "return total_size;\n"); + + printer->Outdent(); + printer->Print("}\n"); +} + +void MessageGenerator:: +GenerateIsInitialized(io::Printer* printer) { + printer->Print( + "bool $classname$::IsInitialized() const {\n", + "classname", classname_); + printer->Indent(); + + // Check that all required fields in this message are set. We can do this + // most efficiently by checking 32 "has bits" at a time. + int has_bits_array_size = (descriptor_->field_count() + 31) / 32; + for (int i = 0; i < has_bits_array_size; i++) { + uint32 mask = 0; + for (int bit = 0; bit < 32; bit++) { + int index = i * 32 + bit; + if (index >= descriptor_->field_count()) break; + const FieldDescriptor* field = descriptor_->field(index); + + if (field->is_required()) { + mask |= 1 << bit; + } + } + + if (mask != 0) { + char buffer[kFastToBufferSize]; + printer->Print( + "if ((_has_bits_[$i$] & 0x$mask$) != 0x$mask$) return false;\n", + "i", SimpleItoa(i), + "mask", FastHex32ToBuffer(mask, buffer)); + } + } + + // Now check that all embedded messages are initialized. + printer->Print("\n"); + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + if (field->is_repeated()) { + printer->Print( + "for (int i = 0; i < $name$_size(); i++) {\n" + " if (!this->$name$(i).IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } else { + printer->Print( + "if (has_$name$()) {\n" + " if (!this->$name$().IsInitialized()) return false;\n" + "}\n", + "name", FieldName(field)); + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "\n" + "if (!_extensions_.IsInitialized()) return false;"); + } + + printer->Outdent(); + printer->Print( + " return true;\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h new file mode 100644 index 00000000..904e0487 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -0,0 +1,125 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class EnumGenerator; // enum.h +class ExtensionGenerator; // extension.h + +class MessageGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit MessageGenerator(const Descriptor* descriptor, + const string& dllexport_decl); + ~MessageGenerator(); + + // Header stuff. + + // Generate foward declarations for this class and all its nested types. + void GenerateForwardDeclaration(io::Printer* printer); + + // Generate definitions of all nested enums (must come before class + // definitions because those classes use the enums definitions). + void GenerateEnumDefinitions(io::Printer* printer); + + // Generate definitions for this class and all its nested types. + void GenerateClassDefinition(io::Printer* printer); + + // Generate definitions of inline methods (placed at the end of the header + // file). + void GenerateInlineMethods(io::Printer* printer); + + // Source file stuff. + + // Generate code which declares all the global descriptor pointers which + // will be initialized by the methods below. + void GenerateDescriptorDeclarations(io::Printer* printer); + + // Generate code that initializes the global variable storing the message's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate all non-inline methods for this class. + void GenerateClassMethods(io::Printer* printer); + + private: + // Generate declarations and definitions of accessors for fields. + void GenerateFieldAccessorDeclarations(io::Printer* printer); + void GenerateFieldAccessorDefinitions(io::Printer* printer); + + // Generate the field offsets array. + void GenerateOffsets(io::Printer* printer); + + // Generate constructors and destructor. + void GenerateStructors(io::Printer* printer); + + // Generate the member initializer list for the constructors. The member + // initializer list is shared between the default constructor and the copy + // constructor. + void GenerateInitializerList(io::Printer* printer); + + // Generate standard Message methods. + void GenerateClear(io::Printer* printer); + void GenerateMergeFromCodedStream(io::Printer* printer); + void GenerateSerializeWithCachedSizes(io::Printer* printer); + void GenerateByteSize(io::Printer* printer); + void GenerateMergeFrom(io::Printer* printer); + void GenerateCopyFrom(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + // Helpers for GenerateSerializeWithCachedSizes(). + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + const Descriptor* descriptor_; + string classname_; + string dllexport_decl_; + FieldGeneratorMap field_generators_; + scoped_array > nested_generators_; + scoped_array > enum_generators_; + scoped_array > extension_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc new file mode 100644 index 00000000..501ca569 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -0,0 +1,229 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = ClassName(descriptor->message_type(), true); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "$type$* $name$_;\n"); +} + +void MessageFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const $type$& $name$() const;\n" + "inline $type$* mutable_$name$();\n"); +} + +void MessageFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const $type$& $classname$::$name$() const {\n" + " return $name$_ != NULL ? *$name$_ : *default_instance_.$name$_;\n" + "}\n" + "inline $type$* $classname$::mutable_$name$() {\n" + " _set_bit($index$);\n" + " if ($name$_ == NULL) $name$_ = new $type$;\n" + " return $name$_;\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ != NULL) $name$_->$type$::Clear();\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "mutable_$name$()->$type$::MergeFrom(from.$name$());\n"); +} + +void MessageFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_(NULL)"); +} + +void MessageFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual(\n" + " input, mutable_$name$()));\n"); + } else { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadGroupNoVirtual(" + "$number$, input, mutable_$name$()));\n"); + } +} + +void MessageFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(), output));\n"); +} + +void MessageFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$SizeNoVirtual(\n" + " this->$name$());\n"); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedPtrField< $type$ > $name$_;\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< $type$ >& $name$() const;\n" + "inline ::google::protobuf::RepeatedPtrField< $type$ >* mutable_$name$();\n" + "inline const $type$& $name$(int index) const;\n" + "inline $type$* mutable_$name$(int index);\n" + "inline $type$* add_$name$();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< $type$ >&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedPtrField< $type$ >*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline const $type$& $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline $type$* $classname$::mutable_$name$(int index) {\n" + " return $name$_.Mutable(index);\n" + "}\n" + "inline $type$* $classname$::add_$name$() {\n" + " return $name$_.Add();\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedMessageFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual(\n" + " input, add_$name$()));\n"); + } else { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::ReadGroupNoVirtual(" + "$number$, input, add_$name$()));\n"); + } +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size +=\n" + " ::google::protobuf::internal::WireFormat::$declared_type$SizeNoVirtual(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h new file mode 100644 index 00000000..a2be6b65 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc new file mode 100644 index 00000000..312ed031 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -0,0 +1,294 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// 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 WireFormat::kFixed32Size; + case FieldDescriptor::TYPE_FIXED64 : return WireFormat::kFixed64Size; + case FieldDescriptor::TYPE_SFIXED32: return WireFormat::kSFixed32Size; + case FieldDescriptor::TYPE_SFIXED64: return WireFormat::kSFixed64Size; + case FieldDescriptor::TYPE_FLOAT : return WireFormat::kFloatSize; + case FieldDescriptor::TYPE_DOUBLE : return WireFormat::kDoubleSize; + + case FieldDescriptor::TYPE_BOOL : return WireFormat::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; +} + +string DefaultValue(const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(field->default_value_uint32()) + "u"; + case FieldDescriptor::CPPTYPE_INT64: + return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")"; + case FieldDescriptor::CPPTYPE_UINT64: + return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()); + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()); + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Shouldn't get here."; + return ""; + } + // Can't actually get here; make compiler happy. (We could add a default + // case above but then we wouldn't get the nice compiler warning when a + // new type is added.) + return ""; +} + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["type"] = PrimitiveTypeName(descriptor->cpp_type()); + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); + + int fixed_size = FixedSize(descriptor->type()); + if (fixed_size != -1) { + (*variables)["fixed_size"] = SimpleItoa(fixed_size); + } +} + +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +void PrimitiveFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, "$type$ $name$_;\n"); +} + +void PrimitiveFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $name$() const;\n" + "inline void set_$name$($type$ value);\n"); +} + +void PrimitiveFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline $type$ $classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline void $classname$::set_$name$($type$ value) {\n" + " _set_bit($index$);\n" + " $name$_ = value;\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void PrimitiveFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, ",\n$name$_($default$)"); +} + +void PrimitiveFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(\n" + " input, &$name$_));\n" + "_set_bit($index$);\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output));\n"); +} + +void PrimitiveFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + int fixed_size = FixedSize(descriptor_->type()); + if (fixed_size == -1) { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$());\n"); + } else { + printer->Print(variables_, + "total_size += $tag_size$ + $fixed_size$;\n"); + } +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedField< $type$ > $name$_;\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField< $type$ >& $name$() const;\n" + "inline ::google::protobuf::RepeatedField< $type$ >* mutable_$name$();\n" + "inline $type$ $name$(int index) const;\n" + "inline void set_$name$(int index, $type$ value);\n" + "inline void add_$name$($type$ value);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedField< $type$ >&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedField< $type$ >*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline $type$ $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline void $classname$::set_$name$(int index, $type$ value) {\n" + " $name$_.Set(index, value);\n" + "}\n" + "inline void $classname$::add_$name$($type$ value) {\n" + " $name$_.Add(value);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "$type$ value;\n" + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(input, &value));\n" + "add_$name$(value);\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + int fixed_size = FixedSize(descriptor_->type()); + if (fixed_size == -1) { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$(i));\n" + "}\n"); + } else { + printer->Print(variables_, + "total_size += ($tag_size$ + $fixed_size$) * $name$_size();\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h new file mode 100644 index 00000000..832b2411 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_service.cc b/src/google/protobuf/compiler/cpp/cpp_service.cc new file mode 100644 index 00000000..124f3b43 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_service.cc @@ -0,0 +1,318 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor, + const string& dllexport_decl) + : descriptor_(descriptor) { + vars_["classname"] = descriptor_->name(); + vars_["full_name"] = descriptor_->full_name(); + if (dllexport_decl.empty()) { + vars_["dllexport"] = ""; + } else { + vars_["dllexport"] = dllexport_decl + " "; + } +} + +ServiceGenerator::~ServiceGenerator() {} + +void ServiceGenerator::GenerateDeclarations(io::Printer* printer) { + // Forward-declare the stub type. + printer->Print(vars_, + "class $classname$_Stub;\n" + "\n"); + + GenerateInterface(printer); + GenerateStubDefinition(printer); +} + +void ServiceGenerator::GenerateInterface(io::Printer* printer) { + printer->Print(vars_, + "class $dllexport$$classname$ : public ::google::protobuf::Service {\n" + " protected:\n" + " // This class should be treated as an abstract interface.\n" + " inline $classname$() {};\n" + " public:\n" + " virtual ~$classname$();\n"); + printer->Indent(); + + printer->Print(vars_, + "\n" + "typedef $classname$_Stub Stub;\n" + "\n" + "static const ::google::protobuf::ServiceDescriptor* descriptor();\n" + "\n"); + + GenerateMethodSignatures(VIRTUAL, printer); + + printer->Print( + "\n" + "// implements Service ----------------------------------------------\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* GetDescriptor();\n" + "void CallMethod(const ::google::protobuf::MethodDescriptor* method,\n" + " ::google::protobuf::RpcController* controller,\n" + " const ::google::protobuf::Message* request,\n" + " ::google::protobuf::Message* response,\n" + " ::google::protobuf::Closure* done);\n" + "const ::google::protobuf::Message& GetRequestPrototype(\n" + " const ::google::protobuf::MethodDescriptor* method) const;\n" + "const ::google::protobuf::Message& GetResponsePrototype(\n" + " const ::google::protobuf::MethodDescriptor* method) const;\n"); + + printer->Outdent(); + printer->Print(vars_, + "\n" + " private:\n" + " GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$);\n" + "};\n" + "\n"); +} + +void ServiceGenerator::GenerateStubDefinition(io::Printer* printer) { + printer->Print(vars_, + "class $dllexport$$classname$_Stub : public $classname$ {\n" + " public:\n"); + + printer->Indent(); + + printer->Print(vars_, + "$classname$_Stub(::google::protobuf::RpcChannel* channel);\n" + "$classname$_Stub(::google::protobuf::RpcChannel* channel,\n" + " ::google::protobuf::Service::ChannelOwnership ownership);\n" + "~$classname$_Stub();\n" + "\n" + "inline ::google::protobuf::RpcChannel* channel() { return channel_; }\n" + "\n" + "// implements $classname$ ------------------------------------------\n" + "\n"); + + GenerateMethodSignatures(NON_VIRTUAL, printer); + + printer->Outdent(); + printer->Print(vars_, + " private:\n" + " ::google::protobuf::RpcChannel* channel_;\n" + " bool owns_channel_;\n" + " GOOGLE_DISALLOW_EVIL_CONSTRUCTORS($classname$_Stub);\n" + "};\n" + "\n"); +} + +void ServiceGenerator::GenerateMethodSignatures( + VirtualOrNon virtual_or_non, io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["name"] = method->name(); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + sub_vars["virtual"] = virtual_or_non == VIRTUAL ? "virtual " : ""; + + printer->Print(sub_vars, + "$virtual$void $name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done);\n"); + } +} + +// =================================================================== + +void ServiceGenerator::GenerateDescriptorInitializer( + io::Printer* printer, int index) { + map vars; + vars["classname"] = descriptor_->name(); + vars["index"] = SimpleItoa(index); + + printer->Print(vars, + "$classname$_descriptor_ = file->service($index$);\n"); +} + +// =================================================================== + +void ServiceGenerator::GenerateImplementation(io::Printer* printer) { + printer->Print(vars_, + "$classname$::~$classname$() {}\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* $classname$::descriptor() {\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n" + "const ::google::protobuf::ServiceDescriptor* $classname$::GetDescriptor() {\n" + " return $classname$_descriptor_;\n" + "}\n" + "\n"); + + // Generate methods of the interface. + GenerateNotImplementedMethods(printer); + GenerateCallMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + + // Generate stub implementation. + printer->Print(vars_, + "$classname$_Stub::$classname$_Stub(::google::protobuf::RpcChannel* channel)\n" + " : channel_(channel), owns_channel_(false) {}\n" + "$classname$_Stub::$classname$_Stub(\n" + " ::google::protobuf::RpcChannel* channel,\n" + " ::google::protobuf::Service::ChannelOwnership ownership)\n" + " : channel_(channel),\n" + " owns_channel_(ownership == ::google::protobuf::Service::STUB_OWNS_CHANNEL) {}\n" + "$classname$_Stub::~$classname$_Stub() {\n" + " if (owns_channel_) delete channel_;\n" + "}\n" + "\n"); + + GenerateStubMethods(printer); +} + +void ServiceGenerator::GenerateNotImplementedMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["classname"] = descriptor_->name(); + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + printer->Print(sub_vars, + "void $classname$::$name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done) {\n" + " controller->SetFailed(\"Method $name$() not implemented.\");\n" + " done->Run();\n" + "}\n" + "\n"); + } +} + +void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { + printer->Print(vars_, + "void $classname$::CallMethod(const ::google::protobuf::MethodDescriptor* method,\n" + " ::google::protobuf::RpcController* controller,\n" + " const ::google::protobuf::Message* request,\n" + " ::google::protobuf::Message* response,\n" + " ::google::protobuf::Closure* done) {\n" + " GOOGLE_DCHECK_EQ(method->service(), $classname$_descriptor_);\n" + " switch(method->index()) {\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + // Note: ::google::protobuf::down_cast does not work here because it only works on pointers, + // not references. + printer->Print(sub_vars, + " case $index$:\n" + " $name$(controller,\n" + " ::google::protobuf::down_cast(request),\n" + " ::google::protobuf::down_cast< $output_type$*>(response),\n" + " done);\n" + " break;\n"); + } + + printer->Print(vars_, + " default:\n" + " GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n" + " break;\n" + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, + io::Printer* printer) { + if (which == REQUEST) { + printer->Print(vars_, + "const ::google::protobuf::Message& $classname$::GetRequestPrototype(\n"); + } else { + printer->Print(vars_, + "const ::google::protobuf::Message& $classname$::GetResponsePrototype(\n"); + } + + printer->Print(vars_, + " const ::google::protobuf::MethodDescriptor* method) const {\n" + " GOOGLE_DCHECK_EQ(method->service(), $classname$_descriptor_);\n" + " switch(method->index()) {\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + const Descriptor* type = + (which == REQUEST) ? method->input_type() : method->output_type(); + + map sub_vars; + sub_vars["index"] = SimpleItoa(i); + sub_vars["type"] = ClassName(type, true); + + printer->Print(sub_vars, + " case $index$:\n" + " return $type$::default_instance();\n"); + } + + printer->Print(vars_, + " default:\n" + " GOOGLE_LOG(FATAL) << \"Bad method index; this should never happen.\";\n" + " return *reinterpret_cast< ::google::protobuf::Message*>(NULL);\n" + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateStubMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map sub_vars; + sub_vars["classname"] = descriptor_->name(); + sub_vars["name"] = method->name(); + sub_vars["index"] = SimpleItoa(i); + sub_vars["input_type"] = ClassName(method->input_type(), true); + sub_vars["output_type"] = ClassName(method->output_type(), true); + + printer->Print(sub_vars, + "void $classname$_Stub::$name$(::google::protobuf::RpcController* controller,\n" + " const $input_type$* request,\n" + " $output_type$* response,\n" + " ::google::protobuf::Closure* done) {\n" + " channel_->CallMethod($classname$_descriptor_->method($index$),\n" + " controller, request, response, done);\n" + "}\n"); + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_service.h b/src/google/protobuf/compiler/cpp/cpp_service.h new file mode 100644 index 00000000..bccb64e4 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_service.h @@ -0,0 +1,104 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace cpp { + +class ServiceGenerator { + public: + // See generator.cc for the meaning of dllexport_decl. + explicit ServiceGenerator(const ServiceDescriptor* descriptor, + const string& dllexport_decl); + ~ServiceGenerator(); + + // Header stuff. + + // Generate the class definitions for the service's interface and the + // stub implementation. + void GenerateDeclarations(io::Printer* printer); + + // Source file stuff. + + // Generate code that initializes the global variable storing the service's + // descriptor. + void GenerateDescriptorInitializer(io::Printer* printer, int index); + + // Generate implementations of everything declared by GenerateDeclarations(). + void GenerateImplementation(io::Printer* printer); + + private: + enum RequestOrResponse { REQUEST, RESPONSE }; + enum VirtualOrNon { VIRTUAL, NON_VIRTUAL }; + + // Header stuff. + + // Generate the service abstract interface. + void GenerateInterface(io::Printer* printer); + + // Generate the stub class definition. + void GenerateStubDefinition(io::Printer* printer); + + // Prints signatures for all methods in the + void GenerateMethodSignatures(VirtualOrNon virtual_or_non, + io::Printer* printer); + + // Source file stuff. + + // Generate the default implementations of the service methods, which + // produce a "not implemented" error. + void GenerateNotImplementedMethods(io::Printer* printer); + + // Generate the CallMethod() method of the service. + void GenerateCallMethod(io::Printer* printer); + + // Generate the Get{Request,Response}Prototype() methods. + void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); + + // Generate the stub's implementations of the service methods. + void GenerateStubMethods(io::Printer* printer); + + const ServiceDescriptor* descriptor_; + map vars_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_SERVICE_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc new file mode 100644 index 00000000..8590df7e --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -0,0 +1,370 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +using internal::WireFormat; + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetStringVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = FieldName(descriptor); + (*variables)["default"] = + "\"" + CEscape(descriptor->default_value_string()) + "\""; + (*variables)["index"] = SimpleItoa(descriptor->index()); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["classname"] = ClassName(FieldScope(descriptor), false); + (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); + (*variables)["tag_size"] = SimpleItoa( + WireFormat::TagSize(descriptor->number(), descriptor->type())); +} + +} // namespace + +// =================================================================== + +StringFieldGenerator:: +StringFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetStringVariables(descriptor, &variables_); +} + +StringFieldGenerator::~StringFieldGenerator() {} + +void StringFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::std::string* $name$_;\n" + "static const ::std::string _default_$name$_;\n"); +} + +void StringFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + // If we're using StringFieldGenerator for a field with a ctype, it's + // because that ctype isn't actually implemented. In particular, this is + // true of ctype=CORD and ctype=STRING_PIECE in the open source release. + // We aren't releasing Cord because it has too many Google-specific + // dependencies and we aren't releasing StringPiece because it's hardly + // useful outside of Google and because it would get confusing to have + // multiple instances of the StringPiece class in different libraries (PCRE + // already includes it for their C++ bindings, which came from Google). + // + // In any case, we make all the accessors private while still actually + // using a string to represent the field internally. This way, we can + // guarantee that if we do ever implement the ctype, it won't break any + // existing users who might be -- for whatever reason -- already using .proto + // files that applied the ctype. The field can still be accessed via the + // reflection interface since the reflection interface is independent of + // the string's underlying representation. + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print( + " private:\n" + " // Hidden due to unknown ctype option.\n"); + printer->Indent(); + } + + printer->Print(variables_, + "inline const ::std::string& $name$() const;\n" + "inline void set_$name$(const ::std::string& value);\n" + "inline void set_$name$(const char* value);\n"); + if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { + printer->Print(variables_, + "inline void set_$name$(const void* value, size_t size);\n"); + } + + printer->Print(variables_, + "inline ::std::string* mutable_$name$();\n"); + + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print(" public:\n"); + printer->Indent(); + } +} + +void StringFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::std::string& $classname$::$name$() const {\n" + " return *$name$_;\n" + "}\n" + "inline void $classname$::set_$name$(const ::std::string& value) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(value);\n" + "}\n" + "inline void $classname$::set_$name$(const char* value) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(value);\n" + "}\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { + printer->Print(variables_, + "inline void $classname$::set_$name$(const void* value, size_t size) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(reinterpret_cast(value), size);\n" + "}\n"); + } + + printer->Print(variables_, + "inline ::std::string* $classname$::mutable_$name$() {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n"); + if (descriptor_->has_default_value()) { + printer->Print(variables_, + " $name$_ = new ::std::string(_default_$name$_);\n"); + } else { + printer->Print(variables_, + " $name$_ = new ::std::string;\n"); + } + printer->Print(variables_, + " }\n" + " return $name$_;\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateNonInlineAccessorDefinitions(io::Printer* printer) const { + if (descriptor_->has_default_value()) { + printer->Print(variables_, + "const ::std::string $classname$::_default_$name$_($default$);"); + } else { + printer->Print(variables_, + "const ::std::string $classname$::_default_$name$_;"); + } +} + +void StringFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + if (descriptor_->has_default_value()) { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " $name$_->assign(_default_$name$_);\n" + "}\n"); + } else { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " $name$_->clear();\n" + "}\n"); + } +} + +void StringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "set_$name$(from.$name$());\n"); +} + +void StringFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + printer->Print(variables_, + ",\n$name$_(const_cast< ::std::string*>(&_default_$name$_))"); +} + +void StringFieldGenerator:: +GenerateDestructorCode(io::Printer* printer) const { + printer->Print(variables_, + "if ($name$_ != &_default_$name$_) {\n" + " delete $name$_;\n" + "}\n"); +} + +void StringFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(" + "input, mutable_$name$()));\n"); +} + +void StringFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output));\n"); +} + +void StringFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ +\n" + " ::google::protobuf::internal::WireFormat::$declared_type$Size(this->$name$());\n"); +} + +// =================================================================== + +RepeatedStringFieldGenerator:: +RepeatedStringFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetStringVariables(descriptor, &variables_); +} + +RepeatedStringFieldGenerator::~RepeatedStringFieldGenerator() {} + +void RepeatedStringFieldGenerator:: +GeneratePrivateMembers(io::Printer* printer) const { + printer->Print(variables_, + "::google::protobuf::RepeatedPtrField< ::std::string> $name$_;\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateAccessorDeclarations(io::Printer* printer) const { + // See comment above about unknown ctypes. + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print( + " private:\n" + " // Hidden due to unknown ctype option.\n"); + printer->Indent(); + } + + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< ::std::string>& $name$() const;\n" + "inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_$name$();\n" + "inline const ::std::string& $name$(int index) const;\n" + "inline ::std::string* mutable_$name$(int index);\n" + "inline void set_$name$(int index, const ::std::string& value);\n" + "inline void set_$name$(int index, const char* value);\n" + "inline ::std::string* add_$name$();\n" + "inline void add_$name$(const ::std::string& value);\n" + "inline void add_$name$(const char* value);\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { + printer->Print(variables_, + "inline void set_$name$(int index, const void* value, size_t size);\n" + "inline void add_$name$(const void* value, size_t size);\n"); + } + + if (descriptor_->options().has_ctype()) { + printer->Outdent(); + printer->Print(" public:\n"); + printer->Indent(); + } +} + +void RepeatedStringFieldGenerator:: +GenerateInlineAccessorDefinitions(io::Printer* printer) const { + printer->Print(variables_, + "inline const ::google::protobuf::RepeatedPtrField< ::std::string>&\n" + "$classname$::$name$() const {\n" + " return $name$_;\n" + "}\n" + "inline ::google::protobuf::RepeatedPtrField< ::std::string>*\n" + "$classname$::mutable_$name$() {\n" + " return &$name$_;\n" + "}\n" + "inline const ::std::string& $classname$::$name$(int index) const {\n" + " return $name$_.Get(index);\n" + "}\n" + "inline ::std::string* $classname$::mutable_$name$(int index) {\n" + " return $name$_.Mutable(index);\n" + "}\n" + "inline void $classname$::set_$name$(int index, const ::std::string& value) {\n" + " $name$_.Mutable(index)->assign(value);\n" + "}\n" + "inline void $classname$::set_$name$(int index, const char* value) {\n" + " $name$_.Mutable(index)->assign(value);\n" + "}\n" + "inline ::std::string* $classname$::add_$name$() {\n" + " return $name$_.Add();\n" + "}\n" + "inline void $classname$::add_$name$(const ::std::string& value) {\n" + " $name$_.Add()->assign(value);\n" + "}\n" + "inline void $classname$::add_$name$(const char* value) {\n" + " $name$_.Add()->assign(value);\n" + "}\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { + printer->Print(variables_, + "inline void " + "$classname$::set_$name$(int index, const void* value, size_t size) {\n" + " $name$_.Mutable(index)->assign(\n" + " reinterpret_cast(value), size);\n" + "}\n" + "inline void $classname$::add_$name$(const void* value, size_t size) {\n" + " $name$_.Add()->assign(reinterpret_cast(value), size);\n" + "}\n"); + } +} + +void RepeatedStringFieldGenerator:: +GenerateClearingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.Clear();\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_.MergeFrom(from.$name$_);\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateInitializer(io::Printer* printer) const { + // Not needed for repeated fields. +} + +void RepeatedStringFieldGenerator:: +GenerateMergeFromCodedStream(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Read$declared_type$(\n" + " input, add_$name$()));\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateSerializeWithCachedSizes(io::Printer* printer) const { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output));\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateByteSize(io::Printer* printer) const { + printer->Print(variables_, + "total_size += $tag_size$ * $name$_size();\n" + "for (int i = 0; i < $name$_size(); i++) {\n" + " total_size += ::google::protobuf::internal::WireFormat::$declared_type$Size(\n" + " this->$name$(i));\n" + "}\n"); +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h new file mode 100644 index 00000000..44ffd18c --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -0,0 +1,86 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +class StringFieldGenerator : public FieldGenerator { + public: + explicit StringFieldGenerator(const FieldDescriptor* descriptor); + ~StringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateNonInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateDestructorCode(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); +}; + +class RepeatedStringFieldGenerator : public FieldGenerator { + public: + explicit RepeatedStringFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedStringFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GeneratePrivateMembers(io::Printer* printer) const; + void GenerateAccessorDeclarations(io::Printer* printer) const; + void GenerateInlineAccessorDefinitions(io::Printer* printer) const; + void GenerateClearingCode(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateInitializer(io::Printer* printer) const; + void GenerateMergeFromCodedStream(io::Printer* printer) const; + void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateByteSize(io::Printer* printer) const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedStringFieldGenerator); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_STRING_FIELD_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto new file mode 100644 index 00000000..ee0499bf --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -0,0 +1,87 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file tests that various identifiers work as field and type names even +// though the same identifiers are used internally by the C++ code generator. + + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +package protobuf_unittest; + +// Test that fields can have names like "input" and "i" which are also used +// internally by the code generator for local variables. +message TestConflictingSymbolNames { + message BuildDescriptors {} + message TypeTraits {} + + optional int32 input = 1; + optional int32 output = 2; + optional string length = 3; + repeated int32 i = 4; + repeated string new_element = 5 [ctype=STRING_PIECE]; + optional int32 total_size = 6; + optional int32 tag = 7; + + optional int32 source = 8; + optional int32 value = 9; + optional int32 file = 10; + optional int32 from = 11; + optional int32 handle_uninterpreted = 12; + repeated int32 index = 13; + optional int32 controller = 14; + optional int32 already_here = 15; + + optional uint32 uint32 = 16; + optional uint64 uint64 = 17; + optional string string = 18; + optional int32 memset = 19; + optional int32 int32 = 20; + optional int64 int64 = 21; + + optional uint32 cached_size = 22; + optional uint32 extensions = 23; + optional uint32 bit = 24; + optional uint32 bits = 25; + optional uint32 offsets = 26; + optional uint32 reflection = 27; + + message Cord {} + optional string some_cord = 28 [ctype=CORD]; + + message StringPiece {} + optional string some_string_piece = 29 [ctype=STRING_PIECE]; + + // Some keywords. + optional uint32 int = 30; + optional uint32 friend = 31; + + // The generator used to #define a macro called "DO" inside the .cc file. + message DO {} + optional DO do = 32; + + extensions 1000 to max; +} + +message DummyMessage {} + +service TestConflictingMethodNames { + rpc Closure(DummyMessage) returns (DummyMessage); +} diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc new file mode 100644 index 00000000..010843cf --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -0,0 +1,848 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// To test the code generator, we actually use it to generate code for +// google/protobuf/unittest.proto, then test that. This means that we +// are actually testing the parser and other parts of the system at the same +// time, and that problems in the generator may show up as compile-time errors +// rather than unittest failures, which may be surprising. However, testing +// the output of the C++ generator directly would be very hard. We can't very +// well just check it against golden files since those files would have to be +// updated for any small change; such a test would be very brittle and probably +// not very helpful. What we really want to test is that the code compiles +// correctly and produces the interfaces we expect, which is why this test +// is written this way. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +// Test that generated code has proper descriptors: +// Parse a descriptor directly (using google::protobuf::compiler::Importer) and +// compare it to the one that was produced by generated code. +TEST(GeneratedDescriptorTest, IdenticalDescriptors) { + const FileDescriptor* generated_descriptor = + unittest::TestAllTypes::descriptor()->file(); + + // Set up the Importer. + MockErrorCollector error_collector; + DiskSourceTree source_tree; + source_tree.MapPath("", TestSourceDir()); + Importer importer(&source_tree, &error_collector); + + // Import (parse) unittest.proto. + const FileDescriptor* parsed_descriptor = + importer.Import("google/protobuf/unittest.proto"); + EXPECT_EQ("", error_collector.text_); + ASSERT_TRUE(parsed_descriptor != NULL); + + // Test that descriptors are generated correctly by converting them to + // FileDescriptorProtos and comparing. + FileDescriptorProto generated_decsriptor_proto, parsed_descriptor_proto; + generated_descriptor->CopyTo(&generated_decsriptor_proto); + parsed_descriptor->CopyTo(&parsed_descriptor_proto); + + EXPECT_EQ(parsed_descriptor_proto.DebugString(), + generated_decsriptor_proto.DebugString()); +} + +// =================================================================== + +TEST(GeneratedMessageTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllTypes message; + + TestUtil::ExpectClear(message); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(GeneratedMessageTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + TestUtil::ExpectAllFieldsSet(message); + + TestUtil::ModifyRepeatedFields(&message); + TestUtil::ExpectRepeatedFieldsModified(message); +} + +TEST(GeneratedMessageTest, MutableStringDefault) { + // mutable_foo() for a string should return a string initialized to its + // default value. + unittest::TestAllTypes message; + + EXPECT_EQ("hello", *message.mutable_default_string()); + + // Note that the first time we call mutable_foo(), we get a newly-allocated + // string, but if we clear it and call it again, we get the same object again. + // We should verify that it has its default value in both cases. + message.set_default_string("blah"); + message.Clear(); + + EXPECT_EQ("hello", *message.mutable_default_string()); +} + +TEST(GeneratedMessageTest, Clear) { + // Set every field to a unique value, clear the message, then check that + // it is cleared. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + message.Clear(); + TestUtil::ExpectClear(message); + + // Unlike with the defaults test, we do NOT expect that requesting embedded + // messages will return a pointer to the default instance. Instead, they + // should return the objects that were created when mutable_blah() was + // called. + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(GeneratedMessageTest, EmbeddedNullsInBytesCharStar) { + unittest::TestAllTypes message; + + const char* value = "\0lalala\0\0"; + message.set_optional_bytes(value, 9); + ASSERT_EQ(9, message.optional_bytes().size()); + EXPECT_EQ(0, memcmp(value, message.optional_bytes().data(), 9)); + + message.add_repeated_bytes(value, 9); + ASSERT_EQ(9, message.repeated_bytes(0).size()); + EXPECT_EQ(0, memcmp(value, message.repeated_bytes(0).data(), 9)); +} + +TEST(GeneratedMessageTest, ClearOneField) { + // Set every field to a unique value, then clear one value and insure that + // only that one value is cleared. + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + int64 original_value = message.optional_int64(); + + // Clear the field and make sure it shows up as cleared. + message.clear_optional_int64(); + EXPECT_FALSE(message.has_optional_int64()); + EXPECT_EQ(0, message.optional_int64()); + + // Other adjacent fields should not be cleared. + EXPECT_TRUE(message.has_optional_int32()); + EXPECT_TRUE(message.has_optional_uint32()); + + // Make sure if we set it again, then all fields are set. + message.set_optional_int64(original_value); + TestUtil::ExpectAllFieldsSet(message); +} + + +TEST(GeneratedMessageTest, CopyFrom) { + unittest::TestAllTypes message1, message2; + string data; + + TestUtil::SetAllFields(&message1); + message2.CopyFrom(message1); + TestUtil::ExpectAllFieldsSet(message2); + + // Copying from self should be a no-op. + message2.CopyFrom(message2); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, CopyConstructor) { + unittest::TestAllTypes message1; + TestUtil::SetAllFields(&message1); + + unittest::TestAllTypes message2(message1); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, CopyAssignmentOperator) { + unittest::TestAllTypes message1; + TestUtil::SetAllFields(&message1); + + unittest::TestAllTypes message2; + message2 = message1; + TestUtil::ExpectAllFieldsSet(message2); + + // Make sure that self-assignment does something sane. + message2 = message2; + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, UpcastCopyFrom) { + // Test the CopyFrom method that takes in the generic const Message& + // parameter. + unittest::TestAllTypes message1, message2; + + TestUtil::SetAllFields(&message1); + + const Message* source = implicit_cast(&message1); + message2.CopyFrom(*source); + + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, DynamicMessageCopyFrom) { + // Test copying from a DynamicMessage, which must fall back to using + // reflection. + unittest::TestAllTypes message2; + + // Construct a new version of the dynamic message via the factory. + DynamicMessageFactory factory; + scoped_ptr message1; + message1.reset(factory.GetPrototype( + unittest::TestAllTypes::descriptor())->New()); + + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + reflection_tester.SetAllFieldsViaReflection(message1.get()); + + message2.CopyFrom(*message1); + + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(GeneratedMessageTest, NonEmptyMergeFrom) { + // Test merging with a non-empty message. Code is a modified form + // of that found in google/protobuf/reflection_ops_unittest.cc. + unittest::TestAllTypes message1, message2; + + TestUtil::SetAllFields(&message1); + + // This field will test merging into an empty spot. + message2.set_optional_int32(message1.optional_int32()); + message1.clear_optional_int32(); + + // This tests overwriting. + message2.set_optional_string(message1.optional_string()); + message1.set_optional_string("something else"); + + // This tests concatenating. + message2.add_repeated_int32(message1.repeated_int32(1)); + int32 i = message1.repeated_int32(0); + message1.clear_repeated_int32(); + message1.add_repeated_int32(i); + + message1.MergeFrom(message2); + + TestUtil::ExpectAllFieldsSet(message1); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageTest, MergeFromSelf) { + unittest::TestAllTypes message; + EXPECT_DEATH(message.MergeFrom(message), "&from"); + EXPECT_DEATH(message.MergeFrom(implicit_cast(message)), + "&from"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageTest, Serialization) { + unittest::TestAllTypes message1, message2; + string data; + + TestUtil::SetAllFields(&message1); + message1.SerializeToString(&data); + EXPECT_TRUE(message2.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(message2); + +} + + +TEST(GeneratedMessageTest, Required) { + // Test that IsInitialized() returns false if required fields are missing. + unittest::TestRequired message; + + EXPECT_FALSE(message.IsInitialized()); + message.set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(GeneratedMessageTest, RequiredForeign) { + // Test that IsInitialized() returns false if required fields in nested + // messages are missing. + unittest::TestRequiredForeign message; + + EXPECT_TRUE(message.IsInitialized()); + + message.mutable_optional_message(); + EXPECT_FALSE(message.IsInitialized()); + + message.mutable_optional_message()->set_a(1); + message.mutable_optional_message()->set_b(2); + message.mutable_optional_message()->set_c(3); + EXPECT_TRUE(message.IsInitialized()); + + message.add_repeated_message(); + EXPECT_FALSE(message.IsInitialized()); + + message.mutable_repeated_message(0)->set_a(1); + message.mutable_repeated_message(0)->set_b(2); + message.mutable_repeated_message(0)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(GeneratedMessageTest, ForeignNested) { + // Test that TestAllTypes::NestedMessage can be embedded directly into + // another message. + unittest::TestForeignNested message; + + // If this compiles and runs without crashing, it must work. We have + // nothing more to test. + unittest::TestAllTypes::NestedMessage* nested = + message.mutable_foreign_nested(); + nested->set_bb(1); +} + +TEST(GeneratedMessageTest, ReallyLargeTagNumber) { + // Test that really large tag numbers don't break anything. + unittest::TestReallyLargeTagNumber message1, message2; + string data; + + // For the most part, if this compiles and runs then we're probably good. + // (The most likely cause for failure would be if something were attempting + // to allocate a lookup table of some sort using tag numbers as the index.) + // We'll try serializing just for fun. + message1.set_a(1234); + message1.set_bb(5678); + message1.SerializeToString(&data); + EXPECT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(1234, message2.a()); + EXPECT_EQ(5678, message2.bb()); +} + +TEST(GeneratedMessageTest, MutualRecursion) { + // Test that mutually-recursive message types work. + unittest::TestMutualRecursionA message; + unittest::TestMutualRecursionA* nested = message.mutable_bb()->mutable_a(); + unittest::TestMutualRecursionA* nested2 = nested->mutable_bb()->mutable_a(); + + // Again, if the above compiles and runs, that's all we really have to + // test, but just for run we'll check that the system didn't somehow come + // up with a pointer loop... + EXPECT_NE(&message, nested); + EXPECT_NE(&message, nested2); + EXPECT_NE(nested, nested2); +} + +TEST(GeneratedMessageTest, CamelCaseFieldNames) { + // This test is mainly checking that the following compiles, which verifies + // that the field names were coerced to lower-case. + // + // Protocol buffers standard style is to use lowercase-with-underscores for + // field names. Some old proto1 .protos unfortunately used camel-case field + // names. In proto1, these names were forced to lower-case. So, we do the + // same thing in proto2. + + unittest::TestCamelCaseFieldNames message; + + message.set_primitivefield(2); + message.set_stringfield("foo"); + message.set_enumfield(unittest::FOREIGN_FOO); + message.mutable_messagefield()->set_c(6); + + message.add_repeatedprimitivefield(8); + message.add_repeatedstringfield("qux"); + message.add_repeatedenumfield(unittest::FOREIGN_BAR); + message.add_repeatedmessagefield()->set_c(15); + + EXPECT_EQ(2, message.primitivefield()); + EXPECT_EQ("foo", message.stringfield()); + EXPECT_EQ(unittest::FOREIGN_FOO, message.enumfield()); + EXPECT_EQ(6, message.messagefield().c()); + + EXPECT_EQ(8, message.repeatedprimitivefield(0)); + EXPECT_EQ("qux", message.repeatedstringfield(0)); + EXPECT_EQ(unittest::FOREIGN_BAR, message.repeatedenumfield(0)); + EXPECT_EQ(15, message.repeatedmessagefield(0).c()); +} + +TEST(GeneratedMessageTest, TestConflictingSymbolNames) { + // test_bad_identifiers.proto successfully compiled, then it works. The + // following is just a token usage to insure that the code is, in fact, + // being compiled and linked. + + protobuf_unittest::TestConflictingSymbolNames message; + message.set_uint32(1); + EXPECT_EQ(3, message.ByteSize()); + + message.set_friend_(5); + EXPECT_EQ(5, message.friend_()); +} + +TEST(GeneratedMessageTest, TestOptimizedForSize) { + // We rely on the tests in reflection_ops_unittest and wire_format_unittest + // to really test that reflection-based methods work. Here we are mostly + // just making sure that TestOptimizedForSize actually builds and seems to + // function. + + protobuf_unittest::TestOptimizedForSize message, message2; + message.set_i(1); + message.mutable_msg()->set_c(2); + message2.CopyFrom(message); + EXPECT_EQ(1, message2.i()); + EXPECT_EQ(2, message2.msg().c()); +} + +TEST(GeneratedMessageTest, TestEmbedOptimizedForSize) { + // Verifies that something optimized for speed can contain something optimized + // for size. + + protobuf_unittest::TestEmbedOptimizedForSize message, message2; + message.mutable_optional_message()->set_i(1); + message.add_repeated_message()->mutable_msg()->set_c(2); + string data; + message.SerializeToString(&data); + ASSERT_TRUE(message2.ParseFromString(data)); + EXPECT_EQ(1, message2.optional_message().i()); + EXPECT_EQ(2, message2.repeated_message(0).msg().c()); +} + +// =================================================================== + +TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { + // Test that our nested enum values can be used as switch cases. This test + // doesn't actually do anything, the proof that it works is that it + // compiles. + int i =0; + unittest::TestAllTypes::NestedEnum a = unittest::TestAllTypes::BAR; + switch (a) { + case unittest::TestAllTypes::FOO: + i = 1; + break; + case unittest::TestAllTypes::BAR: + i = 2; + break; + case unittest::TestAllTypes::BAZ: + i = 3; + break; + // no default case: We want to make sure the compiler recognizes that + // all cases are covered. (GCC warns if you do not cover all cases of + // an enum in a switch.) + } + + // Token check just for fun. + EXPECT_EQ(2, i); +} + +TEST(GeneratedEnumTest, IsValidValue) { + // Test enum IsValidValue. + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(1)); + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(2)); + EXPECT_TRUE(unittest::TestAllTypes::NestedEnum_IsValid(3)); + + EXPECT_FALSE(unittest::TestAllTypes::NestedEnum_IsValid(0)); + EXPECT_FALSE(unittest::TestAllTypes::NestedEnum_IsValid(4)); + + // Make sure it also works when there are dups. + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(1)); + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(2)); + EXPECT_TRUE(unittest::TestEnumWithDupValue_IsValid(3)); + + EXPECT_FALSE(unittest::TestEnumWithDupValue_IsValid(0)); + EXPECT_FALSE(unittest::TestEnumWithDupValue_IsValid(4)); +} + +TEST(GeneratedEnumTest, MinAndMax) { + EXPECT_EQ(unittest::TestAllTypes::FOO,unittest::TestAllTypes::NestedEnum_MIN); + EXPECT_EQ(unittest::TestAllTypes::BAZ,unittest::TestAllTypes::NestedEnum_MAX); + + EXPECT_EQ(unittest::FOREIGN_FOO, unittest::ForeignEnum_MIN); + EXPECT_EQ(unittest::FOREIGN_BAZ, unittest::ForeignEnum_MAX); + + EXPECT_EQ(1, unittest::TestEnumWithDupValue_MIN); + EXPECT_EQ(3, unittest::TestEnumWithDupValue_MAX); + + EXPECT_EQ(unittest::SPARSE_E, unittest::TestSparseEnum_MIN); + EXPECT_EQ(unittest::SPARSE_C, unittest::TestSparseEnum_MAX); + + // Make sure we can use _MIN and _MAX as switch cases. + switch(unittest::SPARSE_A) { + case unittest::TestSparseEnum_MIN: + case unittest::TestSparseEnum_MAX: + break; + default: + break; + } +} + +// =================================================================== + +// Support code for testing services. +class GeneratedServiceTest : public testing::Test { + protected: + class MockTestService : public unittest::TestService { + public: + MockTestService() + : called_(false), + method_(""), + controller_(NULL), + request_(NULL), + response_(NULL), + done_(NULL) {} + + ~MockTestService() {} + + void Reset() { called_ = false; } + + // implements TestService ---------------------------------------- + + void Foo(RpcController* controller, + const unittest::FooRequest* request, + unittest::FooResponse* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = "Foo"; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + void Bar(RpcController* controller, + const unittest::BarRequest* request, + unittest::BarResponse* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = "Bar"; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + // --------------------------------------------------------------- + + bool called_; + string method_; + RpcController* controller_; + const Message* request_; + Message* response_; + Closure* done_; + }; + + class MockRpcChannel : public RpcChannel { + public: + MockRpcChannel() + : called_(false), + method_(NULL), + controller_(NULL), + request_(NULL), + response_(NULL), + done_(NULL), + destroyed_(NULL) {} + + ~MockRpcChannel() { + if (destroyed_ != NULL) *destroyed_ = true; + } + + void Reset() { called_ = false; } + + // implements TestService ---------------------------------------- + + void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) { + ASSERT_FALSE(called_); + called_ = true; + method_ = method; + controller_ = controller; + request_ = request; + response_ = response; + done_ = done; + } + + // --------------------------------------------------------------- + + bool called_; + const MethodDescriptor* method_; + RpcController* controller_; + const Message* request_; + Message* response_; + Closure* done_; + bool* destroyed_; + }; + + class MockController : public RpcController { + public: + void Reset() { + ADD_FAILURE() << "Reset() not expected during this test."; + } + bool Failed() const { + ADD_FAILURE() << "Failed() not expected during this test."; + return false; + } + string ErrorText() const { + ADD_FAILURE() << "ErrorText() not expected during this test."; + return ""; + } + void StartCancel() { + ADD_FAILURE() << "StartCancel() not expected during this test."; + } + void SetFailed(const string& reason) { + ADD_FAILURE() << "SetFailed() not expected during this test."; + } + bool IsCanceled() const { + ADD_FAILURE() << "IsCanceled() not expected during this test."; + return false; + } + void NotifyOnCancel(Closure* callback) { + ADD_FAILURE() << "NotifyOnCancel() not expected during this test."; + } + }; + + GeneratedServiceTest() + : descriptor_(unittest::TestService::descriptor()), + foo_(descriptor_->FindMethodByName("Foo")), + bar_(descriptor_->FindMethodByName("Bar")), + stub_(&mock_channel_), + done_(NewPermanentCallback(&DoNothing)) {} + + virtual void SetUp() { + ASSERT_TRUE(foo_ != NULL); + ASSERT_TRUE(bar_ != NULL); + } + + const ServiceDescriptor* descriptor_; + const MethodDescriptor* foo_; + const MethodDescriptor* bar_; + + MockTestService mock_service_; + MockController mock_controller_; + + MockRpcChannel mock_channel_; + unittest::TestService::Stub stub_; + + // Just so we don't have to re-define these with every test. + unittest::FooRequest foo_request_; + unittest::FooResponse foo_response_; + unittest::BarRequest bar_request_; + unittest::BarResponse bar_response_; + scoped_ptr done_; +}; + +TEST_F(GeneratedServiceTest, GetDescriptor) { + // Test that GetDescriptor() works. + + EXPECT_EQ(descriptor_, mock_service_.GetDescriptor()); +} + +TEST_F(GeneratedServiceTest, GetChannel) { + EXPECT_EQ(&mock_channel_, stub_.channel()); +} + +TEST_F(GeneratedServiceTest, OwnsChannel) { + MockRpcChannel* channel = new MockRpcChannel; + bool destroyed = false; + channel->destroyed_ = &destroyed; + + { + unittest::TestService::Stub owning_stub(channel, + Service::STUB_OWNS_CHANNEL); + EXPECT_FALSE(destroyed); + } + + EXPECT_TRUE(destroyed); +} + +TEST_F(GeneratedServiceTest, CallMethod) { + // Test that CallMethod() works. + + // Call Foo() via CallMethod(). + mock_service_.CallMethod(foo_, &mock_controller_, + &foo_request_, &foo_response_, done_.get()); + + ASSERT_TRUE(mock_service_.called_); + + EXPECT_EQ("Foo" , mock_service_.method_ ); + EXPECT_EQ(&mock_controller_, mock_service_.controller_); + EXPECT_EQ(&foo_request_ , mock_service_.request_ ); + EXPECT_EQ(&foo_response_ , mock_service_.response_ ); + EXPECT_EQ(done_.get() , mock_service_.done_ ); + + // Try again, but call Bar() instead. + mock_service_.Reset(); + mock_service_.CallMethod(bar_, &mock_controller_, + &bar_request_, &bar_response_, done_.get()); + + ASSERT_TRUE(mock_service_.called_); + EXPECT_EQ("Bar", mock_service_.method_); +} + +TEST_F(GeneratedServiceTest, CallMethodTypeFailure) { + // Verify death if we call Foo() with Bar's message types. + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + EXPECT_DEBUG_DEATH( + mock_service_.CallMethod(foo_, &mock_controller_, + &foo_request_, &bar_response_, done_.get()), + "dynamic_cast"); + + mock_service_.Reset(); + EXPECT_DEBUG_DEATH( + mock_service_.CallMethod(foo_, &mock_controller_, + &bar_request_, &foo_response_, done_.get()), + "dynamic_cast"); +#endif // GTEST_HAS_DEATH_TEST +} + +TEST_F(GeneratedServiceTest, GetPrototypes) { + // Test Get{Request,Response}Prototype() methods. + + EXPECT_EQ(&unittest::FooRequest::default_instance(), + &mock_service_.GetRequestPrototype(foo_)); + EXPECT_EQ(&unittest::BarRequest::default_instance(), + &mock_service_.GetRequestPrototype(bar_)); + + EXPECT_EQ(&unittest::FooResponse::default_instance(), + &mock_service_.GetResponsePrototype(foo_)); + EXPECT_EQ(&unittest::BarResponse::default_instance(), + &mock_service_.GetResponsePrototype(bar_)); +} + +TEST_F(GeneratedServiceTest, Stub) { + // Test that the stub class works. + + // Call Foo() via the stub. + stub_.Foo(&mock_controller_, &foo_request_, &foo_response_, done_.get()); + + ASSERT_TRUE(mock_channel_.called_); + + EXPECT_EQ(foo_ , mock_channel_.method_ ); + EXPECT_EQ(&mock_controller_, mock_channel_.controller_); + EXPECT_EQ(&foo_request_ , mock_channel_.request_ ); + EXPECT_EQ(&foo_response_ , mock_channel_.response_ ); + EXPECT_EQ(done_.get() , mock_channel_.done_ ); + + // Call Bar() via the stub. + mock_channel_.Reset(); + stub_.Bar(&mock_controller_, &bar_request_, &bar_response_, done_.get()); + + ASSERT_TRUE(mock_channel_.called_); + EXPECT_EQ(bar_, mock_channel_.method_); +} + +TEST_F(GeneratedServiceTest, NotImplemented) { + // Test that failing to implement a method of a service causes it to fail + // with a "not implemented" error message. + + // A service which doesn't implement any methods. + class UnimplementedService : public unittest::TestService { + public: + UnimplementedService() {} + }; + + UnimplementedService unimplemented_service; + + // And a controller which expects to get a "not implemented" error. + class ExpectUnimplementedController : public MockController { + public: + ExpectUnimplementedController() : called_(false) {} + + void SetFailed(const string& reason) { + EXPECT_FALSE(called_); + called_ = true; + EXPECT_EQ("Method Foo() not implemented.", reason); + } + + bool called_; + }; + + ExpectUnimplementedController controller; + + // Call Foo. + unimplemented_service.Foo(&controller, &foo_request_, &foo_response_, + done_.get()); + + EXPECT_TRUE(controller.called_); +} + +} // namespace + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_enum.cc b/src/google/protobuf/compiler/csharp/csharp_enum.cc new file mode 100644 index 00000000..b68fb5a5 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_enum.cc @@ -0,0 +1,190 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) + : 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) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().csharp_multiple_files(); + printer->Print( + "public $static$ enum $classname$ {\r\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + map vars; + vars["name"] = canonical_values_[i]->name(); + vars["index"] = SimpleItoa(canonical_values_[i]->index()); + vars["number"] = SimpleItoa(canonical_values_[i]->number()); + printer->Print(vars, + "$name$($index$, $number$),\r\n"); + } + + printer->Print( + ";\r\n" + "\r\n"); + + // ----------------------------------------------------------------- + + for (int i = 0; i < aliases_.size(); i++) { + map vars; + vars["classname"] = descriptor_->name(); + vars["name"] = aliases_[i].value->name(); + vars["canonical_name"] = aliases_[i].canonical_value->name(); + printer->Print(vars, + "public static final $classname$ $name$ = $canonical_name$;\r\n"); + } + + // ----------------------------------------------------------------- + + printer->Print( + "\r\n" + "public final int getNumber() { return value; }\r\n" + "\r\n" + "public static $classname$ valueOf(int value) {\r\n" + " switch (value) {\r\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + printer->Print( + "case $number$: return $name$;\r\n", + "name", canonical_values_[i]->name(), + "number", SimpleItoa(canonical_values_[i]->number())); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " default: return null;\r\n" + " }\r\n" + "}\r\n" + "\r\n"); + + // ----------------------------------------------------------------- + // Reflection + + printer->Print( + "public final com.google.protobuf.Descriptors.EnumValueDescriptor\r\n" + " getValueDescriptor() {\r\n" + " return getDescriptor().getValues().get(index);\r\n" + "}\r\n" + "public final com.google.protobuf.Descriptors.EnumDescriptor\r\n" + " getDescriptorForType() {\r\n" + " return getDescriptor();\r\n" + "}\r\n" + "public static final com.google.protobuf.Descriptors.EnumDescriptor\r\n" + " getDescriptor() {\r\n"); + + // TODO(kenton): Cache statically? Note that we can't access descriptors + // at module init time because it wouldn't work with descriptor.proto, but + // we can cache the value the first time getDescriptor() is called. + if (descriptor_->containing_type() == NULL) { + printer->Print( + " return $file$.getDescriptor().getEnumTypes().get($index$);\r\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + } else { + printer->Print( + " return $parent$.getDescriptor().getEnumTypes().get($index$);\r\n", + "parent", ClassName(descriptor_->containing_type()), + "index", SimpleItoa(descriptor_->index())); + } + + printer->Print( + "}\r\n" + "\r\n" + "private static final $classname$[] VALUES = {\r\n" + " ", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->value_count(); i++) { + printer->Print("$name$, ", + "name", descriptor_->value(i)->name()); + } + + printer->Print( + "\r\n" + "};\r\n" + "public static $classname$ valueOf(\r\n" + " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\r\n" + " if (desc.getType() != getDescriptor()) {\r\n" + " throw new java.lang.IllegalArgumentException(\r\n" + " \"EnumValueDescriptor is not for this type.\");\r\n" + " }\r\n" + " return VALUES[desc.getIndex()];\r\n" + "}\r\n", + "classname", descriptor_->name()); + + // ----------------------------------------------------------------- + + printer->Print( + "private final int index;\r\n" + "private final int value;\r\n" + "private $classname$(int index, int value) {\r\n" + " this.index = index;\r\n" + " this.value = value;\r\n" + "}\r\n", + "classname", descriptor_->name()); + + printer->Outdent(); + printer->Print("}\r\n\r\n"); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_enum.h b/src/google/protobuf/compiler/csharp/csharp_enum.h new file mode 100644 index 00000000..dec98918 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_enum.h @@ -0,0 +1,71 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +class EnumGenerator { + public: + explicit EnumGenerator(const EnumDescriptor* descriptor); + ~EnumGenerator(); + + void Generate(io::Printer* printer); + + private: + const EnumDescriptor* descriptor_; + + // The proto language allows multiple enum constants to have the same numeric + // value. Java, however, does not allow multiple enum constants to be + // considered equivalent. We treat the first defined constant for any + // given numeric value as "canonical" and the rest as aliases of that + // canonical value. + vector canonical_values_; + + struct Alias { + const EnumValueDescriptor* value; + const EnumValueDescriptor* canonical_value; + }; + vector aliases_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.cc b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc new file mode 100644 index 00000000..c8d6cf5e --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.cc @@ -0,0 +1,265 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const FieldDescriptor* descriptor, + map* variables) { + const EnumValueDescriptor* default_value; + default_value = descriptor->default_value_enum(); + + string type = ClassName(descriptor->enum_type()); + + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = type; + (*variables)["default"] = type + "." + default_value->name(); +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\r\n" + "private $type$ $name$_ = $default$;\r\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\r\n" + "public $type$ get$capitalized_name$() { return $name$_; }\r\n"); +} + +void EnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\r\n" + " return result.has$capitalized_name$();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$() {\r\n" + " return result.get$capitalized_name$();\r\n" + "}\r\n" + "public Builder set$capitalized_name$($type$ value) {\r\n" + " result.has$capitalized_name$ = true;\r\n" + " result.$name$_ = value;\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.has$capitalized_name$ = false;\r\n" + " result.$name$_ = $default$;\r\n" + " return this;\r\n" + "}\r\n"); +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\r\n" + " set$capitalized_name$(other.get$capitalized_name$());\r\n" + "}\r\n"); +} + +void EnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for enum types. +} + +void EnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\r\n" + "$type$ value = $type$.valueOf(rawValue);\r\n" + "if (value == null) {\r\n" + " unknownFields.mergeVarintField($number$, rawValue);\r\n" + "} else {\r\n" + " set$capitalized_name$(value);\r\n" + "}\r\n"); +} + +void EnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " output.writeEnum($number$, get$capitalized_name$().getNumber());\r\n" + "}\r\n"); +} + +void EnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .computeEnumSize($number$, get$capitalized_name$().getNumber());\r\n" + "}\r\n"); +} + +string EnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\r\n" + " java.util.Collections.emptyList();\r\n" + "public java.util.List<$type$> get$capitalized_name$List() {\r\n" + " return $name$_;\r\n" // note: unmodifiable list + "}\r\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return $name$_.get(index);\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\r\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n" + "public int get$capitalized_name$Count() {\r\n" + " return result.get$capitalized_name$Count();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return result.get$capitalized_name$(index);\r\n" + "}\r\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\r\n" + " result.$name$_.set(index, value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder add$capitalized_name$($type$ value) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " result.$name$_.add(value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder addAll$capitalized_name$(\r\n" + " java.lang.Iterable values) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " super.addAll(values, result.$name$_);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.$name$_ = java.util.Collections.emptyList();\r\n" + " return this;\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " result.$name$_.addAll(other.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\r\n" + " result.$name$_ =\r\n" + " java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\r\n" + "$type$ value = $type$.valueOf(rawValue);\r\n" + "if (value == null) {\r\n" + " unknownFields.mergeVarintField($number$, rawValue);\r\n" + "} else {\r\n" + " add$capitalized_name$(value);\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " output.writeEnum($number$, element.getNumber());\r\n" + "}\r\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .computeEnumSize($number$, element.getNumber());\r\n" + "}\r\n"); +} + +string RepeatedEnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_enum_field.h b/src/google/protobuf/compiler/csharp/csharp_enum_field.h new file mode 100644 index 00000000..85f55d75 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_enum_field.h @@ -0,0 +1,85 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_extension.cc b/src/google/protobuf/compiler/csharp/csharp_extension.cc new file mode 100644 index 00000000..660ad3d3 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_extension.cc @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) {} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::Generate(io::Printer* printer) { + map vars; + vars["name"] = UnderscoresToCamelCase(descriptor_); + vars["containing_type"] = ClassName(descriptor_->containing_type()); + vars["index"] = SimpleItoa(descriptor_->index()); + + JavaType csharp_type = GetJavaType(descriptor_); + string singular_type; + switch (csharp_type) { + case JAVATYPE_MESSAGE: + vars["type"] = ClassName(descriptor_->message_type()); + break; + case JAVATYPE_ENUM: + vars["type"] = ClassName(descriptor_->enum_type()); + break; + default: + vars["type"] = BoxedPrimitiveTypeName(csharp_type); + break; + } + + if (descriptor_->is_repeated()) { + printer->Print(vars, + "public static final\r\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\r\n" + " $containing_type$,\r\n" + " java.util.List<$type$>> $name$ =\r\n" + " com.google.protobuf.GeneratedMessage\r\n" + " .newRepeatedGeneratedExtension(\r\n" + " getDescriptor().getExtensions().get($index$),\r\n" + " $type$.class);\r\n"); + } else { + printer->Print(vars, + "public static final\r\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\r\n" + " $containing_type$,\r\n" + " $type$> $name$ =\r\n" + " com.google.protobuf.GeneratedMessage.newGeneratedExtension(\r\n" + " getDescriptor().getExtensions().get($index$),\r\n" + " $type$.class);\r\n"); + } +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_extension.h b/src/google/protobuf/compiler/csharp/csharp_extension.h new file mode 100644 index 00000000..3f14762f --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_extension.h @@ -0,0 +1,59 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_EXTENSION_H__ + +#include + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +// Generates code for an extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ExtensionGenerator { + public: + explicit ExtensionGenerator(const FieldDescriptor* descriptor); + ~ExtensionGenerator(); + + void Generate(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_field.cc b/src/google/protobuf/compiler/csharp/csharp_field.cc new file mode 100644 index 00000000..fd000b7c --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_field.cc @@ -0,0 +1,89 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +FieldGenerator::~FieldGenerator() {} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr[descriptor->field_count()]), + extension_generators_( + new scoped_ptr[descriptor->extension_count()]) { + + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + } + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset(MakeGenerator(descriptor->extension(i))); + } +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new RepeatedEnumFieldGenerator(field); + default: + return new RepeatedPrimitiveFieldGenerator(field); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new MessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new EnumFieldGenerator(field); + default: + return new PrimitiveFieldGenerator(field); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { + return *extension_generators_[index]; +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_field.h b/src/google/protobuf/compiler/csharp/csharp_field.h new file mode 100644 index 00000000..901a2d0c --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_field.h @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +class FieldGenerator { + public: + FieldGenerator() {} + virtual ~FieldGenerator(); + + virtual void GenerateMembers(io::Printer* printer) const = 0; + virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateBuildingCode(io::Printer* printer) const = 0; + virtual void GenerateParsingCode(io::Printer* printer) const = 0; + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + + virtual string GetBoxedType() const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + const FieldGenerator& get_extension(int index) const; + + private: + const Descriptor* descriptor_; + scoped_array > field_generators_; + scoped_array > extension_generators_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_FIELD_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_file.cc b/src/google/protobuf/compiler/csharp/csharp_file.cc new file mode 100644 index 00000000..581a20f2 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_file.cc @@ -0,0 +1,297 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +FileGenerator::FileGenerator(const FileDescriptor* file) + : file_(file), + csharp_namespace_(FileJavaPackage(file)), + classname_(FileClassName(file)) {} + +FileGenerator::~FileGenerator() {} + +bool FileGenerator::Validate(string* error) { + // 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 csharp_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; i < file_->enum_type_count() && !found_conflict; i++) { + if (file_->enum_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) { + if (file_->message_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->service_count() && !found_conflict; i++) { + if (file_->service(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 csharp_outer_classname " + "option to specify a different outer class name for the .proto file."); + return false; + } + + return true; +} + +// Shameless copy of CEscape, but using backslash-u encoding +// Still won't really work for anything non-ASCII. +static int UnicodeEscapeInternal(const char* src, int src_len, char* dest, + int dest_len) { + const char* src_end = src + src_len; + int used = 0; + for (; src < src_end; src++) { + if (dest_len - used < 2) // Need space for two letter escape + return -1; + + bool is_hex_escape = false; + switch (*src) { + case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break; + case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break; + case '\t': dest[used++] = '\\'; dest[used++] = 't'; break; + case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break; + case '\'': dest[used++] = '\\'; dest[used++] = '\''; break; + case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break; + default: + if (*src < 0x20 || *src > 0x7E) { + if (dest_len - used < 6) // need space for 6 letter escape + return -1; + sprintf(dest + used, "\\u00%02x", static_cast(*src)); + used += 6; + } else { + dest[used++] = *src; break; + } + } + } + + if (dest_len - used < 1) // make sure that there is room for \0 + return -1; + + dest[used] = '\0'; // doesn't count towards return value though + return used; +} + +string UnicodeEscape(const string& src) { + const int dest_length = src.size() * 6 + 1; // Maximum possible expansion + scoped_array dest(new char[dest_length]); + const int len = UnicodeEscapeInternal(src.data(), src.size(), + dest.get(), dest_length); + GOOGLE_DCHECK_GE(len, 0); + return string(dest.get(), len); +} + + +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!\r\n" + "\r\n"); + if (!csharp_namespace_.empty()) { + printer->Print( + "package $package$;\r\n" + "\r\n", + "package", csharp_namespace_); + } + printer->Print( + "public final class $classname$ {\r\n" + " private $classname$() {}\r\n", + "classname", classname_); + printer->Indent(); + + // ----------------------------------------------------------------- + + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. We unfortunately have to put it in + // a string literal, not a byte array, because apparently using a literal + // byte array causes the Java compiler to generate *instructions* to + // initialize each and every byte of the array, e.g. as if you typed: + // b[0] = 123; b[1] = 456; b[2] = 789; + // This makes huge bytecode files and can easily hit the compiler's internal + // code size limits (error "code to large"). String literals are apparently + // embedded raw, which is what we want. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\r\n" + " getDescriptor() {\r\n" + " return descriptor;\r\n" + "}\r\n" + "private static final com.google.protobuf.Descriptors.FileDescriptor\r\n" + " descriptor = buildDescriptor();\r\n" + "private static\r\n" + " com.google.protobuf.Descriptors.FileDescriptor\r\n" + " buildDescriptor() {\r\n" + " string descriptorData =\r\n"); + printer->Indent(); + printer->Indent(); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + if (i > 0) printer->Print(" +\r\n"); + printer->Print("\"$data$\"", + "data", UnicodeEscape(file_data.substr(i, kBytesPerLine))); + } + printer->Print(";\r\n"); + + printer->Outdent(); + printer->Print( + "try {\r\n" + " return com.google.protobuf.Descriptors.FileDescriptor\r\n" + " .internalBuildGeneratedFileFrom(descriptorData,\r\n" + " new com.google.protobuf.Descriptors.FileDescriptor[] {\r\n"); + + for (int i = 0; i < file_->dependency_count(); i++) { + printer->Print( + " $dependency$.getDescriptor(),\r\n", + "dependency", ClassName(file_->dependency(i))); + } + + printer->Print( + " });\r\n" + "} catch (Exception e) {\r\n" + " throw new RuntimeException(\r\n" + " \"Failed to parse protocol buffer descriptor for \" +\r\n" + " \"\\\"$filename$\\\".\", e);\r\n" + "}\r\n", + "filename", file_->name()); + + printer->Outdent(); + printer->Print( + "}\r\n" + "\r\n"); + + // ----------------------------------------------------------------- + + if (!file_->options().csharp_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator(file_->enum_type(i)).Generate(printer); + } + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator(file_->message_type(i)).Generate(printer); + } + for (int i = 0; i < file_->service_count(); i++) { + ServiceGenerator(file_->service(i)).Generate(printer); + } + } + + // Extensions must be generated in the outer class since they are values, + // not classes. + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator(file_->extension(i)).Generate(printer); + } + + // Static variables. + for (int i = 0; i < file_->message_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer); + } + + printer->Outdent(); + printer->Print("}\r\n"); +} + +template +static void GenerateSibling(const string& package_dir, + const string& csharp_namespace, + const DescriptorClass* descriptor, + OutputDirectory* output_directory, + vector* file_list) { + string filename = package_dir + descriptor->name() + ".cs"; + file_list->push_back(filename); + + scoped_ptr output( + output_directory->Open(filename)); + io::Printer printer(output.get(), '$'); + + printer.Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\r\n" + "\r\n"); + if (!csharp_namespace.empty()) { + printer.Print( + "package $package$;\r\n" + "\r\n", + "package", csharp_namespace); + } + + GeneratorClass(descriptor).Generate(&printer); +} + +void FileGenerator::GenerateSiblings(const string& package_dir, + OutputDirectory* output_directory, + vector* file_list) { + if (file_->options().csharp_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + GenerateSibling(package_dir, csharp_namespace_, + file_->enum_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->message_type_count(); i++) { + GenerateSibling(package_dir, csharp_namespace_, + file_->message_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->service_count(); i++) { + GenerateSibling(package_dir, csharp_namespace_, + file_->service(i), + output_directory, file_list); + } + } +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_file.h b/src/google/protobuf/compiler/csharp/csharp_file.h new file mode 100644 index 00000000..88b1c577 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_file.h @@ -0,0 +1,79 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_FILE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } + namespace compiler { + class OutputDirectory; // code_generator.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +class FileGenerator { + public: + explicit FileGenerator(const FileDescriptor* file); + ~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, + OutputDirectory* output_directory, + vector* file_list); + + const string& csharp_namespace() { return csharp_namespace_; } + const string& classname() { return classname_; } + + private: + const FileDescriptor* file_; + string csharp_namespace_; + string classname_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_FILE_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.cc b/src/google/protobuf/compiler/csharp/csharp_generator.cc new file mode 100644 index 00000000..94747f15 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_generator.cc @@ -0,0 +1,134 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +namespace { + +// Parses a set of comma-delimited name/value pairs, e.g.: +// "foo=bar,baz,qux=corge" +// parses to the pairs: +// ("foo", "bar"), ("baz", ""), ("qux", "corge") +void ParseOptions(const string& text, vector >* output) { + vector parts; + SplitStringUsing(text, ",", &parts); + + for (int i = 0; i < parts.size(); i++) { + string::size_type equals_pos = parts[i].find_first_of('='); + pair value; + if (equals_pos == string::npos) { + value.first = parts[i]; + value.second = ""; + } else { + value.first = parts[i].substr(0, equals_pos); + value.second = parts[i].substr(equals_pos + 1); + } + output->push_back(value); + } +} + +} // namespace + +CSharpGenerator::CSharpGenerator() {} +CSharpGenerator::~CSharpGenerator() {} + +bool CSharpGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + vector > options; + ParseOptions(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; + + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "output_list_file") { + output_list_file = options[i].second; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + + // ----------------------------------------------------------------- + + + FileGenerator file_generator(file); + if (!file_generator.Validate(error)) { + return false; + } + + string package_dir = + StringReplace(file_generator.csharp_namespace(), ".", "/", true); + if (!package_dir.empty()) package_dir += "/"; + + vector all_files; + + string csharp_filename = package_dir; + csharp_filename += file_generator.classname(); + csharp_filename += ".cs"; + all_files.push_back(csharp_filename); + + // Generate main csharp file. + scoped_ptr output( + output_directory->Open(csharp_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 .csharp files being generated. + scoped_ptr 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$\r\n", "filename", all_files[i]); + } + } + + return true; +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.h b/src/google/protobuf/compiler/csharp/csharp_generator.h new file mode 100644 index 00000000..41f320a7 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_generator.h @@ -0,0 +1,59 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Generates Java code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +// CodeGenerator implementation which generates Java code. If you create your +// own protocol compiler binary and you want it to support Java output, you +// can do so by registering an instance of this CodeGenerator with the +// CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT CSharpGenerator : public CodeGenerator { + public: + CSharpGenerator(); + ~CSharpGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CSharpGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc new file mode 100644 index 00000000..27c53678 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -0,0 +1,230 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +const char kThickSeparator[] = + "// ===================================================================\r\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\r\n"; + +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 StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +string FileClassName(const FileDescriptor* file) { + if (file->options().has_csharp_outer_classname()) { + return file->options().csharp_outer_classname(); + } else { + 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 FileDescriptor* file) { + if (file->options().has_csharp_namespace()) { + return file->options().csharp_namespace(); + } else { + string result = kDefaultPackage; + if (!file->package().empty()) { + if (!result.empty()) result += '.'; + result += file->package(); + } + return result; + } +} + +string ToJavaName(const string& full_name, const FileDescriptor* file) { + string result; + if (file->options().csharp_multiple_files()) { + result = FileJavaPackage(file); + } else { + result = ClassName(file); + } + if (!result.empty()) { + result += '.'; + } + if (file->package().empty()) { + result += full_name; + } else { + // Strip the proto package from full_name since we've replaced it with + // the Java package. + result += full_name.substr(file->package().size() + 1); + } + return result; +} + +string ClassName(const FileDescriptor* descriptor) { + string result = FileJavaPackage(descriptor); + if (!result.empty()) result += '.'; + result += FileClassName(descriptor); + return result; +} + +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; +} + +const char* 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 "string"; + case JAVATYPE_BYTES : return "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h new file mode 100644 index 00000000..9fd4431b --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -0,0 +1,103 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +// 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); + +// 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 FileDescriptor* file); + +// Returns the file's Java package name. +string FileJavaPackage(const FileDescriptor* file); + +// Converts the given fully-qualified name in the proto namespace to its +// fully-qualified name in the Java namespace, given that it is in the given +// file. +string ToJavaName(const string& full_name, const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +inline string ClassName(const Descriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const EnumDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const ServiceDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +string ClassName(const FileDescriptor* descriptor); + +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()); +} + +// 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. +const char* BoxedPrimitiveTypeName(JavaType type); + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_HELPERS_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc new file mode 100644 index 00000000..c6c094e1 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -0,0 +1,723 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +using internal::WireFormat; + +namespace { + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // Print the field's proto-syntax definition as a comment. We don't want to + // print group bodies so we cut off after the first line. + string def = field->DebugString(); + printer->Print("// $def$\r\n", + "def", def.substr(0, def.find_first_of('\r\n'))); +} + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +struct ExtensionRangeOrdering { + bool operator()(const Descriptor::ExtensionRange* a, + const Descriptor::ExtensionRange* b) const { + return a->start < b->start; + } +}; + +// 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; +} + +// Get an identifier that uniquely identifies this type within the file. +// This is used to declare static variables related to this type at the +// outermost file scope. +string UniqueFileScopeIdentifier(const Descriptor* descriptor) { + return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); +} + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its isInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +static bool HasRequiredFields( + const Descriptor* type, + hash_set* already_seen) { + if (already_seen->count(type) > 0) { + // The type is already in cache. This means that either: + // a. The type has no required fields. + // b. We are in the midst of checking if the type has required fields, + // somewhere up the stack. In this case, we know that if the type + // has any required fields, they'll be found when we return to it, + // and the whole call to HasRequiredFields() will return true. + // Therefore, we don't have to check if this type has required fields + // here. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +static bool HasRequiredFields(const Descriptor* type) { + hash_set already_seen; + return HasRequiredFields(type, &already_seen); +} + +} // namespace + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_(descriptor) { +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { + // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is + // used in the construction of descriptors, we have a tricky bootstrapping + // problem. To help control static initialization order, we make sure all + // descriptors and other static data that depends on them are members of + // the outermost class in the file. This way, they will be initialized in + // a deterministic order. + + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = ClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier(descriptor_->containing_type()); + } + if (descriptor_->file()->options().csharp_multiple_files()) { + // We can only make these package-private since the classes that use them + // are in separate files. + vars["private"] = ""; + } else { + vars["private"] = "private "; + } + + // The descriptor for this type. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\r\n" + " internal_$identifier$_descriptor =\r\n" + " getDescriptor().getMessageTypes().get($index$);\r\n"); + } else { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\r\n" + " internal_$identifier$_descriptor =\r\n" + " internal_$parent$_descriptor.getNestedTypes().get($index$);\r\n"); + } + + // And the FieldAccessorTable. + printer->Print(vars, + "$private$static\r\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable\r\n" + " internal_$identifier$_fieldAccessorTable = new\r\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\r\n" + " internal_$identifier$_descriptor,\r\n" + " new string[] { "); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print( + "\"$field_name$\", ", + "field_name", + UnderscoresToCapitalizedCamelCase(descriptor_->field(i))); + } + printer->Print("},\r\n" + " $classname$.class,\r\n" + " $classname$.Builder.class);\r\n", + "classname", ClassName(descriptor_)); + + // 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)) + .GenerateStaticVariables(printer); + } +} + +void MessageGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().csharp_multiple_files(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public $static$ final class $classname$ extends\r\n" + " com.google.protobuf.GeneratedMessage.ExtendableMessage<\r\n" + " $classname$> {\r\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } else { + printer->Print( + "public $static$ final class $classname$ extends\r\n" + " com.google.protobuf.GeneratedMessage {\r\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } + printer->Indent(); + printer->Print( + "// Use $classname$.newBuilder() to construct.\r\n" + "private $classname$() {}\r\n" + "\r\n" + "private static final $classname$ defaultInstance = new $classname$();\r\n" + "public static $classname$ getDefaultInstance() {\r\n" + " return defaultInstance;\r\n" + "}\r\n" + "\r\n" + "public $classname$ getDefaultInstanceForType() {\r\n" + " return defaultInstance;\r\n" + "}\r\n" + "\r\n", + "classname", descriptor_->name()); + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\r\n" + " getDescriptor() {\r\n" + " return $fileclass$.internal_$identifier$_descriptor;\r\n" + "}\r\n" + "\r\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\r\n" + " internalGetFieldAccessorTable() {\r\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable;\r\n" + "}\r\n" + "\r\n", + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + + // Nested types and extensions + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + MessageGenerator(descriptor_->nested_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->extension_count(); i++) { + ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + } + + // Fields + for (int i = 0; i < descriptor_->field_count(); i++) { + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); + printer->Print("\r\n"); + } + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateIsInitialized(printer); + GenerateMessageSerializationMethods(printer); + } + + GenerateParseFromMethods(printer); + GenerateBuilder(printer); +} + +// =================================================================== + +void MessageGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + printer->Print( + "public void writeTo(com.google.protobuf.CodedOutputStream output)\r\n" + " throws java.io.IOException {\r\n"); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "com.google.protobuf.GeneratedMessage.ExtendableMessage\r\n" + " .ExtensionWriter extensionWriter = newExtensionWriter();\r\n"); + } + + // Merge the fields and the extension ranges, both sorted by field number. + for (int i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "getUnknownFields().writeAsMessageSetTo(output);\r\n"); + } else { + printer->Print( + "getUnknownFields().writeTo(output);\r\n"); + } + + printer->Outdent(); + printer->Print( + "}\r\n" + "\r\n" + "private int memoizedSerializedSize = -1;\r\n" + "public int getSerializedSize() {\r\n" + " int size = memoizedSerializedSize;\r\n" + " if (size != -1) return size;\r\n" + "\r\n" + " size = 0;\r\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "size += extensionsSerializedSize();\r\n"); + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "size += getUnknownFields().getSerializedSizeAsMessageSet();\r\n"); + } else { + printer->Print( + "size += getUnknownFields().getSerializedSize();\r\n"); + } + + printer->Outdent(); + printer->Print( + " memoizedSerializedSize = size;\r\n" + " return size;\r\n" + "}\r\n" + "\r\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( + "public static $classname$ parseFrom(\r\n" + " com.google.protobuf.ByteString data)\r\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\r\n" + " return newBuilder().mergeFrom(data).buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(\r\n" + " com.google.protobuf.ByteString data,\r\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\r\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\r\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\r\n" + " .buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(byte[] data)\r\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\r\n" + " return newBuilder().mergeFrom(data).buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(\r\n" + " byte[] data,\r\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\r\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\r\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\r\n" + " .buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(java.io.InputStream input)\r\n" + " throws java.io.IOException {\r\n" + " return newBuilder().mergeFrom(input).buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(\r\n" + " java.io.InputStream input,\r\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\r\n" + " throws java.io.IOException {\r\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\r\n" + " .buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(\r\n" + " com.google.protobuf.CodedInputStream input)\r\n" + " throws java.io.IOException {\r\n" + " return newBuilder().mergeFrom(input).buildParsed();\r\n" + "}\r\n" + "public static $classname$ parseFrom(\r\n" + " com.google.protobuf.CodedInputStream input,\r\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\r\n" + " throws java.io.IOException {\r\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\r\n" + " .buildParsed();\r\n" + "}\r\n" + "\r\n", + "classname", ClassName(descriptor_)); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void MessageGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + printer->Print( + "extensionWriter.writeUntil($end$, output);\r\n", + "end", SimpleItoa(range->end)); +} + +// =================================================================== + +void MessageGenerator::GenerateBuilder(io::Printer* printer) { + printer->Print( + "public static Builder newBuilder() { return new Builder(); }\r\n" + "public Builder newBuilderForType() { return new Builder(); }\r\n" + "public static Builder newBuilder($classname$ prototype) {\r\n" + " return new Builder().mergeFrom(prototype);\r\n" + "}\r\n" + "\r\n", + "classname", ClassName(descriptor_)); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public static final class Builder extends\r\n" + " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\r\n" + " $classname$, Builder> {\r\n", + "classname", ClassName(descriptor_)); + } else { + printer->Print( + "public static final class Builder extends\r\n" + " com.google.protobuf.GeneratedMessage.Builder {\r\n", + "classname", ClassName(descriptor_)); + } + printer->Indent(); + + GenerateCommonBuilderMethods(printer); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateBuilderParsingMethods(printer); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\r\n"); + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + printer->Outdent(); + printer->Print("}\r\n"); + printer->Outdent(); + printer->Print("}\r\n\r\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\r\n" + "private Builder() {}\r\n" + "\r\n" + "$classname$ result = new $classname$();\r\n" + "\r\n" + "protected $classname$ internalGetResult() {\r\n" + " return result;\r\n" + "}\r\n" + "\r\n" + "public Builder clear() {\r\n" + " result = new $classname$();\r\n" + " return this;\r\n" + "}\r\n" + "\r\n" + "public Builder clone() {\r\n" + " return new Builder().mergeFrom(result);\r\n" + "}\r\n" + "\r\n" + "public com.google.protobuf.Descriptors.Descriptor\r\n" + " getDescriptorForType() {\r\n" + " return $classname$.getDescriptor();\r\n" + "}\r\n" + "\r\n" + "public $classname$ getDefaultInstanceForType() {\r\n" + " return $classname$.getDefaultInstance();\r\n" + "}\r\n" + "\r\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + printer->Print( + "public $classname$ build() {\r\n" + " if (!isInitialized()) {\r\n" + " throw new com.google.protobuf.UninitializedMessageException(\r\n" + " result);\r\n" + " }\r\n" + " return buildPartial();\r\n" + "}\r\n" + "\r\n" + "private $classname$ buildParsed()\r\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\r\n" + " if (!isInitialized()) {\r\n" + " throw new com.google.protobuf.UninitializedMessageException(\r\n" + " result).asInvalidProtocolBufferException();\r\n" + " }\r\n" + " return buildPartial();\r\n" + "}\r\n" + "\r\n" + "public $classname$ buildPartial() {\r\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); + } + + printer->Outdent(); + printer->Print( + " $classname$ returnMe = result;\r\n" + " result = null;\r\n" + " return returnMe;\r\n" + "}\r\n" + "\r\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print( + "public Builder mergeFrom(com.google.protobuf.Message other) {\r\n" + " if (other instanceof $classname$) {\r\n" + " return mergeFrom(($classname$)other);\r\n" + " } else {\r\n" + " super.mergeFrom(other);\r\n" + " return this;\r\n" + " }\r\n" + "}\r\n" + "\r\n" + "public Builder mergeFrom($classname$ other) {\r\n" + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + " if (other == $classname$.getDefaultInstance()) return this;\r\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateMergingCode(printer); + } + + printer->Outdent(); + printer->Print( + " this.mergeUnknownFields(other.getUnknownFields());\r\n" + " return this;\r\n" + "}\r\n" + "\r\n"); + } +} + +// =================================================================== + +void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "public Builder mergeFrom(\r\n" + " com.google.protobuf.CodedInputStream input)\r\n" + " throws java.io.IOException {\r\n" + " return mergeFrom(input,\r\n" + " com.google.protobuf.ExtensionRegistry.getEmptyRegistry());\r\n" + "}\r\n" + "\r\n" + "public Builder mergeFrom(\r\n" + " com.google.protobuf.CodedInputStream input,\r\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\r\n" + " throws java.io.IOException {\r\n"); + printer->Indent(); + + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\r\n" + " com.google.protobuf.UnknownFieldSet.newBuilder(\r\n" + " this.getUnknownFields());\r\n" + "while (true) {\r\n"); + printer->Indent(); + + printer->Print( + "int tag = input.readTag();\r\n" + "switch (tag) {\r\n"); + printer->Indent(); + + printer->Print( + "case 0:\r\n" // zero signals EOF / limit reached + " this.setUnknownFields(unknownFields.build());\r\n" + " return this;\r\n" + "default: {\r\n" + " if (!parseUnknownField(input, unknownFields,\r\n" + " extensionRegistry, tag)) {\r\n" + " this.setUnknownFields(unknownFields.build());\r\n" + " return this;\r\n" // it's an endgroup tag + " }\r\n" + " break;\r\n" + "}\r\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormat::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\r\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\r\n" + "}\r\n"); + } + + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\r\n" // switch (tag) + " }\r\n" // while (true) + "}\r\n" + "\r\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { + printer->Print( + "public final boolean isInitialized() {\r\n"); + printer->Indent(); + + // Check that all required fields in this message are set. + // TODO(kenton): We can optimize this when we switch to putting all the + // "has" fields into a single bitfield. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_required()) { + printer->Print( + "if (!has$name$) return false;\r\n", + "name", UnderscoresToCapitalizedCamelCase(field)); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) return false;\r\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_OPTIONAL: + printer->Print( + "if (has$name$()) {\r\n" + " if (!get$name$().isInitialized()) return false;\r\n" + "}\r\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_REPEATED: + printer->Print( + "for ($type$ element : get$name$List()) {\r\n" + " if (!element.isInitialized()) return false;\r\n" + "}\r\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) return false;\r\n"); + } + + printer->Outdent(); + printer->Print( + " return true;\r\n" + "}\r\n" + "\r\n"); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_message.h b/src/google/protobuf/compiler/csharp/csharp_message.h new file mode 100644 index 00000000..aff3e6f2 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_message.h @@ -0,0 +1,77 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +class MessageGenerator { + public: + explicit MessageGenerator(const Descriptor* descriptor); + ~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); + + // Generate the class itself. + void Generate(io::Printer* printer); + + private: + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateBuilder(io::Printer* printer); + void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateBuilderParsingMethods(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + const Descriptor* descriptor_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc new file mode 100644 index 00000000..8d833b4f --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -0,0 +1,303 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = ClassName(descriptor->message_type()); + (*variables)["group_or_message"] = + (descriptor->type() == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message"; +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\r\n" + "private $type$ $name$_ = $type$.getDefaultInstance();\r\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\r\n" + "public $type$ get$capitalized_name$() { return $name$_; }\r\n"); +} + +void MessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\r\n" + " return result.has$capitalized_name$();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$() {\r\n" + " return result.get$capitalized_name$();\r\n" + "}\r\n" + "public Builder set$capitalized_name$($type$ value) {\r\n" + " result.has$capitalized_name$ = true;\r\n" + " result.$name$_ = value;\r\n" + " return this;\r\n" + "}\r\n" + "public Builder set$capitalized_name$($type$.Builder builderForValue) {\r\n" + " result.has$capitalized_name$ = true;\r\n" + " result.$name$_ = builderForValue.build();\r\n" + " return this;\r\n" + "}\r\n" + "public Builder merge$capitalized_name$($type$ value) {\r\n" + " if (result.has$capitalized_name$() &&\r\n" + " result.$name$_ != $type$.getDefaultInstance()) {\r\n" + " result.$name$_ =\r\n" + " $type$.newBuilder(result.$name$_).mergeFrom(value).buildPartial();\r\n" + " } else {\r\n" + " result.$name$_ = value;\r\n" + " }\r\n" + " result.has$capitalized_name$ = true;\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.has$capitalized_name$ = false;\r\n" + " result.$name$_ = $type$.getDefaultInstance();\r\n" + " return this;\r\n" + "}\r\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\r\n" + " merge$capitalized_name$(other.get$capitalized_name$());\r\n" + "}\r\n"); +} + +void MessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do for singular fields. +} + +void MessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\r\n" + "if (has$capitalized_name$()) {\r\n" + " subBuilder.mergeFrom(get$capitalized_name$());\r\n" + "}\r\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\r\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\r\n"); + } + + printer->Print(variables_, + "set$capitalized_name$(subBuilder.buildPartial());\r\n"); +} + +void MessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " output.write$group_or_message$($number$, get$capitalized_name$());\r\n" + "}\r\n"); +} + +void MessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .compute$group_or_message$Size($number$, get$capitalized_name$());\r\n" + "}\r\n"); +} + +string MessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\r\n" + " java.util.Collections.emptyList();\r\n" + "public java.util.List<$type$> get$capitalized_name$List() {\r\n" + " return $name$_;\r\n" // note: unmodifiable list + "}\r\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return $name$_.get(index);\r\n" + "}\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\r\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n" + "public int get$capitalized_name$Count() {\r\n" + " return result.get$capitalized_name$Count();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return result.get$capitalized_name$(index);\r\n" + "}\r\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\r\n" + " result.$name$_.set(index, value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder set$capitalized_name$(int index, " + "$type$.Builder builderForValue) {\r\n" + " result.$name$_.set(index, builderForValue.build());\r\n" + " return this;\r\n" + "}\r\n" + "public Builder add$capitalized_name$($type$ value) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " result.$name$_.add(value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder add$capitalized_name$($type$.Builder builderForValue) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " result.$name$_.add(builderForValue.build());\r\n" + " return this;\r\n" + "}\r\n" + "public Builder addAll$capitalized_name$(\r\n" + " java.lang.Iterable values) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " super.addAll(values, result.$name$_);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.$name$_ = java.util.Collections.emptyList();\r\n" + " return this;\r\n" + "}\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\r\n" + " }\r\n" + " result.$name$_.addAll(other.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\r\n" + " result.$name$_ =\r\n" + " java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\r\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\r\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\r\n"); + } + + printer->Print(variables_, + "add$capitalized_name$(subBuilder.buildPartial());\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " output.write$group_or_message$($number$, element);\r\n" + "}\r\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .compute$group_or_message$Size($number$, element);\r\n" + "}\r\n"); +} + +string RepeatedMessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.h b/src/google/protobuf/compiler/csharp/csharp_message_field.h new file mode 100644 index 00000000..7d8b5161 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.h @@ -0,0 +1,85 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc new file mode 100644 index 00000000..3ff24499 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -0,0 +1,366 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +namespace { + +const char* 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 "string"; + case JAVATYPE_BYTES : return "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +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; +} + +bool AllPrintableAscii(const string& text) { + // Cannot use isprint() because it's locale-specific. :( + for (int i = 0; i < text.size(); i++) { + if ((text[i] < 0x20) || text[i] >= 0x7F) { + return false; + } + } + return true; +} + +string DefaultValue(const FieldDescriptor* field) { + // 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(field->default_value_uint32())); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field->default_value_int64()) + "L"; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(static_cast(field->default_value_uint64())) + + "L"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()) + "D"; + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()) + "F"; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_STRING: { + bool isBytes = field->type() == FieldDescriptor::TYPE_BYTES; + + if (!isBytes && AllPrintableAscii(field->default_value_string())) { + // All chars are ASCII and printable. In this case CEscape() works + // fine (it will only escape quotes and backslashes). + // Note: If this "optimization" is removed, DescriptorProtos will + // no longer be able to initialize itself due to bootstrapping + // problems. + return "\"" + CEscape(field->default_value_string()) + "\""; + } + + if (isBytes && !field->has_default_value()) { + return "com.google.protobuf.ByteString.EMPTY"; + } + + // Escaping strings correctly for Java and generating efficient + // initializers for ByteStrings are both tricky. We can sidestep the + // whole problem by just grabbing the default value from the descriptor. + return strings::Substitute( + "(($0) $1.getDescriptor().getFields().get($2).getDefaultValue())", + isBytes ? "com.google.protobuf.ByteString" : "string", + ClassName(field->containing_type()), field->index()); + } + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); +} + +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +void PrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\r\n" + "private $type$ $name$_ = $default$;\r\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\r\n" + "public $type$ get$capitalized_name$() { return $name$_; }\r\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\r\n" + " return result.has$capitalized_name$();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$() {\r\n" + " return result.get$capitalized_name$();\r\n" + "}\r\n" + "public Builder set$capitalized_name$($type$ value) {\r\n" + " result.has$capitalized_name$ = true;\r\n" + " result.$name$_ = value;\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.has$capitalized_name$ = false;\r\n" + " result.$name$_ = $default$;\r\n" + " return this;\r\n" + "}\r\n"); +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\r\n" + " set$capitalized_name$(other.get$capitalized_name$());\r\n" + "}\r\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for primitive types. +} + +void PrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(input.read$capitalized_type$());\r\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " output.write$capitalized_type$($number$, get$capitalized_name$());\r\n" + "}\r\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .compute$capitalized_type$Size($number$, get$capitalized_name$());\r\n" + "}\r\n"); +} + +string PrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$boxed_type$> $name$_ =\r\n" + " java.util.Collections.emptyList();\r\n" + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\r\n" + " return $name$_;\r\n" // note: unmodifiable list + "}\r\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return $name$_.get(index);\r\n" + "}\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\r\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n" + "public int get$capitalized_name$Count() {\r\n" + " return result.get$capitalized_name$Count();\r\n" + "}\r\n" + "public $type$ get$capitalized_name$(int index) {\r\n" + " return result.get$capitalized_name$(index);\r\n" + "}\r\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\r\n" + " result.$name$_.set(index, value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder add$capitalized_name$($type$ value) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\r\n" + " }\r\n" + " result.$name$_.add(value);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder addAll$capitalized_name$(\r\n" + " java.lang.Iterable values) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\r\n" + " }\r\n" + " super.addAll(values, result.$name$_);\r\n" + " return this;\r\n" + "}\r\n" + "public Builder clear$capitalized_name$() {\r\n" + " result.$name$_ = java.util.Collections.emptyList();\r\n" + " return this;\r\n" + "}\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\r\n" + " if (result.$name$_.isEmpty()) {\r\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\r\n" + " }\r\n" + " result.$name$_.addAll(other.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\r\n" + " result.$name$_ =\r\n" + " java.util.Collections.unmodifiableList(result.$name$_);\r\n" + "}\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "add$capitalized_name$(input.read$capitalized_type$());\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " output.write$capitalized_type$($number$, element);\r\n" + "}\r\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\r\n" + " size += com.google.protobuf.CodedOutputStream\r\n" + " .compute$capitalized_type$Size($number$, element);\r\n" + "}\r\n"); +} + +string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.h b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h new file mode 100644 index 00000000..80bf2508 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.h @@ -0,0 +1,85 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CSHARP_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/compiler/csharp/csharp_service.cc b/src/google/protobuf/compiler/csharp/csharp_service.cc new file mode 100644 index 00000000..f9e4faf3 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_service.cc @@ -0,0 +1,226 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace csharp { + +ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor) + : descriptor_(descriptor) {} + +ServiceGenerator::~ServiceGenerator() {} + +void ServiceGenerator::Generate(io::Printer* printer) { + bool is_own_file = descriptor_->file()->options().csharp_multiple_files(); + printer->Print( + "public $static$ abstract class $classname$\r\n" + " implements com.google.protobuf.Service {\r\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + // Generate abstract method declarations. + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["name"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "public abstract void $name$(\r\n" + " com.google.protobuf.RpcController controller,\r\n" + " $input$ request,\r\n" + " com.google.protobuf.RpcCallback<$output$> done);\r\n"); + } + + // Generate getDescriptor() and getDescriptorForType(). + printer->Print( + "\r\n" + "public static final\r\n" + " com.google.protobuf.Descriptors.ServiceDescriptor\r\n" + " getDescriptor() {\r\n" + " return $file$.getDescriptor().getServices().get($index$);\r\n" + "}\r\n" + "public final com.google.protobuf.Descriptors.ServiceDescriptor\r\n" + " getDescriptorForType() {\r\n" + " return getDescriptor();\r\n" + "}\r\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + + // Generate more stuff. + GenerateCallMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + GenerateStub(printer); + + printer->Outdent(); + printer->Print("}\r\n\r\n"); +} + +void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { + printer->Print( + "\r\n" + "public final void callMethod(\r\n" + " com.google.protobuf.Descriptors.MethodDescriptor method,\r\n" + " com.google.protobuf.RpcController controller,\r\n" + " com.google.protobuf.Message request,\r\n" + " com.google.protobuf.RpcCallback<\r\n" + " com.google.protobuf.Message> done) {\r\n" + " if (method.getService() != getDescriptor()) {\r\n" + " throw new java.lang.IllegalArgumentException(\r\n" + " \"Service.callMethod() given method descriptor for wrong \" +\r\n" + " \"service type.\");\r\n" + " }\r\n" + " switch(method.getIndex()) {\r\n"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "case $index$:\r\n" + " this.$method$(controller, ($input$)request,\r\n" + " com.google.protobuf.RpcUtil.<$output$>specializeCallback(\r\n" + " done));\r\n" + " return;\r\n"); + } + + printer->Print( + "default:\r\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\r\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\r\n" + "}\r\n" + "\r\n"); +} + +void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, + io::Printer* printer) { + printer->Print( + "public final com.google.protobuf.Message\r\n" + " get$request_or_response$Prototype(\r\n" + " com.google.protobuf.Descriptors.MethodDescriptor method) {\r\n" + " if (method.getService() != getDescriptor()) {\r\n" + " throw new java.lang.IllegalArgumentException(\r\n" + " \"Service.get$request_or_response$Prototype() given method \" +\r\n" + " \"descriptor for wrong service type.\");\r\n" + " }\r\n" + " switch(method.getIndex()) {\r\n", + "request_or_response", (which == REQUEST) ? "Request" : "Response"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["type"] = ClassName( + (which == REQUEST) ? method->input_type() : method->output_type()); + printer->Print(vars, + "case $index$:\r\n" + " return $type$.getDefaultInstance();\r\n"); + } + + printer->Print( + "default:\r\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\r\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\r\n" + "}\r\n" + "\r\n"); +} + +void ServiceGenerator::GenerateStub(io::Printer* printer) { + printer->Print( + "public static Stub newStub(\r\n" + " com.google.protobuf.RpcChannel channel) {\r\n" + " return new Stub(channel);\r\n" + "}\r\n" + "\r\n" + "public static final class Stub extends $classname$ {\r\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + printer->Print( + "private Stub(com.google.protobuf.RpcChannel channel) {\r\n" + " this.channel = channel;\r\n" + "}\r\n" + "\r\n" + "private final com.google.protobuf.RpcChannel channel;\r\n" + "\r\n" + "public com.google.protobuf.RpcChannel getChannel() {\r\n" + " return channel;\r\n" + "}\r\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "\r\n" + "public void $method$(\r\n" + " com.google.protobuf.RpcController controller,\r\n" + " $input$ request,\r\n" + " com.google.protobuf.RpcCallback<$output$> done) {\r\n" + " channel.callMethod(\r\n" + " getDescriptor().getMethods().get($index$),\r\n" + " controller,\r\n" + " request,\r\n" + " $output$.getDefaultInstance(),\r\n" + " com.google.protobuf.RpcUtil.generalizeCallback(\r\n" + " done,\r\n" + " $output$.class,\r\n" + " $output$.getDefaultInstance()));\r\n" + "}\r\n"); + } + + printer->Outdent(); + printer->Print("}\r\n"); +} + +} // namespace csharp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/csharp/csharp_service.h b/src/google/protobuf/compiler/csharp/csharp_service.h new file mode 100644 index 00000000..524c4801 --- /dev/null +++ b/src/google/protobuf/compiler/csharp/csharp_service.h @@ -0,0 +1,67 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jonskeet@google.com (Jon Skeet) +// Following the Java generator by kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CSHARP_SERVICE_H__ +#define GOOGLE_PROTOBUF_COMPILER_CSHARP_SERVICE_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace csharp { + +class ServiceGenerator { + public: + explicit ServiceGenerator(const ServiceDescriptor* descriptor); + ~ServiceGenerator(); + + void Generate(io::Printer* printer); + + private: + // Generate the implementation of Service.callMethod(). + void GenerateCallMethod(io::Printer* printer); + + // Generate the implementations of Service.get{Request,Response}Prototype(). + enum RequestOrResponse { REQUEST, RESPONSE }; + void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); + + // Generate a stub implementation of the service. + void GenerateStub(io::Printer* printer); + + const ServiceDescriptor* descriptor_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; + +} // namespace csharp +} // namespace compiler +} // namespace protobuf + +#endif // NET_PROTO2_COMPILER_CSHARP_SERVICE_H__ +} // namespace google diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc new file mode 100644 index 00000000..62089cdc --- /dev/null +++ b/src/google/protobuf/compiler/importer.cc @@ -0,0 +1,406 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +#ifdef _WIN32 +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#endif + +MultiFileErrorCollector::~MultiFileErrorCollector() {} + +// This class serves two purposes: +// - It implements the ErrorCollector interface (used by Tokenizer and Parser) +// in terms of MultiFileErrorCollector, using a particular filename. +// - It lets us check if any errors have occurred. +class SourceTreeDescriptorDatabase::SingleFileErrorCollector + : public io::ErrorCollector { + public: + SingleFileErrorCollector(const string& filename, + MultiFileErrorCollector* multi_file_error_collector) + : filename_(filename), + multi_file_error_collector_(multi_file_error_collector), + had_errors_(false) {} + ~SingleFileErrorCollector() {} + + bool had_errors() { return had_errors_; } + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + if (multi_file_error_collector_ != NULL) { + multi_file_error_collector_->AddError(filename_, line, column, message); + } + had_errors_ = true; + } + + private: + string filename_; + MultiFileErrorCollector* multi_file_error_collector_; + bool had_errors_; +}; + +// =================================================================== + +SourceTreeDescriptorDatabase::SourceTreeDescriptorDatabase( + SourceTree* source_tree) + : source_tree_(source_tree), + error_collector_(NULL), + using_validation_error_collector_(false), + validation_error_collector_(this) {} + +SourceTreeDescriptorDatabase::~SourceTreeDescriptorDatabase() {} + +bool SourceTreeDescriptorDatabase::FindFileByName( + const string& filename, FileDescriptorProto* output) { + scoped_ptr input(source_tree_->Open(filename)); + if (input == NULL) { + if (error_collector_ != NULL) { + error_collector_->AddError(filename, -1, 0, "File not found."); + } + return false; + } + + // Set up the tokenizer and parser. + SingleFileErrorCollector file_error_collector(filename, error_collector_); + io::Tokenizer tokenizer(input.get(), &file_error_collector); + + Parser parser; + if (error_collector_ != NULL) { + parser.RecordErrorsTo(&file_error_collector); + } + if (using_validation_error_collector_) { + parser.RecordSourceLocationsTo(&source_locations_); + } + + // Parse it. + output->set_name(filename); + return parser.Parse(&tokenizer, output) && + !file_error_collector.had_errors(); +} + +bool SourceTreeDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, FileDescriptorProto* output) { + return false; +} + +bool SourceTreeDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, int field_number, + FileDescriptorProto* output) { + return false; +} + +// ------------------------------------------------------------------- + +SourceTreeDescriptorDatabase::ValidationErrorCollector:: +ValidationErrorCollector(SourceTreeDescriptorDatabase* owner) + : owner_(owner) {} + +SourceTreeDescriptorDatabase::ValidationErrorCollector:: +~ValidationErrorCollector() {} + +void SourceTreeDescriptorDatabase::ValidationErrorCollector::AddError( + const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message) { + if (owner_->error_collector_ == NULL) return; + + int line, column; + owner_->source_locations_.Find(descriptor, location, &line, &column); + owner_->error_collector_->AddError(filename, line, column, message); +} + +// =================================================================== + +Importer::Importer(SourceTree* source_tree, + MultiFileErrorCollector* error_collector) + : database_(source_tree), + pool_(&database_, database_.GetValidationErrorCollector()) { + database_.RecordErrorsTo(error_collector); +} + +Importer::~Importer() {} + +const FileDescriptor* Importer::Import(const string& filename) { + return pool_.FindFileByName(filename); +} + +// =================================================================== + +SourceTree::~SourceTree() {} + +DiskSourceTree::DiskSourceTree() {} + +DiskSourceTree::~DiskSourceTree() {} + +static inline char LastChar(const string& str) { + return str[str.size() - 1]; +} + +// Given a path, returns an equivalent path with these changes: +// - On Windows, any backslashes are replaced with forward slashes. +// - Any instances of the directory "." are removed. +// - Any consecutive '/'s are collapsed into a single slash. +// Note that the resulting string may be empty. +// +// TODO(kenton): It would be nice to handle "..", e.g. so that we can figure +// out that "foo/bar.proto" is inside "baz/../foo". However, if baz is a +// symlink or doesn't exist, then things get complicated, and we can't +// actually determine this without investigating the filesystem, probably +// in non-portable ways. So, we punt. +// +// TODO(kenton): It would be nice to use realpath() here except that it +// resolves symbolic links. This could cause problems if people place +// symbolic links in their source tree. For example, if you executed: +// protoc --proto_path=foo foo/bar/baz.proto +// then if foo/bar is a symbolic link, foo/bar/baz.proto will canonicalize +// to a path which does not appear to be under foo, and thus the compiler +// will complain that baz.proto is not inside the --proto_path. +static string CanonicalizePath(string path) { +#ifdef _WIN32 + // The Win32 API accepts forward slashes as a path delimiter even though + // backslashes are standard. Let's avoid confusion and use only forward + // slashes. + path = StringReplace(path, "\\", "/", true); +#endif + + vector parts; + vector canonical_parts; + SplitStringUsing(path, "/", &parts); // Note: Removes empty parts. + for (int i = 0; i < parts.size(); i++) { + if (parts[i] == ".") { + // Ignore. + } else { + canonical_parts.push_back(parts[i]); + } + } + string result = JoinStrings(canonical_parts, "/"); + if (!path.empty() && path[0] == '/') { + // Restore leading slash. + result = '/' + result; + } + if (!path.empty() && LastChar(path) == '/' && + !result.empty() && LastChar(result) != '/') { + // Restore trailing slash. + result += '/'; + } + return result; +} + +static inline bool ContainsParentReference(const string& path) { + return path == ".." || + HasPrefixString(path, "../") || + HasSuffixString(path, "/..") || + path.find("/../") != string::npos; +} + +// Maps a file from an old location to a new one. Typically, old_prefix is +// a virtual path and new_prefix is its corresponding disk path. Returns +// false if the filename did not start with old_prefix, otherwise replaces +// old_prefix with new_prefix and stores the result in *result. Examples: +// string result; +// assert(ApplyMapping("foo/bar", "", "baz", &result)); +// assert(result == "baz/foo/bar"); +// +// assert(ApplyMapping("foo/bar", "foo", "baz", &result)); +// assert(result == "baz/bar"); +// +// assert(ApplyMapping("foo", "foo", "bar", &result)); +// assert(result == "bar"); +// +// assert(!ApplyMapping("foo/bar", "baz", "qux", &result)); +// assert(!ApplyMapping("foo/bar", "baz", "qux", &result)); +// assert(!ApplyMapping("foobar", "foo", "baz", &result)); +static bool ApplyMapping(const string& filename, + const string& old_prefix, + const string& new_prefix, + string* result) { + if (old_prefix.empty()) { + // old_prefix matches any relative path. + if (ContainsParentReference(filename)) { + // We do not allow the file name to use "..". + return false; + } + if (HasPrefixString(filename, "/")) { + // This is an absolute path, so it isn't matched by the empty string. + return false; + } + result->assign(new_prefix); + if (!result->empty()) result->push_back('/'); + result->append(filename); + return true; + } else if (HasPrefixString(filename, old_prefix)) { + // old_prefix is a prefix of the filename. Is it the whole filename? + if (filename.size() == old_prefix.size()) { + // Yep, it's an exact match. + *result = new_prefix; + return true; + } else { + // Not an exact match. Is the next character a '/'? Otherwise, + // this isn't actually a match at all. E.g. the prefix "foo/bar" + // does not match the filename "foo/barbaz". + int after_prefix_start = -1; + if (filename[old_prefix.size()] == '/') { + after_prefix_start = old_prefix.size() + 1; + } else if (filename[old_prefix.size() - 1] == '/') { + // old_prefix is never empty, and canonicalized paths never have + // consecutive '/' characters. + after_prefix_start = old_prefix.size(); + } + if (after_prefix_start != -1) { + // Yep. So the prefixes are directories and the filename is a file + // inside them. + string after_prefix = filename.substr(after_prefix_start); + if (ContainsParentReference(after_prefix)) { + // We do not allow the file name to use "..". + return false; + } + result->assign(new_prefix); + if (!result->empty()) result->push_back('/'); + result->append(after_prefix); + return true; + } + } + } + + return false; +} + +void DiskSourceTree::MapPath(const string& virtual_path, + const string& disk_path) { + mappings_.push_back(Mapping(virtual_path, CanonicalizePath(disk_path))); +} + +DiskSourceTree::DiskFileToVirtualFileResult +DiskSourceTree::DiskFileToVirtualFile( + const string& disk_file, + string* virtual_file, + string* shadowing_disk_file) { + int mapping_index = -1; + string canonical_disk_file = CanonicalizePath(disk_file); + + for (int i = 0; i < mappings_.size(); i++) { + // Apply the mapping in reverse. + if (ApplyMapping(canonical_disk_file, mappings_[i].disk_path, + mappings_[i].virtual_path, virtual_file)) { + // Success. + mapping_index = i; + break; + } + } + + if (mapping_index == -1) { + return NO_MAPPING; + } + + // Iterate through all mappings with higher precedence and verify that none + // of them map this file to some other existing file. + for (int i = 0; i < mapping_index; i++) { + if (ApplyMapping(*virtual_file, mappings_[i].virtual_path, + mappings_[i].disk_path, shadowing_disk_file)) { + if (access(shadowing_disk_file->c_str(), F_OK) >= 0) { + // File exists. + return SHADOWED; + } + } + } + shadowing_disk_file->clear(); + + // Verify that we can open the file. Note that this also has the side-effect + // of verifying that we are not canonicalizing away any non-existent + // directories. + scoped_ptr stream(OpenDiskFile(disk_file)); + if (stream == NULL) { + return CANNOT_OPEN; + } + + return SUCCESS; +} + +io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) { + if (filename != CanonicalizePath(filename) || + ContainsParentReference(filename)) { + // We do not allow importing of paths containing things like ".." or + // consecutive slashes since the compiler expects files to be uniquely + // identified by file name. + return NULL; + } + + for (int i = 0; i < mappings_.size(); i++) { + string disk_file; + if (ApplyMapping(filename, mappings_[i].virtual_path, + mappings_[i].disk_path, &disk_file)) { + io::ZeroCopyInputStream* stream = OpenDiskFile(disk_file); + if (stream != NULL) return stream; + + if (errno == EACCES) { + // The file exists but is not readable. + // TODO(kenton): Find a way to report this more nicely. + GOOGLE_LOG(WARNING) << "Read access is denied for file: " << disk_file; + return NULL; + } + } + } + + return NULL; +} + +io::ZeroCopyInputStream* DiskSourceTree::OpenDiskFile( + const string& filename) { + int file_descriptor; + do { + file_descriptor = open(filename.c_str(), O_RDONLY); + } while (file_descriptor < 0 && errno == EINTR); + if (file_descriptor >= 0) { + io::FileInputStream* result = new io::FileInputStream(file_descriptor); + result->SetCloseOnDelete(true); + return result; + } else { + return NULL; + } +} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h new file mode 100644 index 00000000..2e3d0df4 --- /dev/null +++ b/src/google/protobuf/compiler/importer.h @@ -0,0 +1,279 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file is the public interface to the .proto file parser. + +#ifndef GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ +#define GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace io { class ZeroCopyInputStream; } + +namespace compiler { + +// Defined in this file. +class Importer; +class MultiFileErrorCollector; +class SourceTree; +class DiskSourceTree; + +// TODO(kenton): Move all SourceTree stuff to a separate file? + +// An implementation of DescriptorDatabase which loads files from a SourceTree +// and parses them. +// +// Note: This class is not thread-safe since it maintains a table of source +// code locations for error reporting. However, when a DescriptorPool wraps +// a DescriptorDatabase, it uses mutex locking to make sure only one method +// of the database is called at a time, even if the DescriptorPool is used +// from multiple threads. Therefore, there is only a problem if you create +// multiple DescriptorPools wrapping the same SourceTreeDescriptorDatabase +// and use them from multiple threads. +// +// Note: This class does not implement FindFileContainingSymbol() or +// FindFileContainingExtension(); these will always return false. +class LIBPROTOBUF_EXPORT SourceTreeDescriptorDatabase : public DescriptorDatabase { + public: + SourceTreeDescriptorDatabase(SourceTree* source_tree); + ~SourceTreeDescriptorDatabase(); + + // Instructs the SourceTreeDescriptorDatabase to report any parse errors + // to the given MultiFileErrorCollector. This should be called before + // parsing. error_collector must remain valid until either this method + // is called again or the SourceTreeDescriptorDatabase is destroyed. + void RecordErrorsTo(MultiFileErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Gets a DescriptorPool::ErrorCollector which records errors to the + // MultiFileErrorCollector specified with RecordErrorsTo(). This collector + // has the ability to determine exact line and column numbers of errors + // from the information given to it by the DescriptorPool. + DescriptorPool::ErrorCollector* GetValidationErrorCollector() { + using_validation_error_collector_ = true; + return &validation_error_collector_; + } + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + class SingleFileErrorCollector; + + SourceTree* source_tree_; + MultiFileErrorCollector* error_collector_; + + class LIBPROTOBUF_EXPORT ValidationErrorCollector : public DescriptorPool::ErrorCollector { + public: + ValidationErrorCollector(SourceTreeDescriptorDatabase* owner); + ~ValidationErrorCollector(); + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message); + + private: + SourceTreeDescriptorDatabase* owner_; + }; + friend class ValidationErrorCollector; + + bool using_validation_error_collector_; + SourceLocationTable source_locations_; + ValidationErrorCollector validation_error_collector_; +}; + +// Simple interface for parsing .proto files. This wraps the process +// of opening the file, parsing it with a Parser, recursively parsing all its +// imports, and then cross-linking the results to produce a FileDescriptor. +// +// This is really just a thin wrapper around SourceTreeDescriptorDatabase. +// You may find that SourceTreeDescriptorDatabase is more flexible. +// +// TODO(kenton): I feel like this class is not well-named. +class LIBPROTOBUF_EXPORT Importer { + public: + Importer(SourceTree* source_tree, + MultiFileErrorCollector* error_collector); + ~Importer(); + + // Import the given file and build a FileDescriptor representing it. If + // the file is already in the DescriptorPool, the existing FileDescriptor + // will be returned. The FileDescriptor is property of the DescriptorPool, + // and will remain valid until it is destroyed. If any errors occur, they + // will be reported using the error collector and Import() will return NULL. + // + // A particular Importer object will only report errors for a particular + // file once. All future attempts to import the same file will return NULL + // without reporting any errors. The idea is that you might want to import + // a lot of files without seeing the same errors over and over again. If + // you want to see errors for the same files repeatedly, you can use a + // separate Importer object to import each one (but use the same + // DescriptorPool so that they can be cross-linked). + const FileDescriptor* Import(const string& filename); + + // The DescriptorPool in which all imported FileDescriptors and their + // contents are stored. + inline const DescriptorPool* pool() const { + return &pool_; + } + + private: + SourceTreeDescriptorDatabase database_; + DescriptorPool pool_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer); +}; + +// If the importer encounters problems while trying to import the proto files, +// it reports them to a MultiFileErrorCollector. +class LIBPROTOBUF_EXPORT MultiFileErrorCollector { + public: + inline MultiFileErrorCollector() {} + virtual ~MultiFileErrorCollector(); + + // Line and column numbers are zero-based. A line number of -1 indicates + // an error with the entire file (e.g. "not found"). + virtual void AddError(const string& filename, int line, int column, + const string& message) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MultiFileErrorCollector); +}; + +// Abstract interface which represents a directory tree containing proto files. +// Used by the default implementation of Importer to resolve import statements +// Most users will probably want to use the DiskSourceTree implementation, +// below. +class LIBPROTOBUF_EXPORT SourceTree { + public: + inline SourceTree() {} + virtual ~SourceTree(); + + // Open the given file and return a stream that reads it, or NULL if not + // found. The caller takes ownership of the returned object. The filename + // must be a path relative to the root of the source tree and must not + // contain "." or ".." components. + virtual io::ZeroCopyInputStream* Open(const string& filename) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SourceTree); +}; + +// An implementation of SourceTree which loads files from locations on disk. +// Multiple mappings can be set up to map locations in the DiskSourceTree to +// locations in the physical filesystem. +class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { + public: + DiskSourceTree(); + ~DiskSourceTree(); + + // Map a path on disk to a location in the SourceTree. The path may be + // either a file or a directory. If it is a directory, the entire tree + // under it will be mapped to the given virtual location. To map a directory + // to the root of the source tree, pass an empty string for virtual_path. + // + // If multiple mapped paths apply when opening a file, they will be searched + // in order. For example, if you do: + // MapPath("bar", "foo/bar"); + // MapPath("", "baz"); + // and then you do: + // Open("bar/qux"); + // the DiskSourceTree will first try to open foo/bar/qux, then baz/bar/qux, + // returning the first one that opens successfuly. + // + // disk_path may be an absolute path or relative to the current directory, + // just like a path you'd pass to open(). + void MapPath(const string& virtual_path, const string& disk_path); + + // Return type for DiskFileToVirtualFile(). + enum DiskFileToVirtualFileResult { + SUCCESS, + SHADOWED, + CANNOT_OPEN, + NO_MAPPING + }; + + // Given a path to a file on disk, find a virtual path mapping to that + // file. The first mapping created with MapPath() whose disk_path contains + // the filename is used. However, that virtual path may not actually be + // usable to open the given file. Possible return values are: + // * SUCCESS: The mapping was found. *virtual_file is filled in so that + // calling Open(*virtual_file) will open the file named by disk_file. + // * SHADOWED: A mapping was found, but using Open() to open this virtual + // path will end up returning some different file. This is because some + // other mapping with a higher precedence also matches this virtual path + // and maps it to a different file that exists on disk. *virtual_file + // is filled in as it would be in the SUCCESS case. *shadowing_disk_file + // is filled in with the disk path of the file which would be opened if + // you were to call Open(*virtual_file). + // * CANNOT_OPEN: The mapping was found and was not shadowed, but the + // file specified cannot be opened. When this value is returned, + // errno will indicate the reason the file cannot be opened. *virtual_file + // will be set to the virtual path as in the SUCCESS case, even though + // it is not useful. + // * NO_MAPPING: Indicates that no mapping was found which contains this + // file. + DiskFileToVirtualFileResult + DiskFileToVirtualFile(const string& disk_file, + string* virtual_file, + string* shadowing_disk_file); + + // implements SourceTree ------------------------------------------- + io::ZeroCopyInputStream* Open(const string& filename); + + private: + struct Mapping { + string virtual_path; + string disk_path; + + inline Mapping(const string& virtual_path, const string& disk_path) + : virtual_path(virtual_path), disk_path(disk_path) {} + }; + vector mappings_; + + // Like Open() but given the actual on-disk path. + io::ZeroCopyInputStream* OpenDiskFile(const string& filename); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DiskSourceTree); +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_IMPORTER_H__ diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc new file mode 100644 index 00000000..55c52e47 --- /dev/null +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -0,0 +1,546 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +namespace { + +#define EXPECT_SUBSTRING(needle, haystack) \ + EXPECT_PRED_FORMAT2(testing::IsSubstring, (needle), (haystack)) + +class MockErrorCollector : public MultiFileErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, int line, int column, + const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1:$2: $3\n", + filename, line, column, message); + } +}; + +// ------------------------------------------------------------------- + +// A dummy implementation of SourceTree backed by a simple map. +class MockSourceTree : public SourceTree { + public: + MockSourceTree() {} + ~MockSourceTree() {} + + void AddFile(const string& name, const char* contents) { + files_[name] = contents; + } + + // implements SourceTree ------------------------------------------- + io::ZeroCopyInputStream* Open(const string& filename) { + const char* contents = FindPtrOrNull(files_, filename); + if (contents == NULL) { + return NULL; + } else { + return new io::ArrayInputStream(contents, strlen(contents)); + } + } + + private: + hash_map files_; +}; + +// =================================================================== + +class ImporterTest : public testing::Test { + protected: + ImporterTest() + : importer_(&source_tree_, &error_collector_) {} + + void AddFile(const string& filename, const char* text) { + source_tree_.AddFile(filename, text); + } + + // Return the collected error text + string error() const { return error_collector_.text_; } + + MockErrorCollector error_collector_; + MockSourceTree source_tree_; + Importer importer_; +}; + +TEST_F(ImporterTest, Import) { + // Test normal importing. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "message Foo {}\n"); + + const FileDescriptor* file = importer_.Import("foo.proto"); + EXPECT_EQ("", error_collector_.text_); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->message_type_count()); + EXPECT_EQ("Foo", file->message_type(0)->name()); + + // Importing again should return same object. + EXPECT_EQ(file, importer_.Import("foo.proto")); +} + +TEST_F(ImporterTest, ImportNested) { + // Test that importing a file which imports another file works. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n" + "message Foo {\n" + " optional Bar bar = 1;\n" + "}\n"); + AddFile("bar.proto", + "syntax = \"proto2\";\n" + "message Bar {}\n"); + + // Note that both files are actually parsed by the first call to Import() + // here, since foo.proto imports bar.proto. The second call just returns + // the same ProtoFile for bar.proto which was constructed while importing + // foo.proto. We test that this is the case below by checking that bar + // is among foo's dependencies (by pointer). + const FileDescriptor* foo = importer_.Import("foo.proto"); + const FileDescriptor* bar = importer_.Import("bar.proto"); + EXPECT_EQ("", error_collector_.text_); + ASSERT_TRUE(foo != NULL); + ASSERT_TRUE(bar != NULL); + + // Check that foo's dependency is the same object as bar. + ASSERT_EQ(1, foo->dependency_count()); + EXPECT_EQ(bar, foo->dependency(0)); + + // Check that foo properly cross-links bar. + ASSERT_EQ(1, foo->message_type_count()); + ASSERT_EQ(1, bar->message_type_count()); + ASSERT_EQ(1, foo->message_type(0)->field_count()); + ASSERT_EQ(FieldDescriptor::TYPE_MESSAGE, + foo->message_type(0)->field(0)->type()); + EXPECT_EQ(bar->message_type(0), + foo->message_type(0)->field(0)->message_type()); +} + +TEST_F(ImporterTest, FileNotFound) { + // Error: Parsing a file that doesn't exist. + EXPECT_TRUE(importer_.Import("foo.proto") == NULL); + EXPECT_EQ( + "foo.proto:-1:0: File not found.\n", + error_collector_.text_); +} + +TEST_F(ImporterTest, ImportNotFound) { + // Error: Importing a file that doesn't exist. + AddFile("foo.proto", + "syntax = \"proto2\";\n" + "import \"bar.proto\";\n"); + + EXPECT_TRUE(importer_.Import("foo.proto") == NULL); + EXPECT_EQ( + "bar.proto:-1:0: File not found.\n" + "foo.proto:-1:0: Import \"bar.proto\" was not found or had errors.\n", + error_collector_.text_); +} + +TEST_F(ImporterTest, RecursiveImport) { + // Error: Recursive import. + AddFile("recursive1.proto", + "syntax = \"proto2\";\n" + "import \"recursive2.proto\";\n"); + AddFile("recursive2.proto", + "syntax = \"proto2\";\n" + "import \"recursive1.proto\";\n"); + + EXPECT_TRUE(importer_.Import("recursive1.proto") == NULL); + EXPECT_EQ( + "recursive1.proto:-1:0: File recursively imports itself: recursive1.proto " + "-> recursive2.proto -> recursive1.proto\n" + "recursive2.proto:-1:0: Import \"recursive1.proto\" was not found " + "or had errors.\n" + "recursive1.proto:-1:0: Import \"recursive2.proto\" was not found " + "or had errors.\n", + error_collector_.text_); +} + +// TODO(sanjay): The MapField tests below more properly belong in +// descriptor_unittest, but are more convenient to test here. +TEST_F(ImporterTest, MapFieldValid) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + const FileDescriptor* file = importer_.Import("map.proto"); + ASSERT_TRUE(file != NULL) << error_collector_.text_; + EXPECT_EQ("", error_collector_.text_); + + // Check that Map::items points to Item::key + const Descriptor* item_type = file->FindMessageTypeByName("Item"); + ASSERT_TRUE(item_type != NULL); + const Descriptor* map_type = file->FindMessageTypeByName("Map"); + ASSERT_TRUE(map_type != NULL); + const FieldDescriptor* key_field = item_type->FindFieldByName("key"); + ASSERT_TRUE(key_field != NULL); + const FieldDescriptor* items_field = map_type->FindFieldByName("items"); + ASSERT_TRUE(items_field != NULL); + EXPECT_EQ(items_field->experimental_map_key(), key_field); +} + +TEST_F(ImporterTest, MapFieldNotRepeated) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " required Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("only allowed for repeated fields", error()); +} + +TEST_F(ImporterTest, MapFieldNotMessageType) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Map {\n" + " repeated int32 items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("only allowed for fields with a message type", error()); +} + +TEST_F(ImporterTest, MapFieldTypeNotFound) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Map {\n" + " repeated Unknown items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("not defined", error()); +} + +TEST_F(ImporterTest, MapFieldKeyNotFound) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " required string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"badkey\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("Could not find field", error()); +} + +TEST_F(ImporterTest, MapFieldKeyRepeated) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message Item {\n" + " repeated string key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("must not name a repeated field", error()); +} + +TEST_F(ImporterTest, MapFieldKeyNotScalar) { + AddFile( + "map.proto", + "syntax = \"proto2\";\n" + "message ItemKey { }\n" + "message Item {\n" + " required ItemKey key = 1;\n" + "}\n" + "message Map {\n" + " repeated Item items = 1 [experimental_map_key = \"key\"];\n" + "}\n" + ); + EXPECT_TRUE(importer_.Import("map.proto") == NULL); + EXPECT_SUBSTRING("must name a scalar or string", error()); +} + +// =================================================================== + +class DiskSourceTreeTest : public testing::Test { + protected: + virtual void SetUp() { + dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_1"); + dirnames_.push_back(TestTempDir() + "/test_proto2_import_path_2"); + + for (int i = 0; i < dirnames_.size(); i++) { + if (File::Exists(dirnames_[i])) { + File::DeleteRecursively(dirnames_[i], NULL, NULL); + } + GOOGLE_CHECK(File::CreateDir(dirnames_[i].c_str(), DEFAULT_FILE_MODE)); + } + } + + virtual void TearDown() { + for (int i = 0; i < dirnames_.size(); i++) { + File::DeleteRecursively(dirnames_[i], NULL, NULL); + } + } + + void AddFile(const string& filename, const char* contents) { + File::WriteStringToFileOrDie(contents, filename); + } + + void AddSubdir(const string& dirname) { + GOOGLE_CHECK(File::CreateDir(dirname.c_str(), DEFAULT_FILE_MODE)); + } + + void ExpectFileContents(const string& filename, + const char* expected_contents) { + scoped_ptr input(source_tree_.Open(filename)); + + ASSERT_FALSE(input == NULL); + + // Read all the data from the file. + string file_contents; + const void* data; + int size; + while (input->Next(&data, &size)) { + file_contents.append(reinterpret_cast(data), size); + } + + EXPECT_EQ(expected_contents, file_contents); + } + + void ExpectFileNotFound(const string& filename) { + scoped_ptr input(source_tree_.Open(filename)); + EXPECT_TRUE(input == NULL); + } + + DiskSourceTree source_tree_; + + // Paths of two on-disk directories to use during the test. + vector dirnames_; +}; + +TEST_F(DiskSourceTreeTest, MapRoot) { + // Test opening a file in a directory that is mapped to the root of the + // source tree. + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("", dirnames_[0]); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileNotFound("bar"); +} + +TEST_F(DiskSourceTreeTest, MapDirectory) { + // Test opening a file in a directory that is mapped to somewhere other + // than the root of the source tree. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("baz", dirnames_[0]); + + ExpectFileContents("baz/foo", "Hello World!"); + ExpectFileNotFound("baz/bar"); + ExpectFileNotFound("foo"); + ExpectFileNotFound("bar"); + + // Non-canonical file names should not work. + ExpectFileNotFound("baz//foo"); + ExpectFileNotFound("baz/../baz/foo"); + ExpectFileNotFound("baz/./foo"); + ExpectFileNotFound("baz/foo/"); +} + +TEST_F(DiskSourceTreeTest, NoParent) { + // Test that we cannot open files in a parent of a mapped directory. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddSubdir(dirnames_[0] + "/bar"); + AddFile(dirnames_[0] + "/bar/baz", "Blah."); + source_tree_.MapPath("", dirnames_[0] + "/bar"); + + ExpectFileContents("baz", "Blah."); + ExpectFileNotFound("../foo"); + ExpectFileNotFound("../bar/baz"); +} + +TEST_F(DiskSourceTreeTest, MapFile) { + // Test opening a file that is mapped directly into the source tree. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + source_tree_.MapPath("foo", dirnames_[0] + "/foo"); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileNotFound("bar"); +} + +TEST_F(DiskSourceTreeTest, SearchMultipleDirectories) { + // Test mapping and searching multiple directories. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + AddFile(dirnames_[1] + "/bar", "Goodbye World!"); + source_tree_.MapPath("", dirnames_[0]); + source_tree_.MapPath("", dirnames_[1]); + + ExpectFileContents("foo", "Hello World!"); + ExpectFileContents("bar", "Goodbye World!"); + ExpectFileNotFound("baz"); +} + +TEST_F(DiskSourceTreeTest, OrderingTrumpsSpecificity) { + // Test that directories are always searched in order, even when a latter + // directory is more-specific than a former one. + + // Create the "bar" directory so we can put a file in it. + ASSERT_TRUE(File::CreateDir((dirnames_[0] + "/bar").c_str(), + DEFAULT_FILE_MODE)); + + // Add files and map paths. + AddFile(dirnames_[0] + "/bar/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + source_tree_.MapPath("", dirnames_[0]); + source_tree_.MapPath("bar", dirnames_[1]); + + // Check. + ExpectFileContents("bar/foo", "Hello World!"); +} + +TEST_F(DiskSourceTreeTest, DiskFileToVirtualFile) { + // Test DiskFileToVirtualFile. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + source_tree_.MapPath("bar", dirnames_[0]); + source_tree_.MapPath("bar", dirnames_[1]); + + string virtual_file; + string shadowing_disk_file; + + EXPECT_EQ(DiskSourceTree::NO_MAPPING, + source_tree_.DiskFileToVirtualFile( + "/foo", &virtual_file, &shadowing_disk_file)); + + EXPECT_EQ(DiskSourceTree::SHADOWED, + source_tree_.DiskFileToVirtualFile( + dirnames_[1] + "/foo", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/foo", virtual_file); + EXPECT_EQ(dirnames_[0] + "/foo", shadowing_disk_file); + + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + dirnames_[1] + "/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/baz", virtual_file); + + EXPECT_EQ(DiskSourceTree::SUCCESS, + source_tree_.DiskFileToVirtualFile( + dirnames_[0] + "/foo", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("bar/foo", virtual_file); +} + +TEST_F(DiskSourceTreeTest, DiskFileToVirtualFileCanonicalization) { + // Test handling of "..", ".", etc. in DiskFileToVirtualFile(). + + source_tree_.MapPath("dir1", ".."); + source_tree_.MapPath("dir2", "../../foo"); + source_tree_.MapPath("dir3", "./foo/bar/."); + source_tree_.MapPath("dir4", "."); + source_tree_.MapPath("", "/qux"); + source_tree_.MapPath("dir5", "/quux/"); + + string virtual_file; + string shadowing_disk_file; + + // "../.." should not be considered to be under "..". + EXPECT_EQ(DiskSourceTree::NO_MAPPING, + source_tree_.DiskFileToVirtualFile( + "../../baz", &virtual_file, &shadowing_disk_file)); + + // But "../baz" should be. + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "../baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir1/baz", virtual_file); + + // "../../foo/baz" is under "../../foo". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "../../foo/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir2/baz", virtual_file); + + // "foo/./bar/baz" is under "./foo/bar/.". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "foo/bar/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir3/baz", virtual_file); + + // "bar" is under ".". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "bar", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir4/bar", virtual_file); + + // "/qux/baz" is under "/qux". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "/qux/baz", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("baz", virtual_file); + + // "/quux/bar" is under "/quux". + EXPECT_EQ(DiskSourceTree::CANNOT_OPEN, + source_tree_.DiskFileToVirtualFile( + "/quux/bar", &virtual_file, &shadowing_disk_file)); + EXPECT_EQ("dir5/bar", virtual_file); +} + +} // namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum.cc b/src/google/protobuf/compiler/java/java_enum.cc new file mode 100644 index 00000000..aa15a468 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -0,0 +1,189 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +EnumGenerator::EnumGenerator(const EnumDescriptor* descriptor) + : 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) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().java_multiple_files(); + printer->Print( + "public $static$ enum $classname$ {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + map vars; + vars["name"] = canonical_values_[i]->name(); + vars["index"] = SimpleItoa(canonical_values_[i]->index()); + vars["number"] = SimpleItoa(canonical_values_[i]->number()); + printer->Print(vars, + "$name$($index$, $number$),\n"); + } + + printer->Print( + ";\n" + "\n"); + + // ----------------------------------------------------------------- + + for (int i = 0; i < aliases_.size(); i++) { + map vars; + vars["classname"] = descriptor_->name(); + vars["name"] = aliases_[i].value->name(); + vars["canonical_name"] = aliases_[i].canonical_value->name(); + printer->Print(vars, + "public static final $classname$ $name$ = $canonical_name$;\n"); + } + + // ----------------------------------------------------------------- + + printer->Print( + "\n" + "public final int getNumber() { return value; }\n" + "\n" + "public static $classname$ valueOf(int value) {\n" + " switch (value) {\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < canonical_values_.size(); i++) { + printer->Print( + "case $number$: return $name$;\n", + "name", canonical_values_[i]->name(), + "number", SimpleItoa(canonical_values_[i]->number())); + } + + printer->Outdent(); + printer->Outdent(); + printer->Print( + " default: return null;\n" + " }\n" + "}\n" + "\n"); + + // ----------------------------------------------------------------- + // Reflection + + printer->Print( + "public final com.google.protobuf.Descriptors.EnumValueDescriptor\n" + " getValueDescriptor() {\n" + " return getDescriptor().getValues().get(index);\n" + "}\n" + "public final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n" + "public static final com.google.protobuf.Descriptors.EnumDescriptor\n" + " getDescriptor() {\n"); + + // TODO(kenton): Cache statically? Note that we can't access descriptors + // at module init time because it wouldn't work with descriptor.proto, but + // we can cache the value the first time getDescriptor() is called. + if (descriptor_->containing_type() == NULL) { + printer->Print( + " return $file$.getDescriptor().getEnumTypes().get($index$);\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + } else { + printer->Print( + " return $parent$.getDescriptor().getEnumTypes().get($index$);\n", + "parent", ClassName(descriptor_->containing_type()), + "index", SimpleItoa(descriptor_->index())); + } + + printer->Print( + "}\n" + "\n" + "private static final $classname$[] VALUES = {\n" + " ", + "classname", descriptor_->name()); + + for (int i = 0; i < descriptor_->value_count(); i++) { + printer->Print("$name$, ", + "name", descriptor_->value(i)->name()); + } + + printer->Print( + "\n" + "};\n" + "public static $classname$ valueOf(\n" + " com.google.protobuf.Descriptors.EnumValueDescriptor desc) {\n" + " if (desc.getType() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"EnumValueDescriptor is not for this type.\");\n" + " }\n" + " return VALUES[desc.getIndex()];\n" + "}\n", + "classname", descriptor_->name()); + + // ----------------------------------------------------------------- + + printer->Print( + "private final int index;\n" + "private final int value;\n" + "private $classname$(int index, int value) {\n" + " this.index = index;\n" + " this.value = value;\n" + "}\n", + "classname", descriptor_->name()); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum.h b/src/google/protobuf/compiler/java/java_enum.h new file mode 100644 index 00000000..f0a16036 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum.h @@ -0,0 +1,70 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class EnumGenerator { + public: + explicit EnumGenerator(const EnumDescriptor* descriptor); + ~EnumGenerator(); + + void Generate(io::Printer* printer); + + private: + const EnumDescriptor* descriptor_; + + // The proto language allows multiple enum constants to have the same numeric + // value. Java, however, does not allow multiple enum constants to be + // considered equivalent. We treat the first defined constant for any + // given numeric value as "canonical" and the rest as aliases of that + // canonical value. + vector canonical_values_; + + struct Alias { + const EnumValueDescriptor* value; + const EnumValueDescriptor* canonical_value; + }; + vector aliases_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_H__ diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc new file mode 100644 index 00000000..cb80c4b8 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field.cc @@ -0,0 +1,264 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetEnumVariables(const FieldDescriptor* descriptor, + map* variables) { + const EnumValueDescriptor* default_value; + default_value = descriptor->default_value_enum(); + + string type = ClassName(descriptor->enum_type()); + + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = type; + (*variables)["default"] = type + "." + default_value->name(); +} + +} // namespace + +// =================================================================== + +EnumFieldGenerator:: +EnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +EnumFieldGenerator::~EnumFieldGenerator() {} + +void EnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $default$;\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void EnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $default$;\n" + " return this;\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for enum types. +} + +void EnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n" + " unknownFields.mergeVarintField($number$, rawValue);\n" + "} else {\n" + " set$capitalized_name$(value);\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.writeEnum($number$, get$capitalized_name$().getNumber());\n" + "}\n"); +} + +void EnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, get$capitalized_name$().getNumber());\n" + "}\n"); +} + +string EnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +// =================================================================== + +RepeatedEnumFieldGenerator:: +RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetEnumVariables(descriptor, &variables_); +} + +RepeatedEnumFieldGenerator::~RepeatedEnumFieldGenerator() {} + +void RepeatedEnumFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "int rawValue = input.readEnum();\n" + "$type$ value = $type$.valueOf(rawValue);\n" + "if (value == null) {\n" + " unknownFields.mergeVarintField($number$, rawValue);\n" + "} else {\n" + " add$capitalized_name$(value);\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.writeEnum($number$, element.getNumber());\n" + "}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .computeEnumSize($number$, element.getNumber());\n" + "}\n"); +} + +string RepeatedEnumFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->enum_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h new file mode 100644 index 00000000..473ba617 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_enum_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class EnumFieldGenerator : public FieldGenerator { + public: + explicit EnumFieldGenerator(const FieldDescriptor* descriptor); + ~EnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumFieldGenerator); +}; + +class RepeatedEnumFieldGenerator : public FieldGenerator { + public: + explicit RepeatedEnumFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedEnumFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedEnumFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_ENUM_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_extension.cc b/src/google/protobuf/compiler/java/java_extension.cc new file mode 100644 index 00000000..1b637fab --- /dev/null +++ b/src/google/protobuf/compiler/java/java_extension.cc @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) {} + +ExtensionGenerator::~ExtensionGenerator() {} + +void ExtensionGenerator::Generate(io::Printer* printer) { + map vars; + vars["name"] = UnderscoresToCamelCase(descriptor_); + vars["containing_type"] = ClassName(descriptor_->containing_type()); + vars["index"] = SimpleItoa(descriptor_->index()); + + JavaType java_type = GetJavaType(descriptor_); + string singular_type; + switch (java_type) { + case JAVATYPE_MESSAGE: + vars["type"] = ClassName(descriptor_->message_type()); + break; + case JAVATYPE_ENUM: + vars["type"] = ClassName(descriptor_->enum_type()); + break; + default: + vars["type"] = BoxedPrimitiveTypeName(java_type); + break; + } + + if (descriptor_->is_repeated()) { + printer->Print(vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " java.util.List<$type$>> $name$ =\n" + " com.google.protobuf.GeneratedMessage\n" + " .newRepeatedGeneratedExtension(\n" + " getDescriptor().getExtensions().get($index$),\n" + " $type$.class);\n"); + } else { + printer->Print(vars, + "public static final\n" + " com.google.protobuf.GeneratedMessage.GeneratedExtension<\n" + " $containing_type$,\n" + " $type$> $name$ =\n" + " com.google.protobuf.GeneratedMessage.newGeneratedExtension(\n" + " getDescriptor().getExtensions().get($index$),\n" + " $type$.class);\n"); + } +} + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_extension.h b/src/google/protobuf/compiler/java/java_extension.h new file mode 100644 index 00000000..9088fecd --- /dev/null +++ b/src/google/protobuf/compiler/java/java_extension.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_EXTENSION_H__ + +#include + +namespace google { +namespace protobuf { + class FieldDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +// Generates code for an extension, which may be within the scope of some +// message or may be at file scope. This is much simpler than FieldGenerator +// since extensions are just simple identifiers with interesting types. +class ExtensionGenerator { + public: + explicit ExtensionGenerator(const FieldDescriptor* descriptor); + ~ExtensionGenerator(); + + void Generate(io::Printer* printer); + + private: + const FieldDescriptor* descriptor_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc new file mode 100644 index 00000000..b7197cac --- /dev/null +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -0,0 +1,88 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +FieldGenerator::~FieldGenerator() {} + +FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_( + new scoped_ptr[descriptor->field_count()]), + extension_generators_( + new scoped_ptr[descriptor->extension_count()]) { + + // Construct all the FieldGenerators. + for (int i = 0; i < descriptor->field_count(); i++) { + field_generators_[i].reset(MakeGenerator(descriptor->field(i))); + } + for (int i = 0; i < descriptor->extension_count(); i++) { + extension_generators_[i].reset(MakeGenerator(descriptor->extension(i))); + } +} + +FieldGenerator* FieldGeneratorMap::MakeGenerator(const FieldDescriptor* field) { + if (field->is_repeated()) { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new RepeatedMessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new RepeatedEnumFieldGenerator(field); + default: + return new RepeatedPrimitiveFieldGenerator(field); + } + } else { + switch (GetJavaType(field)) { + case JAVATYPE_MESSAGE: + return new MessageFieldGenerator(field); + case JAVATYPE_ENUM: + return new EnumFieldGenerator(field); + default: + return new PrimitiveFieldGenerator(field); + } + } +} + +FieldGeneratorMap::~FieldGeneratorMap() {} + +const FieldGenerator& FieldGeneratorMap::get( + const FieldDescriptor* field) const { + GOOGLE_CHECK_EQ(field->containing_type(), descriptor_); + return *field_generators_[field->index()]; +} + +const FieldGenerator& FieldGeneratorMap::get_extension(int index) const { + return *extension_generators_[index]; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h new file mode 100644 index 00000000..ef47c735 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_field.h @@ -0,0 +1,82 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class FieldGenerator { + public: + FieldGenerator() {} + virtual ~FieldGenerator(); + + virtual void GenerateMembers(io::Printer* printer) const = 0; + virtual void GenerateBuilderMembers(io::Printer* printer) const = 0; + virtual void GenerateMergingCode(io::Printer* printer) const = 0; + virtual void GenerateBuildingCode(io::Printer* printer) const = 0; + virtual void GenerateParsingCode(io::Printer* printer) const = 0; + virtual void GenerateSerializationCode(io::Printer* printer) const = 0; + virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; + + virtual string GetBoxedType() const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGenerator); +}; + +// Convenience class which constructs FieldGenerators for a Descriptor. +class FieldGeneratorMap { + public: + explicit FieldGeneratorMap(const Descriptor* descriptor); + ~FieldGeneratorMap(); + + const FieldGenerator& get(const FieldDescriptor* field) const; + const FieldGenerator& get_extension(int index) const; + + private: + const Descriptor* descriptor_; + scoped_array > field_generators_; + scoped_array > extension_generators_; + + static FieldGenerator* MakeGenerator(const FieldDescriptor* field); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldGeneratorMap); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc new file mode 100644 index 00000000..e7e8618d --- /dev/null +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -0,0 +1,249 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +FileGenerator::FileGenerator(const FileDescriptor* file) + : file_(file), + java_package_(FileJavaPackage(file)), + classname_(FileClassName(file)) {} + +FileGenerator::~FileGenerator() {} + +bool FileGenerator::Validate(string* error) { + // 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; i < file_->enum_type_count() && !found_conflict; i++) { + if (file_->enum_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->message_type_count() && !found_conflict; i++) { + if (file_->message_type(i)->name() == classname_) { + found_conflict = true; + } + } + for (int i = 0; i < file_->service_count() && !found_conflict; i++) { + if (file_->service(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" + "\n"); + if (!java_package_.empty()) { + printer->Print( + "package $package$;\n" + "\n", + "package", java_package_); + } + printer->Print( + "public final class $classname$ {\n" + " private $classname$() {}\n", + "classname", classname_); + printer->Indent(); + + // ----------------------------------------------------------------- + + // Embed the descriptor. We simply serialize the entire FileDescriptorProto + // and embed it as a string literal, which is parsed and built into real + // descriptors at initialization time. We unfortunately have to put it in + // a string literal, not a byte array, because apparently using a literal + // byte array causes the Java compiler to generate *instructions* to + // initialize each and every byte of the array, e.g. as if you typed: + // b[0] = 123; b[1] = 456; b[2] = 789; + // This makes huge bytecode files and can easily hit the compiler's internal + // code size limits (error "code to large"). String literals are apparently + // embedded raw, which is what we want. + FileDescriptorProto file_proto; + file_->CopyTo(&file_proto); + string file_data; + file_proto.SerializeToString(&file_data); + + printer->Print( + "public static com.google.protobuf.Descriptors.FileDescriptor\n" + " getDescriptor() {\n" + " return descriptor;\n" + "}\n" + "private static final com.google.protobuf.Descriptors.FileDescriptor\n" + " descriptor = buildDescriptor();\n" + "private static\n" + " com.google.protobuf.Descriptors.FileDescriptor\n" + " buildDescriptor() {\n" + " java.lang.String descriptorData =\n"); + printer->Indent(); + printer->Indent(); + + // Only write 40 bytes per line. + static const int kBytesPerLine = 40; + for (int i = 0; i < file_data.size(); i += kBytesPerLine) { + if (i > 0) printer->Print(" +\n"); + printer->Print("\"$data$\"", + "data", CEscape(file_data.substr(i, kBytesPerLine))); + } + printer->Print(";\n"); + + printer->Outdent(); + printer->Print( + "try {\n" + " return com.google.protobuf.Descriptors.FileDescriptor\n" + " .internalBuildGeneratedFileFrom(descriptorData,\n" + " new com.google.protobuf.Descriptors.FileDescriptor[] {\n"); + + for (int i = 0; i < file_->dependency_count(); i++) { + printer->Print( + " $dependency$.getDescriptor(),\n", + "dependency", ClassName(file_->dependency(i))); + } + + printer->Print( + " });\n" + "} catch (Exception e) {\n" + " throw new RuntimeException(\n" + " \"Failed to parse protocol buffer descriptor for \" +\n" + " \"\\\"$filename$\\\".\", e);\n" + "}\n", + "filename", file_->name()); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + // ----------------------------------------------------------------- + + if (!file_->options().java_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + EnumGenerator(file_->enum_type(i)).Generate(printer); + } + for (int i = 0; i < file_->message_type_count(); i++) { + MessageGenerator(file_->message_type(i)).Generate(printer); + } + for (int i = 0; i < file_->service_count(); i++) { + ServiceGenerator(file_->service(i)).Generate(printer); + } + } + + // Extensions must be generated in the outer class since they are values, + // not classes. + for (int i = 0; i < file_->extension_count(); i++) { + ExtensionGenerator(file_->extension(i)).Generate(printer); + } + + // Static variables. + for (int i = 0; i < file_->message_type_count(); i++) { + // TODO(kenton): Reuse MessageGenerator objects? + MessageGenerator(file_->message_type(i)).GenerateStaticVariables(printer); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +template +static void GenerateSibling(const string& package_dir, + const string& java_package, + const DescriptorClass* descriptor, + OutputDirectory* output_directory, + vector* file_list) { + string filename = package_dir + descriptor->name() + ".java"; + file_list->push_back(filename); + + scoped_ptr output( + output_directory->Open(filename)); + io::Printer printer(output.get(), '$'); + + printer.Print( + "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n"); + if (!java_package.empty()) { + printer.Print( + "package $package$;\n" + "\n", + "package", java_package); + } + + GeneratorClass(descriptor).Generate(&printer); +} + +void FileGenerator::GenerateSiblings(const string& package_dir, + OutputDirectory* output_directory, + vector* file_list) { + if (file_->options().java_multiple_files()) { + for (int i = 0; i < file_->enum_type_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->enum_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->message_type_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->message_type(i), + output_directory, file_list); + } + for (int i = 0; i < file_->service_count(); i++) { + GenerateSibling(package_dir, java_package_, + file_->service(i), + output_directory, file_list); + } + } +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_file.h b/src/google/protobuf/compiler/java/java_file.h new file mode 100644 index 00000000..9eeec253 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_file.h @@ -0,0 +1,78 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + class FileDescriptor; // descriptor.h + namespace io { + class Printer; // printer.h + } + namespace compiler { + class OutputDirectory; // code_generator.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class FileGenerator { + public: + explicit FileGenerator(const FileDescriptor* file); + ~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, + OutputDirectory* output_directory, + vector* file_list); + + const string& java_package() { return java_package_; } + const string& classname() { return classname_; } + + private: + const FileDescriptor* file_; + string java_package_; + string classname_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_FILE_H__ diff --git a/src/google/protobuf/compiler/java/java_generator.cc b/src/google/protobuf/compiler/java/java_generator.cc new file mode 100644 index 00000000..89a32da4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator.cc @@ -0,0 +1,133 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// Parses a set of comma-delimited name/value pairs, e.g.: +// "foo=bar,baz,qux=corge" +// parses to the pairs: +// ("foo", "bar"), ("baz", ""), ("qux", "corge") +void ParseOptions(const string& text, vector >* output) { + vector parts; + SplitStringUsing(text, ",", &parts); + + for (int i = 0; i < parts.size(); i++) { + string::size_type equals_pos = parts[i].find_first_of('='); + pair value; + if (equals_pos == string::npos) { + value.first = parts[i]; + value.second = ""; + } else { + value.first = parts[i].substr(0, equals_pos); + value.second = parts[i].substr(equals_pos + 1); + } + output->push_back(value); + } +} + +} // namespace + +JavaGenerator::JavaGenerator() {} +JavaGenerator::~JavaGenerator() {} + +bool JavaGenerator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + vector > options; + ParseOptions(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; + + for (int i = 0; i < options.size(); i++) { + if (options[i].first == "output_list_file") { + output_list_file = options[i].second; + } else { + *error = "Unknown generator option: " + options[i].first; + return false; + } + } + + + // ----------------------------------------------------------------- + + + FileGenerator file_generator(file); + if (!file_generator.Validate(error)) { + return false; + } + + string package_dir = + StringReplace(file_generator.java_package(), ".", "/", true); + if (!package_dir.empty()) package_dir += "/"; + + vector all_files; + + 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 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 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/java/java_generator.h b/src/google/protobuf/compiler/java/java_generator.h new file mode 100644 index 00000000..11886aa0 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_generator.h @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Generates Java code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// CodeGenerator implementation which generates Java code. If you create your +// own protocol compiler binary and you want it to support Java output, you +// can do so by registering an instance of this CodeGenerator with the +// CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT JavaGenerator : public CodeGenerator { + public: + JavaGenerator(); + ~JavaGenerator(); + + // implements CodeGenerator ---------------------------------------- + bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_GENERATOR_H__ diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc new file mode 100644 index 00000000..4ba82c2a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -0,0 +1,229 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +const char kThickSeparator[] = + "// ===================================================================\n"; +const char kThinSeparator[] = + "// -------------------------------------------------------------------\n"; + +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 StripProto(const string& filename) { + if (HasSuffixString(filename, ".protodevel")) { + return StripSuffixString(filename, ".protodevel"); + } else { + return StripSuffixString(filename, ".proto"); + } +} + +string FileClassName(const FileDescriptor* file) { + if (file->options().has_java_outer_classname()) { + return file->options().java_outer_classname(); + } else { + 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 FileDescriptor* file) { + if (file->options().has_java_package()) { + return file->options().java_package(); + } else { + string result = kDefaultPackage; + if (!file->package().empty()) { + if (!result.empty()) result += '.'; + result += file->package(); + } + return result; + } +} + +string ToJavaName(const string& full_name, const FileDescriptor* file) { + string result; + if (file->options().java_multiple_files()) { + result = FileJavaPackage(file); + } else { + result = ClassName(file); + } + if (!result.empty()) { + result += '.'; + } + if (file->package().empty()) { + result += full_name; + } else { + // Strip the proto package from full_name since we've replaced it with + // the Java package. + result += full_name.substr(file->package().size() + 1); + } + return result; +} + +string ClassName(const FileDescriptor* descriptor) { + string result = FileJavaPackage(descriptor); + if (!result.empty()) result += '.'; + result += FileClassName(descriptor); + return result; +} + +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; +} + +const char* 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 "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h new file mode 100644 index 00000000..74376330 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -0,0 +1,102 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +// 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); + +// 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 FileDescriptor* file); + +// Returns the file's Java package name. +string FileJavaPackage(const FileDescriptor* file); + +// Converts the given fully-qualified name in the proto namespace to its +// fully-qualified name in the Java namespace, given that it is in the given +// file. +string ToJavaName(const string& full_name, const FileDescriptor* file); + +// These return the fully-qualified class name corresponding to the given +// descriptor. +inline string ClassName(const Descriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const EnumDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +inline string ClassName(const ServiceDescriptor* descriptor) { + return ToJavaName(descriptor->full_name(), descriptor->file()); +} +string ClassName(const FileDescriptor* descriptor); + +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()); +} + +// 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. +const char* BoxedPrimitiveTypeName(JavaType type); + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_HELPERS_H__ diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc new file mode 100644 index 00000000..ebba1f7b --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -0,0 +1,722 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +using internal::WireFormat; + +namespace { + +void PrintFieldComment(io::Printer* printer, const FieldDescriptor* field) { + // Print the field's proto-syntax definition as a comment. We don't want to + // print group bodies so we cut off after the first line. + string def = field->DebugString(); + printer->Print("// $def$\n", + "def", def.substr(0, def.find_first_of('\n'))); +} + +struct FieldOrderingByNumber { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) const { + return a->number() < b->number(); + } +}; + +struct ExtensionRangeOrdering { + bool operator()(const Descriptor::ExtensionRange* a, + const Descriptor::ExtensionRange* b) const { + return a->start < b->start; + } +}; + +// 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; +} + +// Get an identifier that uniquely identifies this type within the file. +// This is used to declare static variables related to this type at the +// outermost file scope. +string UniqueFileScopeIdentifier(const Descriptor* descriptor) { + return "static_" + StringReplace(descriptor->full_name(), ".", "_", true); +} + +// Returns true if the message type has any required fields. If it doesn't, +// we can optimize out calls to its isInitialized() method. +// +// already_seen is used to avoid checking the same type multiple times +// (and also to protect against recursion). +static bool HasRequiredFields( + const Descriptor* type, + hash_set* already_seen) { + if (already_seen->count(type) > 0) { + // The type is already in cache. This means that either: + // a. The type has no required fields. + // b. We are in the midst of checking if the type has required fields, + // somewhere up the stack. In this case, we know that if the type + // has any required fields, they'll be found when we return to it, + // and the whole call to HasRequiredFields() will return true. + // Therefore, we don't have to check if this type has required fields + // here. + return false; + } + already_seen->insert(type); + + // If the type has extensions, an extension with message type could contain + // required fields, so we have to be conservative and assume such an + // extension exists. + if (type->extension_range_count() > 0) return true; + + for (int i = 0; i < type->field_count(); i++) { + const FieldDescriptor* field = type->field(i); + if (field->is_required()) { + return true; + } + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (HasRequiredFields(field->message_type(), already_seen)) { + return true; + } + } + } + + return false; +} + +static bool HasRequiredFields(const Descriptor* type) { + hash_set already_seen; + return HasRequiredFields(type, &already_seen); +} + +} // namespace + +// =================================================================== + +MessageGenerator::MessageGenerator(const Descriptor* descriptor) + : descriptor_(descriptor), + field_generators_(descriptor) { +} + +MessageGenerator::~MessageGenerator() {} + +void MessageGenerator::GenerateStaticVariables(io::Printer* printer) { + // Because descriptor.proto (com.google.protobuf.DescriptorProtos) is + // used in the construction of descriptors, we have a tricky bootstrapping + // problem. To help control static initialization order, we make sure all + // descriptors and other static data that depends on them are members of + // the outermost class in the file. This way, they will be initialized in + // a deterministic order. + + map vars; + vars["identifier"] = UniqueFileScopeIdentifier(descriptor_); + vars["index"] = SimpleItoa(descriptor_->index()); + vars["classname"] = ClassName(descriptor_); + if (descriptor_->containing_type() != NULL) { + vars["parent"] = UniqueFileScopeIdentifier(descriptor_->containing_type()); + } + if (descriptor_->file()->options().java_multiple_files()) { + // We can only make these package-private since the classes that use them + // are in separate files. + vars["private"] = ""; + } else { + vars["private"] = "private "; + } + + // The descriptor for this type. + if (descriptor_->containing_type() == NULL) { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor =\n" + " getDescriptor().getMessageTypes().get($index$);\n"); + } else { + printer->Print(vars, + "$private$static final com.google.protobuf.Descriptors.Descriptor\n" + " internal_$identifier$_descriptor =\n" + " internal_$parent$_descriptor.getNestedTypes().get($index$);\n"); + } + + // And the FieldAccessorTable. + printer->Print(vars, + "$private$static\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internal_$identifier$_fieldAccessorTable = new\n" + " com.google.protobuf.GeneratedMessage.FieldAccessorTable(\n" + " internal_$identifier$_descriptor,\n" + " new java.lang.String[] { "); + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print( + "\"$field_name$\", ", + "field_name", + UnderscoresToCapitalizedCamelCase(descriptor_->field(i))); + } + printer->Print("},\n" + " $classname$.class,\n" + " $classname$.Builder.class);\n", + "classname", ClassName(descriptor_)); + + // 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)) + .GenerateStaticVariables(printer); + } +} + +void MessageGenerator::Generate(io::Printer* printer) { + bool is_own_file = + descriptor_->containing_type() == NULL && + descriptor_->file()->options().java_multiple_files(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public $static$ final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableMessage<\n" + " $classname$> {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } else { + printer->Print( + "public $static$ final class $classname$ extends\n" + " com.google.protobuf.GeneratedMessage {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + } + printer->Indent(); + printer->Print( + "// Use $classname$.newBuilder() to construct.\n" + "private $classname$() {}\n" + "\n" + "private static final $classname$ defaultInstance = new $classname$();\n" + "public static $classname$ getDefaultInstance() {\n" + " return defaultInstance;\n" + "}\n" + "\n" + "public $classname$ getDefaultInstanceForType() {\n" + " return defaultInstance;\n" + "}\n" + "\n", + "classname", descriptor_->name()); + printer->Print( + "public static final com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptor() {\n" + " return $fileclass$.internal_$identifier$_descriptor;\n" + "}\n" + "\n" + "protected com.google.protobuf.GeneratedMessage.FieldAccessorTable\n" + " internalGetFieldAccessorTable() {\n" + " return $fileclass$.internal_$identifier$_fieldAccessorTable;\n" + "}\n" + "\n", + "fileclass", ClassName(descriptor_->file()), + "identifier", UniqueFileScopeIdentifier(descriptor_)); + + // Nested types and extensions + for (int i = 0; i < descriptor_->enum_type_count(); i++) { + EnumGenerator(descriptor_->enum_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + MessageGenerator(descriptor_->nested_type(i)).Generate(printer); + } + + for (int i = 0; i < descriptor_->extension_count(); i++) { + ExtensionGenerator(descriptor_->extension(i)).Generate(printer); + } + + // Fields + for (int i = 0; i < descriptor_->field_count(); i++) { + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)).GenerateMembers(printer); + printer->Print("\n"); + } + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateIsInitialized(printer); + GenerateMessageSerializationMethods(printer); + } + + GenerateParseFromMethods(printer); + GenerateBuilder(printer); +} + +// =================================================================== + +void MessageGenerator:: +GenerateMessageSerializationMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + vector sorted_extensions; + for (int i = 0; i < descriptor_->extension_range_count(); ++i) { + sorted_extensions.push_back(descriptor_->extension_range(i)); + } + sort(sorted_extensions.begin(), sorted_extensions.end(), + ExtensionRangeOrdering()); + + printer->Print( + "public void writeTo(com.google.protobuf.CodedOutputStream output)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "com.google.protobuf.GeneratedMessage.ExtendableMessage\n" + " .ExtensionWriter extensionWriter = newExtensionWriter();\n"); + } + + // Merge the fields and the extension ranges, both sorted by field number. + for (int i = 0, j = 0; + i < descriptor_->field_count() || j < sorted_extensions.size(); + ) { + if (i == descriptor_->field_count()) { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } else if (j == sorted_extensions.size()) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { + GenerateSerializeOneField(printer, sorted_fields[i++]); + } else { + GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + } + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "getUnknownFields().writeAsMessageSetTo(output);\n"); + } else { + printer->Print( + "getUnknownFields().writeTo(output);\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n" + "private int memoizedSerializedSize = -1;\n" + "public int getSerializedSize() {\n" + " int size = memoizedSerializedSize;\n" + " if (size != -1) return size;\n" + "\n" + " size = 0;\n"); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(sorted_fields[i]).GenerateSerializedSizeCode(printer); + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "size += extensionsSerializedSize();\n"); + } + + if (descriptor_->options().message_set_wire_format()) { + printer->Print( + "size += getUnknownFields().getSerializedSizeAsMessageSet();\n"); + } else { + printer->Print( + "size += getUnknownFields().getSerializedSize();\n"); + } + + printer->Outdent(); + printer->Print( + " memoizedSerializedSize = size;\n" + " return size;\n" + "}\n" + "\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( + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.ByteString data,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(byte[] data)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " byte[] data,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " return newBuilder().mergeFrom(data, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input).buildParsed();\n" + "}\n" + "public static $classname$ parseFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeFrom(input, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); +} + +void MessageGenerator::GenerateSerializeOneField( + io::Printer* printer, const FieldDescriptor* field) { + field_generators_.get(field).GenerateSerializationCode(printer); +} + +void MessageGenerator::GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range) { + printer->Print( + "extensionWriter.writeUntil($end$, output);\n", + "end", SimpleItoa(range->end)); +} + +// =================================================================== + +void MessageGenerator::GenerateBuilder(io::Printer* printer) { + printer->Print( + "public static Builder newBuilder() { return new Builder(); }\n" + "public Builder newBuilderForType() { return new Builder(); }\n" + "public static Builder newBuilder($classname$ prototype) {\n" + " return new Builder().mergeFrom(prototype);\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.ExtendableBuilder<\n" + " $classname$, Builder> {\n", + "classname", ClassName(descriptor_)); + } else { + printer->Print( + "public static final class Builder extends\n" + " com.google.protobuf.GeneratedMessage.Builder {\n", + "classname", ClassName(descriptor_)); + } + printer->Indent(); + + GenerateCommonBuilderMethods(printer); + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + GenerateBuilderParsingMethods(printer); + } + + for (int i = 0; i < descriptor_->field_count(); i++) { + printer->Print("\n"); + PrintFieldComment(printer, descriptor_->field(i)); + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderMembers(printer); + } + + printer->Outdent(); + printer->Print("}\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { + printer->Print( + "// Construct using $classname$.newBuilder()\n" + "private Builder() {}\n" + "\n" + "$classname$ result = new $classname$();\n" + "\n" + "protected $classname$ internalGetResult() {\n" + " return result;\n" + "}\n" + "\n" + "public Builder clear() {\n" + " result = new $classname$();\n" + " return this;\n" + "}\n" + "\n" + "public Builder clone() {\n" + " return new Builder().mergeFrom(result);\n" + "}\n" + "\n" + "public com.google.protobuf.Descriptors.Descriptor\n" + " getDescriptorForType() {\n" + " return $classname$.getDescriptor();\n" + "}\n" + "\n" + "public $classname$ getDefaultInstanceForType() {\n" + " return $classname$.getDefaultInstance();\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + printer->Print( + "public $classname$ build() {\n" + " if (!isInitialized()) {\n" + " throw new com.google.protobuf.UninitializedMessageException(\n" + " result);\n" + " }\n" + " return buildPartial();\n" + "}\n" + "\n" + "private $classname$ buildParsed()\n" + " throws com.google.protobuf.InvalidProtocolBufferException {\n" + " if (!isInitialized()) {\n" + " throw new com.google.protobuf.UninitializedMessageException(\n" + " result).asInvalidProtocolBufferException();\n" + " }\n" + " return buildPartial();\n" + "}\n" + "\n" + "public $classname$ buildPartial() {\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateBuildingCode(printer); + } + + printer->Outdent(); + printer->Print( + " $classname$ returnMe = result;\n" + " result = null;\n" + " return returnMe;\n" + "}\n" + "\n", + "classname", ClassName(descriptor_)); + + // ----------------------------------------------------------------- + + if (descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { + printer->Print( + "public Builder mergeFrom(com.google.protobuf.Message other) {\n" + " if (other instanceof $classname$) {\n" + " return mergeFrom(($classname$)other);\n" + " } else {\n" + " super.mergeFrom(other);\n" + " return this;\n" + " }\n" + "}\n" + "\n" + "public Builder mergeFrom($classname$ other) {\n" + // Optimization: If other is the default instance, we know none of its + // fields are set so we can skip the merge. + " if (other == $classname$.getDefaultInstance()) return this;\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)).GenerateMergingCode(printer); + } + + printer->Outdent(); + printer->Print( + " this.mergeUnknownFields(other.getUnknownFields());\n" + " return this;\n" + "}\n" + "\n"); + } +} + +// =================================================================== + +void MessageGenerator::GenerateBuilderParsingMethods(io::Printer* printer) { + scoped_array sorted_fields( + SortFieldsByNumber(descriptor_)); + + printer->Print( + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input)\n" + " throws java.io.IOException {\n" + " return mergeFrom(input,\n" + " com.google.protobuf.ExtensionRegistry.getEmptyRegistry());\n" + "}\n" + "\n" + "public Builder mergeFrom(\n" + " com.google.protobuf.CodedInputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n"); + printer->Indent(); + + printer->Print( + "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" + " com.google.protobuf.UnknownFieldSet.newBuilder(\n" + " this.getUnknownFields());\n" + "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 + " this.setUnknownFields(unknownFields.build());\n" + " return this;\n" + "default: {\n" + " if (!parseUnknownField(input, unknownFields,\n" + " extensionRegistry, tag)) {\n" + " this.setUnknownFields(unknownFields.build());\n" + " return this;\n" // it's an endgroup tag + " }\n" + " break;\n" + "}\n"); + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = sorted_fields[i]; + uint32 tag = WireFormat::MakeTag(field->number(), + WireFormat::WireTypeForFieldType(field->type())); + + printer->Print( + "case $tag$: {\n", + "tag", SimpleItoa(tag)); + printer->Indent(); + + field_generators_.get(field).GenerateParsingCode(printer); + + printer->Outdent(); + printer->Print( + " break;\n" + "}\n"); + } + + printer->Outdent(); + printer->Outdent(); + printer->Outdent(); + printer->Print( + " }\n" // switch (tag) + " }\n" // while (true) + "}\n" + "\n"); +} + +// =================================================================== + +void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { + printer->Print( + "public final boolean isInitialized() {\n"); + printer->Indent(); + + // Check that all required fields in this message are set. + // TODO(kenton): We can optimize this when we switch to putting all the + // "has" fields into a single bitfield. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (field->is_required()) { + printer->Print( + "if (!has$name$) return false;\n", + "name", UnderscoresToCapitalizedCamelCase(field)); + } + } + + // Now check that all embedded messages are initialized. + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + HasRequiredFields(field->message_type())) { + switch (field->label()) { + case FieldDescriptor::LABEL_REQUIRED: + printer->Print( + "if (!get$name$().isInitialized()) return false;\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_OPTIONAL: + printer->Print( + "if (has$name$()) {\n" + " if (!get$name$().isInitialized()) return false;\n" + "}\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + case FieldDescriptor::LABEL_REPEATED: + printer->Print( + "for ($type$ element : get$name$List()) {\n" + " if (!element.isInitialized()) return false;\n" + "}\n", + "type", ClassName(field->message_type()), + "name", UnderscoresToCapitalizedCamelCase(field)); + break; + } + } + } + + if (descriptor_->extension_range_count() > 0) { + printer->Print( + "if (!extensionsAreInitialized()) return false;\n"); + } + + printer->Outdent(); + printer->Print( + " return true;\n" + "}\n" + "\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message.h b/src/google/protobuf/compiler/java/java_message.h new file mode 100644 index 00000000..d9f8798e --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message.h @@ -0,0 +1,76 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class MessageGenerator { + public: + explicit MessageGenerator(const Descriptor* descriptor); + ~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); + + // Generate the class itself. + void Generate(io::Printer* printer); + + private: + void GenerateMessageSerializationMethods(io::Printer* printer); + void GenerateParseFromMethods(io::Printer* printer); + void GenerateSerializeOneField(io::Printer* printer, + const FieldDescriptor* field); + void GenerateSerializeOneExtensionRange( + io::Printer* printer, const Descriptor::ExtensionRange* range); + + void GenerateBuilder(io::Printer* printer); + void GenerateCommonBuilderMethods(io::Printer* printer); + void GenerateBuilderParsingMethods(io::Printer* printer); + void GenerateIsInitialized(io::Printer* printer); + + const Descriptor* descriptor_; + FieldGeneratorMap field_generators_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc new file mode 100644 index 00000000..16ddb0d6 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field.cc @@ -0,0 +1,302 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +// TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of +// repeat code between this and the other field types. +void SetMessageVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = ClassName(descriptor->message_type()); + (*variables)["group_or_message"] = + (descriptor->type() == FieldDescriptor::TYPE_GROUP) ? + "Group" : "Message"; +} + +} // namespace + +// =================================================================== + +MessageFieldGenerator:: +MessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +MessageFieldGenerator::~MessageFieldGenerator() {} + +void MessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $type$.getDefaultInstance();\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void MessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder set$capitalized_name$($type$.Builder builderForValue) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = builderForValue.build();\n" + " return this;\n" + "}\n" + "public Builder merge$capitalized_name$($type$ value) {\n" + " if (result.has$capitalized_name$() &&\n" + " result.$name$_ != $type$.getDefaultInstance()) {\n" + " result.$name$_ =\n" + " $type$.newBuilder(result.$name$_).mergeFrom(value).buildPartial();\n" + " } else {\n" + " result.$name$_ = value;\n" + " }\n" + " result.has$capitalized_name$ = true;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $type$.getDefaultInstance();\n" + " return this;\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " merge$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do for singular fields. +} + +void MessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\n" + "if (has$capitalized_name$()) {\n" + " subBuilder.mergeFrom(get$capitalized_name$());\n" + "}\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\n"); + } + + printer->Print(variables_, + "set$capitalized_name$(subBuilder.buildPartial());\n"); +} + +void MessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.write$group_or_message$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void MessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +string MessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +// =================================================================== + +RepeatedMessageFieldGenerator:: +RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetMessageVariables(descriptor, &variables_); +} + +RepeatedMessageFieldGenerator::~RepeatedMessageFieldGenerator() {} + +void RepeatedMessageFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder set$capitalized_name$(int index, " + "$type$.Builder builderForValue) {\n" + " result.$name$_.set(index, builderForValue.build());\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$.Builder builderForValue) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.add(builderForValue.build());\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "$type$.Builder subBuilder = $type$.newBuilder();\n"); + + if (descriptor_->type() == FieldDescriptor::TYPE_GROUP) { + printer->Print(variables_, + "input.readGroup($number$, subBuilder, extensionRegistry);\n"); + } else { + printer->Print(variables_, + "input.readMessage(subBuilder, extensionRegistry);\n"); + } + + printer->Print(variables_, + "add$capitalized_name$(subBuilder.buildPartial());\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$group_or_message$($number$, element);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$group_or_message$Size($number$, element);\n" + "}\n"); +} + +string RepeatedMessageFieldGenerator::GetBoxedType() const { + return ClassName(descriptor_->message_type()); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h new file mode 100644 index 00000000..52cf7d15 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_message_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class MessageFieldGenerator : public FieldGenerator { + public: + explicit MessageFieldGenerator(const FieldDescriptor* descriptor); + ~MessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFieldGenerator); +}; + +class RepeatedMessageFieldGenerator : public FieldGenerator { + public: + explicit RepeatedMessageFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedMessageFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedMessageFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc new file mode 100644 index 00000000..9138f2c9 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -0,0 +1,365 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +namespace { + +const char* 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 "com.google.protobuf.ByteString"; + case JAVATYPE_ENUM : return NULL; + case JAVATYPE_MESSAGE: return NULL; + + // No default because we want the compiler to complain if any new + // JavaTypes are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return NULL; +} + +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; +} + +bool AllPrintableAscii(const string& text) { + // Cannot use isprint() because it's locale-specific. :( + for (int i = 0; i < text.size(); i++) { + if ((text[i] < 0x20) || text[i] >= 0x7F) { + return false; + } + } + return true; +} + +string DefaultValue(const FieldDescriptor* field) { + // 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(field->default_value_uint32())); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field->default_value_int64()) + "L"; + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(static_cast(field->default_value_uint64())) + + "L"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()) + "D"; + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()) + "F"; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_STRING: { + bool isBytes = field->type() == FieldDescriptor::TYPE_BYTES; + + if (!isBytes && AllPrintableAscii(field->default_value_string())) { + // All chars are ASCII and printable. In this case CEscape() works + // fine (it will only escape quotes and backslashes). + // Note: If this "optimization" is removed, DescriptorProtos will + // no longer be able to initialize itself due to bootstrapping + // problems. + return "\"" + CEscape(field->default_value_string()) + "\""; + } + + if (isBytes && !field->has_default_value()) { + return "com.google.protobuf.ByteString.EMPTY"; + } + + // Escaping strings correctly for Java and generating efficient + // initializers for ByteStrings are both tricky. We can sidestep the + // whole problem by just grabbing the default value from the descriptor. + return strings::Substitute( + "(($0) $1.getDescriptor().getFields().get($2).getDefaultValue())", + isBytes ? "com.google.protobuf.ByteString" : "java.lang.String", + ClassName(field->containing_type()), field->index()); + } + + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_MESSAGE: + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; + + // No default because we want the compiler to complain if any new + // types are added. + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + +void SetPrimitiveVariables(const FieldDescriptor* descriptor, + map* variables) { + (*variables)["name"] = + UnderscoresToCamelCase(descriptor); + (*variables)["capitalized_name"] = + UnderscoresToCapitalizedCamelCase(descriptor); + (*variables)["number"] = SimpleItoa(descriptor->number()); + (*variables)["type"] = PrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["boxed_type"] = BoxedPrimitiveTypeName(GetJavaType(descriptor)); + (*variables)["default"] = DefaultValue(descriptor); + (*variables)["capitalized_type"] = GetCapitalizedType(descriptor); +} + +} // namespace + +// =================================================================== + +PrimitiveFieldGenerator:: +PrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +PrimitiveFieldGenerator::~PrimitiveFieldGenerator() {} + +void PrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private boolean has$capitalized_name$;\n" + "private $type$ $name$_ = $default$;\n" + "public boolean has$capitalized_name$() { return has$capitalized_name$; }\n" + "public $type$ get$capitalized_name$() { return $name$_; }\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + "public boolean has$capitalized_name$() {\n" + " return result.has$capitalized_name$();\n" + "}\n" + "public $type$ get$capitalized_name$() {\n" + " return result.get$capitalized_name$();\n" + "}\n" + "public Builder set$capitalized_name$($type$ value) {\n" + " result.has$capitalized_name$ = true;\n" + " result.$name$_ = value;\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.has$capitalized_name$ = false;\n" + " result.$name$_ = $default$;\n" + " return this;\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (other.has$capitalized_name$()) {\n" + " set$capitalized_name$(other.get$capitalized_name$());\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + // Nothing to do here for primitive types. +} + +void PrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "set$capitalized_name$(input.read$capitalized_type$());\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " output.write$capitalized_type$($number$, get$capitalized_name$());\n" + "}\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "if (has$capitalized_name$()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, get$capitalized_name$());\n" + "}\n"); +} + +string PrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +// =================================================================== + +RepeatedPrimitiveFieldGenerator:: +RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor) + : descriptor_(descriptor) { + SetPrimitiveVariables(descriptor, &variables_); +} + +RepeatedPrimitiveFieldGenerator::~RepeatedPrimitiveFieldGenerator() {} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMembers(io::Printer* printer) const { + printer->Print(variables_, + "private java.util.List<$boxed_type$> $name$_ =\n" + " java.util.Collections.emptyList();\n" + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" + " return $name$_;\n" // note: unmodifiable list + "}\n" + "public int get$capitalized_name$Count() { return $name$_.size(); }\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return $name$_.get(index);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuilderMembers(io::Printer* printer) const { + printer->Print(variables_, + // Note: We return an unmodifiable list because otherwise the caller + // could hold on to the returned list and modify it after the message + // has been built, thus mutating the message which is supposed to be + // immutable. + "public java.util.List<$boxed_type$> get$capitalized_name$List() {\n" + " return java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n" + "public int get$capitalized_name$Count() {\n" + " return result.get$capitalized_name$Count();\n" + "}\n" + "public $type$ get$capitalized_name$(int index) {\n" + " return result.get$capitalized_name$(index);\n" + "}\n" + "public Builder set$capitalized_name$(int index, $type$ value) {\n" + " result.$name$_.set(index, value);\n" + " return this;\n" + "}\n" + "public Builder add$capitalized_name$($type$ value) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " result.$name$_.add(value);\n" + " return this;\n" + "}\n" + "public Builder addAll$capitalized_name$(\n" + " java.lang.Iterable values) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " super.addAll(values, result.$name$_);\n" + " return this;\n" + "}\n" + "public Builder clear$capitalized_name$() {\n" + " result.$name$_ = java.util.Collections.emptyList();\n" + " return this;\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateMergingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (!other.$name$_.isEmpty()) {\n" + " if (result.$name$_.isEmpty()) {\n" + " result.$name$_ = new java.util.ArrayList<$boxed_type$>();\n" + " }\n" + " result.$name$_.addAll(other.$name$_);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateBuildingCode(io::Printer* printer) const { + printer->Print(variables_, + "if (result.$name$_ != java.util.Collections.EMPTY_LIST) {\n" + " result.$name$_ =\n" + " java.util.Collections.unmodifiableList(result.$name$_);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateParsingCode(io::Printer* printer) const { + printer->Print(variables_, + "add$capitalized_name$(input.read$capitalized_type$());\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializationCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " output.write$capitalized_type$($number$, element);\n" + "}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializedSizeCode(io::Printer* printer) const { + printer->Print(variables_, + "for ($type$ element : get$capitalized_name$List()) {\n" + " size += com.google.protobuf.CodedOutputStream\n" + " .compute$capitalized_type$Size($number$, element);\n" + "}\n"); +} + +string RepeatedPrimitiveFieldGenerator::GetBoxedType() const { + return BoxedPrimitiveTypeName(GetJavaType(descriptor_)); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h new file mode 100644 index 00000000..6fe9177a --- /dev/null +++ b/src/google/protobuf/compiler/java/java_primitive_field.h @@ -0,0 +1,84 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +class PrimitiveFieldGenerator : public FieldGenerator { + public: + explicit PrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~PrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PrimitiveFieldGenerator); +}; + +class RepeatedPrimitiveFieldGenerator : public FieldGenerator { + public: + explicit RepeatedPrimitiveFieldGenerator(const FieldDescriptor* descriptor); + ~RepeatedPrimitiveFieldGenerator(); + + // implements FieldGenerator --------------------------------------- + void GenerateMembers(io::Printer* printer) const; + void GenerateBuilderMembers(io::Printer* printer) const; + void GenerateMergingCode(io::Printer* printer) const; + void GenerateBuildingCode(io::Printer* printer) const; + void GenerateParsingCode(io::Printer* printer) const; + void GenerateSerializationCode(io::Printer* printer) const; + void GenerateSerializedSizeCode(io::Printer* printer) const; + + string GetBoxedType() const; + + private: + const FieldDescriptor* descriptor_; + map variables_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPrimitiveFieldGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_JAVA_PRIMITIVE_FIELD_H__ diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc new file mode 100644 index 00000000..8cb06270 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -0,0 +1,225 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace java { + +ServiceGenerator::ServiceGenerator(const ServiceDescriptor* descriptor) + : descriptor_(descriptor) {} + +ServiceGenerator::~ServiceGenerator() {} + +void ServiceGenerator::Generate(io::Printer* printer) { + bool is_own_file = descriptor_->file()->options().java_multiple_files(); + printer->Print( + "public $static$ abstract class $classname$\n" + " implements com.google.protobuf.Service {\n", + "static", is_own_file ? "" : "static", + "classname", descriptor_->name()); + printer->Indent(); + + // Generate abstract method declarations. + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["name"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "public abstract void $name$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request,\n" + " com.google.protobuf.RpcCallback<$output$> done);\n"); + } + + // Generate getDescriptor() and getDescriptorForType(). + printer->Print( + "\n" + "public static final\n" + " com.google.protobuf.Descriptors.ServiceDescriptor\n" + " getDescriptor() {\n" + " return $file$.getDescriptor().getServices().get($index$);\n" + "}\n" + "public final com.google.protobuf.Descriptors.ServiceDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n", + "file", ClassName(descriptor_->file()), + "index", SimpleItoa(descriptor_->index())); + + // Generate more stuff. + GenerateCallMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + GenerateStub(printer); + + printer->Outdent(); + printer->Print("}\n\n"); +} + +void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { + printer->Print( + "\n" + "public final void callMethod(\n" + " com.google.protobuf.Descriptors.MethodDescriptor method,\n" + " com.google.protobuf.RpcController controller,\n" + " com.google.protobuf.Message request,\n" + " com.google.protobuf.RpcCallback<\n" + " com.google.protobuf.Message> done) {\n" + " if (method.getService() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Service.callMethod() given method descriptor for wrong \" +\n" + " \"service type.\");\n" + " }\n" + " switch(method.getIndex()) {\n"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "case $index$:\n" + " this.$method$(controller, ($input$)request,\n" + " com.google.protobuf.RpcUtil.<$output$>specializeCallback(\n" + " done));\n" + " return;\n"); + } + + printer->Print( + "default:\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, + io::Printer* printer) { + printer->Print( + "public final com.google.protobuf.Message\n" + " get$request_or_response$Prototype(\n" + " com.google.protobuf.Descriptors.MethodDescriptor method) {\n" + " if (method.getService() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Service.get$request_or_response$Prototype() given method \" +\n" + " \"descriptor for wrong service type.\");\n" + " }\n" + " switch(method.getIndex()) {\n", + "request_or_response", (which == REQUEST) ? "Request" : "Response"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["type"] = ClassName( + (which == REQUEST) ? method->input_type() : method->output_type()); + printer->Print(vars, + "case $index$:\n" + " return $type$.getDefaultInstance();\n"); + } + + printer->Print( + "default:\n" + " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateStub(io::Printer* printer) { + printer->Print( + "public static Stub newStub(\n" + " com.google.protobuf.RpcChannel channel) {\n" + " return new Stub(channel);\n" + "}\n" + "\n" + "public static final class Stub extends $classname$ {\n", + "classname", ClassName(descriptor_)); + printer->Indent(); + + printer->Print( + "private Stub(com.google.protobuf.RpcChannel channel) {\n" + " this.channel = channel;\n" + "}\n" + "\n" + "private final com.google.protobuf.RpcChannel channel;\n" + "\n" + "public com.google.protobuf.RpcChannel getChannel() {\n" + " return channel;\n" + "}\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "\n" + "public void $method$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request,\n" + " com.google.protobuf.RpcCallback<$output$> done) {\n" + " channel.callMethod(\n" + " getDescriptor().getMethods().get($index$),\n" + " controller,\n" + " request,\n" + " $output$.getDefaultInstance(),\n" + " com.google.protobuf.RpcUtil.generalizeCallback(\n" + " done,\n" + " $output$.class,\n" + " $output$.getDefaultInstance()));\n" + "}\n"); + } + + printer->Outdent(); + printer->Print("}\n"); +} + +} // namespace java +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/java/java_service.h b/src/google/protobuf/compiler/java/java_service.h new file mode 100644 index 00000000..adf3dfd4 --- /dev/null +++ b/src/google/protobuf/compiler/java/java_service.h @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_JAVA_SERVICE_H__ +#define GOOGLE_PROTOBUF_COMPILER_JAVA_SERVICE_H__ + +#include +#include + +namespace google { +namespace protobuf { + namespace io { + class Printer; // printer.h + } +} + +namespace protobuf { +namespace compiler { +namespace java { + +class ServiceGenerator { + public: + explicit ServiceGenerator(const ServiceDescriptor* descriptor); + ~ServiceGenerator(); + + void Generate(io::Printer* printer); + + private: + // Generate the implementation of Service.callMethod(). + void GenerateCallMethod(io::Printer* printer); + + // Generate the implementations of Service.get{Request,Response}Prototype(). + enum RequestOrResponse { REQUEST, RESPONSE }; + void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); + + // Generate a stub implementation of the service. + void GenerateStub(io::Printer* printer); + + const ServiceDescriptor* descriptor_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); +}; + +} // namespace java +} // namespace compiler +} // namespace protobuf + +#endif // NET_PROTO2_COMPILER_JAVA_SERVICE_H__ +} // namespace google diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc new file mode 100644 index 00000000..8d69a02c --- /dev/null +++ b/src/google/protobuf/compiler/main.cc @@ -0,0 +1,51 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include +#include + +int main(int argc, char* argv[]) { + + google::protobuf::compiler::CommandLineInterface cli; + + // Proto2 C++ + google::protobuf::compiler::cpp::CppGenerator cpp_generator; + cli.RegisterGenerator("--cpp_out", &cpp_generator, + "Generate C++ header and source."); + + // Proto2 Java + google::protobuf::compiler::java::JavaGenerator java_generator; + cli.RegisterGenerator("--java_out", &java_generator, + "Generate Java source file."); + + + // Proto2 Python + google::protobuf::compiler::python::Generator py_generator; + cli.RegisterGenerator("--python_out", &py_generator, + "Generate Python source file."); + + // Proto2 C# + google::protobuf::compiler::csharp::CSharpGenerator csharp_generator; + cli.RegisterGenerator("--csharp_out", &csharp_generator, + "Generate C# source file."); + + return cli.Run(argc, argv); +} diff --git a/src/google/protobuf/compiler/package_info.h b/src/google/protobuf/compiler/package_info.h new file mode 100644 index 00000000..aa0c2823 --- /dev/null +++ b/src/google/protobuf/compiler/package_info.h @@ -0,0 +1,50 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf::compiler namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +namespace protobuf { + +// Implementation of the Protocol Buffer compiler. +// +// This package contains code for parsing .proto files and generating code +// based on them. There are two reasons you might be interested in this +// package: +// - You want to parse .proto files at runtime. In this case, you should +// look at importer.h. Since this functionality is widely useful, it is +// included in the libprotobuf base library; you do not have to link against +// libprotoc. +// - You want to write a custom protocol compiler which generates different +// kinds of code, e.g. code in a different language which is not supported +// by the official compiler. For this purpose, command_line_interface.h +// provides you with a complete compiler front-end, so all you need to do +// is write a custom implementation of CodeGenerator and a trivial main() +// function. You can even make your compiler support the official languages +// in addition to your own. Since this functionality is only useful to those +// writing custom compilers, it is in a separate library called "libprotoc" +// which you will have to link against. +namespace compiler {} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc new file mode 100644 index 00000000..7a148c5a --- /dev/null +++ b/src/google/protobuf/compiler/parser.cc @@ -0,0 +1,1108 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Recursive descent FTW. + +#include +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +using internal::WireFormat; + +namespace { + +typedef hash_map TypeNameMap; + +TypeNameMap MakeTypeNameTable() { + TypeNameMap result; + + result["double" ] = FieldDescriptorProto::TYPE_DOUBLE; + result["float" ] = FieldDescriptorProto::TYPE_FLOAT; + result["uint64" ] = FieldDescriptorProto::TYPE_UINT64; + result["fixed64" ] = FieldDescriptorProto::TYPE_FIXED64; + result["fixed32" ] = FieldDescriptorProto::TYPE_FIXED32; + result["bool" ] = FieldDescriptorProto::TYPE_BOOL; + result["string" ] = FieldDescriptorProto::TYPE_STRING; + result["group" ] = FieldDescriptorProto::TYPE_GROUP; + + result["bytes" ] = FieldDescriptorProto::TYPE_BYTES; + result["uint32" ] = FieldDescriptorProto::TYPE_UINT32; + result["sfixed32"] = FieldDescriptorProto::TYPE_SFIXED32; + result["sfixed64"] = FieldDescriptorProto::TYPE_SFIXED64; + result["int32" ] = FieldDescriptorProto::TYPE_INT32; + result["int64" ] = FieldDescriptorProto::TYPE_INT64; + result["sint32" ] = FieldDescriptorProto::TYPE_SINT32; + result["sint64" ] = FieldDescriptorProto::TYPE_SINT64; + + return result; +} + +const TypeNameMap kTypeNames = MakeTypeNameTable(); + +} // anonymous namespace + +// Makes code slightly more readable. The meaning of "DO(foo)" is +// "Execute foo and fail if it fails.", where failure is indicated by +// returning false. +#define DO(STATEMENT) if (STATEMENT) {} else return false + +// =================================================================== + +Parser::Parser() + : input_(NULL), + error_collector_(NULL), + source_location_table_(NULL), + had_errors_(false), + require_syntax_identifier_(false) { +} + +Parser::~Parser() { +} + +// =================================================================== + +inline bool Parser::LookingAt(const char* text) { + return input_->current().text == text; +} + +inline bool Parser::LookingAtType(io::Tokenizer::TokenType token_type) { + return input_->current().type == token_type; +} + +inline bool Parser::AtEnd() { + return LookingAtType(io::Tokenizer::TYPE_END); +} + +bool Parser::TryConsume(const char* text) { + if (LookingAt(text)) { + input_->Next(); + return true; + } else { + return false; + } +} + +bool Parser::Consume(const char* text, const char* error) { + if (TryConsume(text)) { + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::Consume(const char* text) { + if (TryConsume(text)) { + return true; + } else { + AddError("Expected \"" + string(text) + "\"."); + return false; + } +} + +bool Parser::ConsumeIdentifier(string* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + *output = input_->current().text; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeInteger(int* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + uint64 value = 0; + if (!io::Tokenizer::ParseInteger(input_->current().text, + kint32max, &value)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse an integer. + } + *output = value; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeInteger64(uint64 max_value, uint64* output, + const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + if (!io::Tokenizer::ParseInteger(input_->current().text, max_value, + output)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse an integer. + *output = 0; + } + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeNumber(double* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) { + *output = io::Tokenizer::ParseFloat(input_->current().text); + input_->Next(); + return true; + } else if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + // Also accept integers. + uint64 value = 0; + if (!io::Tokenizer::ParseInteger(input_->current().text, + kuint64max, &value)) { + AddError("Integer out of range."); + // We still return true because we did, in fact, parse a number. + } + *output = value; + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +bool Parser::ConsumeString(string* output, const char* error) { + if (LookingAtType(io::Tokenizer::TYPE_STRING)) { + io::Tokenizer::ParseString(input_->current().text, output); + input_->Next(); + return true; + } else { + AddError(error); + return false; + } +} + +// ------------------------------------------------------------------- + +void Parser::AddError(int line, int column, const string& error) { + if (error_collector_ != NULL) { + error_collector_->AddError(line, column, error); + } + had_errors_ = true; +} + +void Parser::AddError(const string& error) { + AddError(input_->current().line, input_->current().column, error); +} + +void Parser::RecordLocation( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column) { + if (source_location_table_ != NULL) { + source_location_table_->Add(descriptor, location, line, column); + } +} + +void Parser::RecordLocation( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location) { + RecordLocation(descriptor, location, + input_->current().line, input_->current().column); +} + +// ------------------------------------------------------------------- + +void Parser::SkipStatement() { + while (true) { + if (AtEnd()) { + return; + } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { + if (TryConsume(";")) { + return; + } else if (TryConsume("{")) { + SkipRestOfBlock(); + return; + } else if (LookingAt("}")) { + return; + } + } + input_->Next(); + } +} + +void Parser::SkipRestOfBlock() { + while (true) { + if (AtEnd()) { + return; + } else if (LookingAtType(io::Tokenizer::TYPE_SYMBOL)) { + if (TryConsume("}")) { + return; + } else if (TryConsume("{")) { + SkipRestOfBlock(); + } + } + input_->Next(); + } +} + +// =================================================================== + +bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { + input_ = input; + had_errors_ = false; + syntax_identifier_.clear(); + + if (LookingAtType(io::Tokenizer::TYPE_START)) { + // Advance to first token. + input_->Next(); + } + + if (require_syntax_identifier_ || LookingAt("syntax")) { + if (!ParseSyntaxIdentifier()) { + // Don't attempt to parse the file if we didn't recognize the syntax + // identifier. + return false; + } + } else { + syntax_identifier_ = "proto2"; + } + + // Repeatedly parse statemetns until we reach the end of the file. + while (!AtEnd()) { + if (!ParseTopLevelStatement(file)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + + if (LookingAt("}")) { + AddError("Unmatched \"}\"."); + input_->Next(); + } + } + } + + input_ = NULL; + return !had_errors_; +} + +bool Parser::ParseSyntaxIdentifier() { + DO(Consume("syntax", "File must begin with 'syntax = \"proto2\";'.")); + DO(Consume("=")); + io::Tokenizer::Token syntax_token = input_->current(); + string syntax; + DO(ConsumeString(&syntax, "Expected syntax identifier.")); + DO(Consume(";")); + + syntax_identifier_ = syntax; + + if (syntax != "proto2") { + AddError(syntax_token.line, syntax_token.column, + "Unrecognized syntax identifier \"" + syntax + "\". This parser " + "only recognizes \"proto2\"."); + return false; + } + + return true; +} + +bool Parser::ParseTopLevelStatement(FileDescriptorProto* file) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("message")) { + return ParseMessageDefinition(file->add_message_type()); + } else if (LookingAt("enum")) { + return ParseEnumDefinition(file->add_enum_type()); + } else if (LookingAt("service")) { + return ParseServiceDefinition(file->add_service()); + } else if (LookingAt("extend")) { + return ParseExtend(file->mutable_extension(), + file->mutable_message_type()); + } else if (LookingAt("import")) { + return ParseImport(file->add_dependency()); + } else if (LookingAt("package")) { + return ParsePackage(file); + } else if (LookingAt("option")) { + return ParseOption(file->mutable_options()); + } else { + AddError("Expected top-level statement (e.g. \"message\")."); + return false; + } +} + +// ------------------------------------------------------------------- +// Messages + +bool Parser::ParseMessageDefinition(DescriptorProto* message) { + DO(Consume("message")); + RecordLocation(message, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(message->mutable_name(), "Expected message name.")); + DO(ParseMessageBlock(message)); + return true; +} + +bool Parser::ParseMessageBlock(DescriptorProto* message) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in message definition (missing '}')."); + return false; + } + + if (!ParseMessageStatement(message)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseMessageStatement(DescriptorProto* message) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("message")) { + return ParseMessageDefinition(message->add_nested_type()); + } else if (LookingAt("enum")) { + return ParseEnumDefinition(message->add_enum_type()); + } else if (LookingAt("extensions")) { + return ParseExtensions(message); + } else if (LookingAt("extend")) { + return ParseExtend(message->mutable_extension(), + message->mutable_nested_type()); + } else if (LookingAt("option")) { + return ParseOption(message->mutable_options()); + } else { + return ParseMessageField(message->add_field(), + message->mutable_nested_type()); + } +} + +bool Parser::ParseMessageField(FieldDescriptorProto* field, + RepeatedPtrField* messages) { + // Parse label and type. + FieldDescriptorProto::Label label; + DO(ParseLabel(&label)); + field->set_label(label); + + RecordLocation(field, DescriptorPool::ErrorCollector::TYPE); + FieldDescriptorProto::Type type = FieldDescriptorProto::TYPE_INT32; + string type_name; + DO(ParseType(&type, &type_name)); + if (type_name.empty()) { + field->set_type(type); + } else { + field->set_type_name(type_name); + } + + // Parse name and '='. + RecordLocation(field, DescriptorPool::ErrorCollector::NAME); + io::Tokenizer::Token name_token = input_->current(); + DO(ConsumeIdentifier(field->mutable_name(), "Expected field name.")); + DO(Consume("=", "Missing field number.")); + + // Parse field number. + RecordLocation(field, DescriptorPool::ErrorCollector::NUMBER); + int number; + DO(ConsumeInteger(&number, "Expected field number.")); + field->set_number(number); + + // Parse options. + DO(ParseFieldOptions(field)); + + // Deal with groups. + if (type_name.empty() && type == FieldDescriptorProto::TYPE_GROUP) { + DescriptorProto* group = messages->Add(); + group->set_name(field->name()); + // Record name location to match the field name's location. + RecordLocation(group, DescriptorPool::ErrorCollector::NAME, + name_token.line, name_token.column); + + // As a hack for backwards-compatibility, we force the group name to start + // with a capital letter and lower-case the field name. New code should + // not use groups; it should use nested messages. + if (group->name()[0] < 'A' || 'Z' < group->name()[0]) { + AddError(name_token.line, name_token.column, + "Group names must start with a capital letter."); + } + LowerString(field->mutable_name()); + + field->set_type_name(group->name()); + if (LookingAt("{")) { + DO(ParseMessageBlock(group)); + } else { + AddError("Missing group body."); + return false; + } + } else { + DO(Consume(";")); + } + + return true; +} + +bool Parser::ParseFieldOptions(FieldDescriptorProto* field) { + if (!TryConsume("[")) return true; + + // Parse field options. + do { + if (LookingAt("default")) { + DO(ParseDefaultAssignment(field)); + } else { + DO(ParseOptionAssignment(field->mutable_options())); + } + } while (TryConsume(",")); + + DO(Consume("]")); + return true; +} + +bool Parser::ParseDefaultAssignment(FieldDescriptorProto* field) { + if (field->has_default_value()) { + AddError("Already set option \"default\"."); + field->clear_default_value(); + } + + DO(Consume("default")); + DO(Consume("=")); + + RecordLocation(field, DescriptorPool::ErrorCollector::DEFAULT_VALUE); + string* default_value = field->mutable_default_value(); + + if (!field->has_type()) { + // The field has a type name, but we don't know if it is a message or an + // enum yet. Assume an enum for now. + DO(ConsumeIdentifier(default_value, "Expected identifier.")); + return true; + } + + switch (field->type()) { + case FieldDescriptorProto::TYPE_INT32: + case FieldDescriptorProto::TYPE_INT64: + case FieldDescriptorProto::TYPE_SINT32: + case FieldDescriptorProto::TYPE_SINT64: + case FieldDescriptorProto::TYPE_SFIXED32: + case FieldDescriptorProto::TYPE_SFIXED64: { + uint64 max_value = kint64max; + if (field->type() == FieldDescriptorProto::TYPE_INT32 || + field->type() == FieldDescriptorProto::TYPE_SINT32 || + field->type() == FieldDescriptorProto::TYPE_SFIXED32) { + max_value = kint32max; + } + + // These types can be negative. + if (TryConsume("-")) { + default_value->append("-"); + // Two's complement always has one more negative value than positive. + ++max_value; + } + // Parse the integer to verify that it is not out-of-range. + uint64 value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + // And stringify it again. + default_value->append(SimpleItoa(value)); + break; + } + + case FieldDescriptorProto::TYPE_UINT32: + case FieldDescriptorProto::TYPE_UINT64: + case FieldDescriptorProto::TYPE_FIXED32: + case FieldDescriptorProto::TYPE_FIXED64: { + uint64 max_value = kuint64max; + if (field->type() == FieldDescriptorProto::TYPE_UINT32 || + field->type() == FieldDescriptorProto::TYPE_FIXED32) { + max_value = kuint32max; + } + + // Numeric, not negative. + if (TryConsume("-")) { + AddError("Unsigned field can't have negative default value."); + } + // Parse the integer to verify that it is not out-of-range. + uint64 value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + // And stringify it again. + default_value->append(SimpleItoa(value)); + break; + } + + case FieldDescriptorProto::TYPE_FLOAT: + case FieldDescriptorProto::TYPE_DOUBLE: + // These types can be negative. + if (TryConsume("-")) { + default_value->append("-"); + } + // Parse the integer because we have to convert hex integers to decimal + // floats. + double value; + DO(ConsumeNumber(&value, "Expected number.")); + // And stringify it again. + default_value->append(SimpleDtoa(value)); + break; + + case FieldDescriptorProto::TYPE_BOOL: + if (TryConsume("true")) { + default_value->assign("true"); + } else if (TryConsume("false")) { + default_value->assign("false"); + } else { + AddError("Expected \"true\" or \"false\"."); + return false; + } + break; + + case FieldDescriptorProto::TYPE_STRING: + DO(ConsumeString(default_value, "Expected string.")); + break; + + case FieldDescriptorProto::TYPE_BYTES: + DO(ConsumeString(default_value, "Expected string.")); + *default_value = CEscape(*default_value); + break; + + case FieldDescriptorProto::TYPE_ENUM: + DO(ConsumeIdentifier(default_value, "Expected identifier.")); + break; + + case FieldDescriptorProto::TYPE_MESSAGE: + case FieldDescriptorProto::TYPE_GROUP: + AddError("Messages can't have default values."); + return false; + } + + return true; +} + +bool Parser::ParseOptionAssignment(Message* options) { + const Reflection* reflection = options->GetReflection(); + const Descriptor* descriptor = options->GetDescriptor(); + + // Parse name. + string name; + int line = input_->current().line; + int column = input_->current().column; + DO(ConsumeIdentifier(&name, "Expected option name.")); + + // Is it valid? + const FieldDescriptor* field = descriptor->FindFieldByName(name); + if (field == NULL) { + AddError(line, column, "Unknown option: " + name); + return false; + } + if (field->is_repeated()) { + AddError(line, column, "Not implemented: repeated options."); + return false; + } + if (reflection->HasField(*options, field)) { + AddError(line, column, "Option \"" + name + "\" was already set."); + return false; + } + + // Are we trying to assign a member of a message? + if (LookingAt(".")) { + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + AddError("Option \"" + name + "\" is an atomic type, not a message."); + return false; + } + DO(Consume(".")); + + // This field is a message/group. The user must identify a field within + // it to set. + return ParseOptionAssignment(reflection->MutableMessage(options, field)); + } + + DO(Consume("=")); + + // Parse the option value. + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: { + uint64 value; + bool is_negative = TryConsume("-"); + uint64 max_value = kint32max; + if (is_negative) ++max_value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + reflection->SetInt32(options, field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_INT64: { + uint64 value; + bool is_negative = TryConsume("-"); + uint64 max_value = kint64max; + if (is_negative) ++max_value; + DO(ConsumeInteger64(max_value, &value, "Expected integer.")); + reflection->SetInt64(options, field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT32: { + uint64 value; + DO(ConsumeInteger64(kuint32max, &value, "Expected integer.")); + reflection->SetUInt32(options, field, value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 value; + DO(ConsumeInteger64(kuint64max, &value, "Expected integer.")); + reflection->SetUInt64(options, field, value); + break; + } + + case FieldDescriptor::CPPTYPE_DOUBLE: { + double value; + bool is_negative = TryConsume("-"); + DO(ConsumeNumber(&value, "Expected number.")); + reflection->SetDouble(options, field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_FLOAT: { + double value; + bool is_negative = TryConsume("-"); + DO(ConsumeNumber(&value, "Expected number.")); + reflection->SetFloat(options, field, is_negative ? -value : value); + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: + if (TryConsume("true")) { + reflection->SetBool(options, field, true); + } else if (TryConsume("false")) { + reflection->SetBool(options, field, false); + } else { + AddError("Expected \"true\" or \"false\"."); + return false; + } + break; + + case FieldDescriptor::CPPTYPE_ENUM: { + string value_name; + int value_line = input_->current().line; + int value_column = input_->current().column; + DO(ConsumeIdentifier(&value_name, "Expected enum value.")); + const EnumValueDescriptor* value = + field->enum_type()->FindValueByName(value_name); + if (value == NULL) { + AddError(value_line, value_column, + "Enum type \"" + field->enum_type()->full_name() + "\" has no value " + "named \"" + value_name + "\"."); + return false; + } + reflection->SetEnum(options, field, value); + break; + } + + case FieldDescriptor::CPPTYPE_STRING: { + string value; + DO(ConsumeString(&value, "Expected string.")); + reflection->SetString(options, field, value); + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // TODO(kenton): Allow use of protocol buffer text format here? + AddError("\"" + name + "\" is a message. To set fields within it, use " + "syntax like \"" + name + ".foo = value\"."); + return false; + break; + } + } + + return true; +} + +bool Parser::ParseExtensions(DescriptorProto* message) { + // Parse the declaration. + DO(Consume("extensions")); + + do { + DescriptorProto::ExtensionRange* range = message->add_extension_range(); + RecordLocation(range, DescriptorPool::ErrorCollector::NUMBER); + + int start, end; + DO(ConsumeInteger(&start, "Expected field number range.")); + + if (TryConsume("to")) { + if (TryConsume("max")) { + end = FieldDescriptor::kMaxNumber; + } else { + DO(ConsumeInteger(&end, "Expected integer.")); + } + } else { + end = start; + } + + // Users like to specify inclusive ranges, but in code we like the end + // number to be exclusive. + ++end; + + range->set_start(start); + range->set_end(end); + } while (TryConsume(",")); + + DO(Consume(";")); + return true; +} + +bool Parser::ParseExtend(RepeatedPtrField* extensions, + RepeatedPtrField* messages) { + DO(Consume("extend")); + + // We expect to see at least one extension field defined in the extend block. + // We need to create it now so we can record the extendee's location. + FieldDescriptorProto* first_field = extensions->Add(); + + // Parse the extendee type. + RecordLocation(first_field, DescriptorPool::ErrorCollector::EXTENDEE); + DO(ParseUserDefinedType(first_field->mutable_extendee())); + + // Parse the block. + DO(Consume("{")); + + bool is_first = true; + + do { + if (AtEnd()) { + AddError("Reached end of input in extend definition (missing '}')."); + return false; + } + + FieldDescriptorProto* field; + if (is_first) { + field = first_field; + is_first = false; + } else { + field = extensions->Add(); + field->set_extendee(first_field->extendee()); + } + + if (!ParseMessageField(field, messages)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } while(!TryConsume("}")); + + return true; +} + +// ------------------------------------------------------------------- +// Enums + +bool Parser::ParseEnumDefinition(EnumDescriptorProto* enum_type) { + DO(Consume("enum")); + RecordLocation(enum_type, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_type->mutable_name(), "Expected enum name.")); + DO(ParseEnumBlock(enum_type)); + return true; +} + +bool Parser::ParseEnumBlock(EnumDescriptorProto* enum_type) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in enum definition (missing '}')."); + return false; + } + + if (!ParseEnumStatement(enum_type)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseEnumStatement(EnumDescriptorProto* enum_type) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("option")) { + return ParseOption(enum_type->mutable_options()); + } else { + return ParseEnumConstant(enum_type->add_value()); + } +} + +bool Parser::ParseEnumConstant(EnumValueDescriptorProto* enum_value) { + RecordLocation(enum_value, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(enum_value->mutable_name(), + "Expected enum constant name.")); + DO(Consume("=", "Missing numeric value for enum constant.")); + + bool is_negative = TryConsume("-"); + int number; + DO(ConsumeInteger(&number, "Expected integer.")); + if (is_negative) number *= -1; + enum_value->set_number(number); + + // TODO(kenton): Options for enum values? + + DO(Consume(";")); + + return true; +} + +// ------------------------------------------------------------------- +// Services + +bool Parser::ParseServiceDefinition(ServiceDescriptorProto* service) { + DO(Consume("service")); + RecordLocation(service, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(service->mutable_name(), "Expected service name.")); + DO(ParseServiceBlock(service)); + return true; +} + +bool Parser::ParseServiceBlock(ServiceDescriptorProto* service) { + DO(Consume("{")); + + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in service definition (missing '}')."); + return false; + } + + if (!ParseServiceStatement(service)) { + // This statement failed to parse. Skip it, but keep looping to parse + // other statements. + SkipStatement(); + } + } + + return true; +} + +bool Parser::ParseServiceStatement(ServiceDescriptorProto* service) { + if (TryConsume(";")) { + // empty statement; ignore + return true; + } else if (LookingAt("option")) { + return ParseOption(service->mutable_options()); + } else { + return ParseServiceMethod(service->add_method()); + } +} + +bool Parser::ParseServiceMethod(MethodDescriptorProto* method) { + DO(Consume("rpc")); + RecordLocation(method, DescriptorPool::ErrorCollector::NAME); + DO(ConsumeIdentifier(method->mutable_name(), "Expected method name.")); + + // Parse input type. + DO(Consume("(")); + RecordLocation(method, DescriptorPool::ErrorCollector::INPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_input_type())); + DO(Consume(")")); + + // Parse output type. + DO(Consume("returns")); + DO(Consume("(")); + RecordLocation(method, DescriptorPool::ErrorCollector::OUTPUT_TYPE); + DO(ParseUserDefinedType(method->mutable_output_type())); + DO(Consume(")")); + + if (TryConsume("{")) { + // Options! + while (!TryConsume("}")) { + if (AtEnd()) { + AddError("Reached end of input in method options (missing '}')."); + return false; + } + + if (TryConsume(";")) { + // empty statement; ignore + } else { + if (!ParseOption(method->mutable_options())) { + // This statement failed to parse. Skip it, but keep looping to + // parse other statements. + SkipStatement(); + } + } + } + } else { + DO(Consume(";")); + } + + return true; +} + +// ------------------------------------------------------------------- + +bool Parser::ParseLabel(FieldDescriptorProto::Label* label) { + if (TryConsume("optional")) { + *label = FieldDescriptorProto::LABEL_OPTIONAL; + return true; + } else if (TryConsume("repeated")) { + *label = FieldDescriptorProto::LABEL_REPEATED; + return true; + } else if (TryConsume("required")) { + *label = FieldDescriptorProto::LABEL_REQUIRED; + return true; + } else { + AddError("Expected \"required\", \"optional\", or \"repeated\"."); + // We can actually reasonably recover here by just assuming the user + // forgot the label altogether. + *label = FieldDescriptorProto::LABEL_OPTIONAL; + return true; + } +} + +bool Parser::ParseType(FieldDescriptorProto::Type* type, + string* type_name) { + TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text); + if (iter != kTypeNames.end()) { + *type = iter->second; + input_->Next(); + } else { + DO(ParseUserDefinedType(type_name)); + } + return true; +} + +bool Parser::ParseUserDefinedType(string* type_name) { + type_name->clear(); + + TypeNameMap::const_iterator iter = kTypeNames.find(input_->current().text); + if (iter != kTypeNames.end()) { + // Note: The only place enum types are allowed is for field types, but + // if we are parsing a field type then we would not get here because + // primitives are allowed there as well. So this error message doesn't + // need to account for enums. + AddError("Expected message type."); + + // Pretend to accept this type so that we can go on parsing. + *type_name = input_->current().text; + input_->Next(); + return true; + } + + // A leading "." means the name is fully-qualified. + if (TryConsume(".")) type_name->append("."); + + // Consume the first part of the name. + string identifier; + DO(ConsumeIdentifier(&identifier, "Expected type name.")); + type_name->append(identifier); + + // Consume more parts. + while (TryConsume(".")) { + type_name->append("."); + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + type_name->append(identifier); + } + + return true; +} + +// =================================================================== + +bool Parser::ParsePackage(FileDescriptorProto* file) { + if (file->has_package()) { + AddError("Multiple package definitions."); + // Don't append the new package to the old one. Just replace it. Not + // that it really matters since this is an error anyway. + file->clear_package(); + } + + DO(Consume("package")); + + RecordLocation(file, DescriptorPool::ErrorCollector::NAME); + + while (true) { + string identifier; + DO(ConsumeIdentifier(&identifier, "Expected identifier.")); + file->mutable_package()->append(identifier); + if (!TryConsume(".")) break; + file->mutable_package()->append("."); + } + + DO(Consume(";")); + return true; +} + +bool Parser::ParseImport(string* import_filename) { + DO(Consume("import")); + DO(ConsumeString(import_filename, + "Expected a string naming the file to import.")); + DO(Consume(";")); + return true; +} + +bool Parser::ParseOption(Message* options) { + DO(Consume("option")); + DO(ParseOptionAssignment(options)); + DO(Consume(";")); + return true; +} + +// =================================================================== + +SourceLocationTable::SourceLocationTable() {} +SourceLocationTable::~SourceLocationTable() {} + +bool SourceLocationTable::Find( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int* line, int* column) const { + const pair* result = + FindOrNull(location_map_, make_pair(descriptor, location)); + if (result == NULL) { + *line = -1; + *column = 0; + return false; + } else { + *line = result->first; + *column = result->second; + return true; + } +} + +void SourceLocationTable::Add( + const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column) { + location_map_[make_pair(descriptor, location)] = make_pair(line, column); +} + +void SourceLocationTable::Clear() { + location_map_.clear(); +} + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h new file mode 100644 index 00000000..adf6e9b1 --- /dev/null +++ b/src/google/protobuf/compiler/parser.h @@ -0,0 +1,301 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Implements parsing of .proto files to FileDescriptorProtos. + +#ifndef GOOGLE_PROTOBUF_COMPILER_PARSER_H__ +#define GOOGLE_PROTOBUF_COMPILER_PARSER_H__ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { class Message; } + +namespace protobuf { +namespace compiler { + +// Defined in this file. +class Parser; +class SourceLocationTable; + +// Implements parsing of protocol definitions (such as .proto files). +// +// Note that most users will be more interested in the Importer class. +// Parser is a lower-level class which simply converts a single .proto file +// to a FileDescriptorProto. It does not resolve import directives or perform +// many other kinds of validation needed to construct a complete +// FileDescriptor. +class LIBPROTOBUF_EXPORT Parser { + public: + Parser(); + ~Parser(); + + // Parse the entire input and construct a FileDescriptorProto representing + // it. Returns true if no errors occurred, false otherwise. + bool Parse(io::Tokenizer* input, FileDescriptorProto* file); + + // Optional fetaures: + + // Requests that locations of certain definitions be recorded to the given + // SourceLocationTable while parsing. This can be used to look up exact line + // and column numbers for errors reported by DescriptorPool during validation. + // Set to NULL (the default) to discard source location information. + void RecordSourceLocationsTo(SourceLocationTable* location_table) { + source_location_table_ = location_table; + } + + // Requsets that errors be recorded to the given ErrorCollector while + // parsing. Set to NULL (the default) to discard error messages. + void RecordErrorsTo(io::ErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Returns the identifier used in the "syntax = " declaration, if one was + // seen during the last call to Parse(), or the empty string otherwise. + const string& GetSyntaxIndentifier() { return syntax_identifier_; } + + // If set true, input files will be required to begin with a syntax + // identifier. Otherwise, files may omit this. If a syntax identifier + // is provided, it must be 'syntax = "proto2";' and must appear at the + // top of this file regardless of whether or not it was required. + void SetRequireSyntaxIdentifier(bool value) { + require_syntax_identifier_ = value; + } + + private: + // ================================================================= + // Error recovery helpers + + // Consume the rest of the current statement. This consumes tokens + // until it sees one of: + // ';' Consumes the token and returns. + // '{' Consumes the brace then calls SkipRestOfBlock(). + // '}' Returns without consuming. + // EOF Returns (can't consume). + // The Parser often calls SkipStatement() after encountering a syntax + // error. This allows it to go on parsing the following lines, allowing + // it to report more than just one error in the file. + void SkipStatement(); + + // Consume the rest of the current block, including nested blocks, + // ending after the closing '}' is encountered and consumed, or at EOF. + void SkipRestOfBlock(); + + // ----------------------------------------------------------------- + // Single-token consuming helpers + // + // These make parsing code more readable. + + // True if the current token is TYPE_END. + inline bool AtEnd(); + + // True if the next token matches the given text. + inline bool LookingAt(const char* text); + // True if the next token is of the given type. + inline bool LookingAtType(io::Tokenizer::TokenType token_type); + + // If the next token exactly matches the text given, consume it and return + // true. Otherwise, return false without logging an error. + bool TryConsume(const char* text); + + // These attempt to read some kind of token from the input. If successful, + // they return true. Otherwise they return false and add the given error + // to the error list. + + // Consume a token with the exact text given. + bool Consume(const char* text, const char* error); + // Same as above, but automatically generates the error "Expected \"text\".", + // where "text" is the expected token text. + bool Consume(const char* text); + // Consume a token of type IDENTIFIER and store its text in "output". + bool ConsumeIdentifier(string* output, const char* error); + // Consume an integer and store its value in "output". + bool ConsumeInteger(int* output, const char* error); + // Consume a 64-bit integer and store its value in "output". If the value + // is greater than max_value, an error will be reported. + bool ConsumeInteger64(uint64 max_value, uint64* output, const char* error); + // Consume a number and store its value in "output". This will accept + // tokens of either INTEGER or FLOAT type. + bool ConsumeNumber(double* output, const char* error); + // Consume a string literal and store its (unescaped) value in "output". + bool ConsumeString(string* output, const char* error); + + // ----------------------------------------------------------------- + // Error logging helpers + + // Invokes error_collector_->AddError(), if error_collector_ is not NULL. + void AddError(int line, int column, const string& error); + + // Invokes error_collector_->AddError() with the line and column number + // of the current token. + void AddError(const string& error); + + // Record the given line and column and associate it with this descriptor + // in the SourceLocationTable. + void RecordLocation(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column); + + // Record the current line and column and associate it with this descriptor + // in the SourceLocationTable. + void RecordLocation(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location); + + // ================================================================= + // Parsers for various language constructs + + // Parses the "syntax = \"proto2\";" line at the top of the file. Returns + // false if it failed to parse or if the syntax identifier was not + // recognized. + bool ParseSyntaxIdentifier(); + + // These methods parse various individual bits of code. They return + // false if they completely fail to parse the construct. In this case, + // it is probably necessary to skip the rest of the statement to recover. + // However, if these methods return true, it does NOT mean that there + // were no errors; only that there were no *syntax* errors. For instance, + // if a service method is defined using proper syntax but uses a primitive + // type as its input or output, ParseMethodField() still returns true + // and only reports the error by calling AddError(). In practice, this + // makes logic much simpler for the caller. + + // Parse a top-level message, enum, service, etc. + bool ParseTopLevelStatement(FileDescriptorProto* file); + + // Parse various language high-level language construrcts. + bool ParseMessageDefinition(DescriptorProto* message); + bool ParseEnumDefinition(EnumDescriptorProto* enum_type); + bool ParseServiceDefinition(ServiceDescriptorProto* service); + bool ParsePackage(FileDescriptorProto* file); + bool ParseImport(string* import_filename); + bool ParseOption(Message* options); + + // These methods parse the contents of a message, enum, or service type and + // add them to the given object. They consume the entire block including + // the beginning and ending brace. + bool ParseMessageBlock(DescriptorProto* message); + bool ParseEnumBlock(EnumDescriptorProto* enum_type); + bool ParseServiceBlock(ServiceDescriptorProto* service); + + // Parse one statement within a message, enum, or service block, inclunding + // final semicolon. + bool ParseMessageStatement(DescriptorProto* message); + bool ParseEnumStatement(EnumDescriptorProto* message); + bool ParseServiceStatement(ServiceDescriptorProto* message); + + // Parse a field of a message. If the field is a group, its type will be + // added to "messages". + bool ParseMessageField(FieldDescriptorProto* field, + RepeatedPtrField* messages); + + // Parse an "extensions" declaration. + bool ParseExtensions(DescriptorProto* message); + + // Parse an "extend" declaration. + bool ParseExtend(RepeatedPtrField* extensions, + RepeatedPtrField* messages); + + // Parse a single enum value within an enum block. + bool ParseEnumConstant(EnumValueDescriptorProto* enum_value); + + // Parse a single method within a service definition. + bool ParseServiceMethod(MethodDescriptorProto* method); + + // Parse "required", "optional", or "repeated" and fill in "label" + // with the value. + bool ParseLabel(FieldDescriptorProto::Label* label); + + // Parse a type name and fill in "type" (if it is a primitive) or + // "type_name" (if it is not) with the type parsed. + bool ParseType(FieldDescriptorProto::Type* type, + string* type_name); + // Parse a user-defined type and fill in "type_name" with the name. + // If a primitive type is named, it is treated as an error. + bool ParseUserDefinedType(string* type_name); + + // Parses field options, i.e. the stuff in square brackets at the end + // of a field definition. Also parses default value. + bool ParseFieldOptions(FieldDescriptorProto* field); + + // Parse the "default" option. This needs special handling because its + // type is the field's type. + bool ParseDefaultAssignment(FieldDescriptorProto* field); + + // Parse a single option name/value pair, e.g. "ctype = CORD". The name + // identifies a field of the given Message, and the value of that field + // is set to the parsed value. + bool ParseOptionAssignment(Message* options); + + // ================================================================= + + io::Tokenizer* input_; + io::ErrorCollector* error_collector_; + SourceLocationTable* source_location_table_; + bool had_errors_; + bool require_syntax_identifier_; + string syntax_identifier_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser); +}; + +// A table mapping (descriptor, ErrorLocation) pairs -- as reported by +// DescriptorPool when validating descriptors -- to line and column numbers +// within the original source code. +class LIBPROTOBUF_EXPORT SourceLocationTable { + public: + SourceLocationTable(); + ~SourceLocationTable(); + + // Finds the precise location of the given error and fills in *line and + // *column with the line and column numbers. If not found, sets *line to + // -1 and *column to 0 (since line = -1 is used to mean "error has no exact + // location" in the ErrorCollector interface). Returns true if found, false + // otherwise. + bool Find(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int* line, int* column) const; + + // Adds a location to the table. + void Add(const Message* descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + int line, int column); + + // Clears the contents of the table. + void Clear(); + + private: + typedef map< + pair, + pair > LocationMap; + LocationMap location_map_; +}; + +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_PARSER_H__ diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc new file mode 100644 index 00000000..489106b0 --- /dev/null +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -0,0 +1,1142 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { + +namespace { + +class MockErrorCollector : public io::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line, column, message); + } +}; + +class MockValidationErrorCollector : public DescriptorPool::ErrorCollector { + public: + MockValidationErrorCollector(const SourceLocationTable& source_locations, + io::ErrorCollector* wrapped_collector) + : source_locations_(source_locations), + wrapped_collector_(wrapped_collector) {} + ~MockValidationErrorCollector() {} + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, + const Message* descriptor, + ErrorLocation location, + const string& message) { + int line, column; + source_locations_.Find(descriptor, location, &line, &column); + wrapped_collector_->AddError(line, column, message); + } + + private: + const SourceLocationTable& source_locations_; + io::ErrorCollector* wrapped_collector_; +}; + +class ParserTest : public testing::Test { + protected: + ParserTest() + : require_syntax_identifier_(false) {} + + // Set up the parser to parse the given text. + void SetupParser(const char* text) { + raw_input_.reset(new io::ArrayInputStream(text, strlen(text))); + input_.reset(new io::Tokenizer(raw_input_.get(), &error_collector_)); + parser_.reset(new Parser()); + parser_->RecordErrorsTo(&error_collector_); + parser_->SetRequireSyntaxIdentifier(require_syntax_identifier_); + } + + // Parse the input and expect that the resulting FileDescriptorProto matches + // the given output. The output is a FileDescriptorProto in protocol buffer + // text format. + void ExpectParsesTo(const char* input, const char* output) { + SetupParser(input); + FileDescriptorProto actual, expected; + + parser_->Parse(input_.get(), &actual); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // Parse the ASCII representation in order to canonicalize it. We could + // just compare directly to actual.DebugString(), but that would require + // that the caller precisely match the formatting that DebugString() + // produces. + ASSERT_TRUE(TextFormat::ParseFromString(output, &expected)); + + // Compare by comparing debug strings. + // TODO(kenton): Use differencer, once it is available. + EXPECT_EQ(expected.DebugString(), actual.DebugString()); + } + + // Parse the text and expect that the given errors are reported. + void ExpectHasErrors(const char* text, const char* expected_errors) { + ExpectHasEarlyExitErrors(text, expected_errors); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + } + + // Same as above but does not expect that the parser parses the complete + // input. + void ExpectHasEarlyExitErrors(const char* text, const char* expected_errors) { + SetupParser(text); + FileDescriptorProto file; + parser_->Parse(input_.get(), &file); + EXPECT_EQ(expected_errors, error_collector_.text_); + } + + // Parse the text as a file and validate it (with a DescriptorPool), and + // expect that the validation step reports the given errors. + void ExpectHasValidationErrors(const char* text, + const char* expected_errors) { + SetupParser(text); + SourceLocationTable source_locations; + parser_->RecordSourceLocationsTo(&source_locations); + + FileDescriptorProto file; + file.set_name("foo.proto"); + parser_->Parse(input_.get(), &file); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + MockValidationErrorCollector validation_error_collector( + source_locations, &error_collector_); + EXPECT_TRUE(pool_.BuildFileCollectingErrors( + file, &validation_error_collector) == NULL); + EXPECT_EQ(expected_errors, error_collector_.text_); + } + + MockErrorCollector error_collector_; + DescriptorPool pool_; + + scoped_ptr raw_input_; + scoped_ptr input_; + scoped_ptr parser_; + bool require_syntax_identifier_; +}; + +// =================================================================== + +typedef ParserTest ParseMessageTest; + +TEST_F(ParseMessageTest, SimpleMessage) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); +} + +TEST_F(ParseMessageTest, ImplicitSyntaxIdentifier) { + require_syntax_identifier_ = false; + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) { + ExpectParsesTo( + "syntax = \"proto2\";\n" + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) { + require_syntax_identifier_ = true; + ExpectParsesTo( + "syntax = \"proto2\";\n" + "message TestMessage {\n" + " required int32 foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + "}"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseMessageTest, SimpleFields) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 15;\n" + " optional int32 bar = 34;\n" + " repeated int32 baz = 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:15 }" + " field { name:\"bar\" label:LABEL_OPTIONAL type:TYPE_INT32 number:34 }" + " field { name:\"baz\" label:LABEL_REPEATED type:TYPE_INT32 number:3 }" + "}"); +} + +TEST_F(ParseMessageTest, PrimitiveFieldTypes) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1;\n" + " required int64 foo = 1;\n" + " required uint32 foo = 1;\n" + " required uint64 foo = 1;\n" + " required sint32 foo = 1;\n" + " required sint64 foo = 1;\n" + " required fixed32 foo = 1;\n" + " required fixed64 foo = 1;\n" + " required sfixed32 foo = 1;\n" + " required sfixed64 foo = 1;\n" + " required float foo = 1;\n" + " required double foo = 1;\n" + " required string foo = 1;\n" + " required bytes foo = 1;\n" + " required bool foo = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_UINT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SINT64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FIXED64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED32 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_SFIXED64 number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_FLOAT number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_DOUBLE number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_STRING number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BYTES number:1 }" + " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_BOOL number:1 }" + "}"); +} + +TEST_F(ParseMessageTest, FieldDefaults) { + ExpectParsesTo( + "message TestMessage {\n" + " required int32 foo = 1 [default= 1 ];\n" + " required int32 foo = 1 [default= -2 ];\n" + " required int64 foo = 1 [default= 3 ];\n" + " required int64 foo = 1 [default= -4 ];\n" + " required uint32 foo = 1 [default= 5 ];\n" + " required uint64 foo = 1 [default= 6 ];\n" + " required float foo = 1 [default= 7.5];\n" + " required float foo = 1 [default= -8.5];\n" + " required float foo = 1 [default= 9 ];\n" + " required double foo = 1 [default= 10.5];\n" + " required double foo = 1 [default=-11.5];\n" + " required double foo = 1 [default= 12 ];\n" + " required string foo = 1 [default='13\\001'];\n" + " required bytes foo = 1 [default='14\\002'];\n" + " required bool foo = 1 [default=true ];\n" + " required Foo foo = 1 [default=FOO ];\n" + + " required int32 foo = 1 [default= 0x7FFFFFFF];\n" + " required int32 foo = 1 [default=-0x80000000];\n" + " required uint32 foo = 1 [default= 0xFFFFFFFF];\n" + " required int64 foo = 1 [default= 0x7FFFFFFFFFFFFFFF];\n" + " required int64 foo = 1 [default=-0x8000000000000000];\n" + " required uint64 foo = 1 [default= 0xFFFFFFFFFFFFFFFF];\n" + " required double foo = 1 [default= 0xabcd];\n" + "}\n", + +#define ETC "name:\"foo\" label:LABEL_REQUIRED number:1" + "message_type {" + " name: \"TestMessage\"" + " field { type:TYPE_INT32 default_value:\"1\" "ETC" }" + " field { type:TYPE_INT32 default_value:\"-2\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"3\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"-4\" "ETC" }" + " field { type:TYPE_UINT32 default_value:\"5\" "ETC" }" + " field { type:TYPE_UINT64 default_value:\"6\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"7.5\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"-8.5\" "ETC" }" + " field { type:TYPE_FLOAT default_value:\"9\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"10.5\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"-11.5\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"12\" "ETC" }" + " field { type:TYPE_STRING default_value:\"13\\001\" "ETC" }" + " field { type:TYPE_BYTES default_value:\"14\\\\002\" "ETC" }" + " field { type:TYPE_BOOL default_value:\"true\" "ETC" }" + " field { type_name:\"Foo\" default_value:\"FOO\" "ETC" }" + + " field { type:TYPE_INT32 default_value:\"2147483647\" "ETC" }" + " field { type:TYPE_INT32 default_value:\"-2147483648\" "ETC" }" + " field { type:TYPE_UINT32 default_value:\"4294967295\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"9223372036854775807\" "ETC" }" + " field { type:TYPE_INT64 default_value:\"-9223372036854775808\" "ETC" }" + " field { type:TYPE_UINT64 default_value:\"18446744073709551615\" "ETC" }" + " field { type:TYPE_DOUBLE default_value:\"43981\" "ETC" }" + "}"); +#undef ETC +} + +TEST_F(ParseMessageTest, FieldOptions) { + ExpectParsesTo( + "message TestMessage {\n" + " optional string foo = 1 [ctype=CORD];\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_STRING number:1" + " options { ctype:CORD } }" + "}"); +} + +TEST_F(ParseMessageTest, Group) { + ExpectParsesTo( + "message TestMessage {\n" + " optional group TestGroup = 1 {};\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " nested_type { name: \"TestGroup\" }" + " field { name:\"testgroup\" label:LABEL_OPTIONAL number:1" + " type:TYPE_GROUP type_name: \"TestGroup\" }" + "}"); +} + +TEST_F(ParseMessageTest, NestedMessage) { + ExpectParsesTo( + "message TestMessage {\n" + " message Nested {}\n" + " optional Nested test_nested = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " nested_type { name: \"Nested\" }" + " field { name:\"test_nested\" label:LABEL_OPTIONAL number:1" + " type_name: \"Nested\" }" + "}"); +} + +TEST_F(ParseMessageTest, NestedEnum) { + ExpectParsesTo( + "message TestMessage {\n" + " enum NestedEnum {}\n" + " optional NestedEnum test_enum = 1;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " enum_type { name: \"NestedEnum\" }" + " field { name:\"test_enum\" label:LABEL_OPTIONAL number:1" + " type_name: \"NestedEnum\" }" + "}"); +} + +TEST_F(ParseMessageTest, ExtensionRange) { + ExpectParsesTo( + "message TestMessage {\n" + " extensions 10 to 19;\n" + " extensions 30 to max;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension_range { start:10 end:20 }" + " extension_range { start:30 end:536870912 }" + "}"); +} + +TEST_F(ParseMessageTest, CompoundExtensionRange) { + ExpectParsesTo( + "message TestMessage {\n" + " extensions 2, 15, 9 to 11, 100 to max, 3;\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension_range { start:2 end:3 }" + " extension_range { start:15 end:16 }" + " extension_range { start:9 end:12 }" + " extension_range { start:100 end:536870912 }" + " extension_range { start:3 end:4 }" + "}"); +} + +TEST_F(ParseMessageTest, Extensions) { + ExpectParsesTo( + "extend Extendee1 { optional int32 foo = 12; }\n" + "extend Extendee2 { repeated TestMessage bar = 22; }\n", + + "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" } " + "extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee2\" }"); +} + +TEST_F(ParseMessageTest, ExtensionsInMessageScope) { + ExpectParsesTo( + "message TestMessage {\n" + " extend Extendee1 { optional int32 foo = 12; }\n" + " extend Extendee2 { repeated TestMessage bar = 22; }\n" + "}\n", + + "message_type {" + " name: \"TestMessage\"" + " extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" }" + " extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee2\" }" + "}"); +} + +TEST_F(ParseMessageTest, MultipleExtensionsOneExtendee) { + ExpectParsesTo( + "extend Extendee1 {\n" + " optional int32 foo = 12;\n" + " repeated TestMessage bar = 22;\n" + "}\n", + + "extension { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:12" + " extendee: \"Extendee1\" } " + "extension { name:\"bar\" label:LABEL_REPEATED number:22" + " type_name:\"TestMessage\" extendee: \"Extendee1\" }"); +} + +// =================================================================== + +typedef ParserTest ParseEnumTest; + +TEST_F(ParseEnumTest, SimpleEnum) { + ExpectParsesTo( + "enum TestEnum {\n" + " FOO = 0;\n" + "}\n", + + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"FOO\" number:0 }" + "}"); +} + +TEST_F(ParseEnumTest, Values) { + ExpectParsesTo( + "enum TestEnum {\n" + " FOO = 13;\n" + " BAR = -10;\n" + " BAZ = 500;\n" + "}\n", + + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"FOO\" number:13 }" + " value { name:\"BAR\" number:-10 }" + " value { name:\"BAZ\" number:500 }" + "}"); +} + +// =================================================================== + +typedef ParserTest ParseServiceTest; + +TEST_F(ParseServiceTest, SimpleService) { + ExpectParsesTo( + "service TestService {\n" + " rpc Foo(In) returns (Out);\n" + "}\n", + + "service {" + " name: \"TestService\"" + " method { name:\"Foo\" input_type:\"In\" output_type:\"Out\" }" + "}"); +} + +TEST_F(ParseServiceTest, Methods) { + ExpectParsesTo( + "service TestService {\n" + " rpc Foo(In1) returns (Out1);\n" + " rpc Bar(In2) returns (Out2);\n" + " rpc Baz(In3) returns (Out3);\n" + "}\n", + + "service {" + " name: \"TestService\"" + " method { name:\"Foo\" input_type:\"In1\" output_type:\"Out1\" }" + " method { name:\"Bar\" input_type:\"In2\" output_type:\"Out2\" }" + " method { name:\"Baz\" input_type:\"In3\" output_type:\"Out3\" }" + "}"); +} + +// =================================================================== +// imports and packages + +typedef ParserTest ParseMiscTest; + +TEST_F(ParseMiscTest, ParseImport) { + ExpectParsesTo( + "import \"foo/bar/baz.proto\";\n", + "dependency: \"foo/bar/baz.proto\""); +} + +TEST_F(ParseMiscTest, ParseMultipleImports) { + ExpectParsesTo( + "import \"foo.proto\";\n" + "import \"bar.proto\";\n" + "import \"baz.proto\";\n", + "dependency: \"foo.proto\"" + "dependency: \"bar.proto\"" + "dependency: \"baz.proto\""); +} + +TEST_F(ParseMiscTest, ParsePackage) { + ExpectParsesTo( + "package foo.bar.baz;\n", + "package: \"foo.bar.baz\""); +} + +TEST_F(ParseMiscTest, ParsePackageWithSpaces) { + ExpectParsesTo( + "package foo . bar. \n" + " baz;\n", + "package: \"foo.bar.baz\""); +} + +// =================================================================== +// options + +TEST_F(ParseMiscTest, ParseFileOptions) { + ExpectParsesTo( + "option java_package = \"com.google.foo\";\n" + "option optimize_for = CODE_SIZE;", + + "options {" + " java_package: \"com.google.foo\"" + " optimize_for: CODE_SIZE" + "}"); +} + +// TODO(kenton): We'd like to be able to test all possible option types, +// but we are unable to do so here because we can only test the options +// that actually exist, which currently doesn't cover everything. Perhaps +// we can solve this in the future by allowing options to be extended, then +// defining extensions of every type? + +// =================================================================== +// Error tests +// +// There are a very large number of possible errors that the parser could +// report, so it's infeasible to test every single one of them. Instead, +// we test each unique call to AddError() in parser.h. This does not mean +// we are testing every possible error that Parser can generate because +// each variant of the Consume() helper only counts as one unique call to +// AddError(). + +typedef ParserTest ParseErrorTest; + +TEST_F(ParseErrorTest, MissingSyntaxIdentifier) { + require_syntax_identifier_ = true; + ExpectHasEarlyExitErrors( + "message TestMessage {}", + "0:0: File must begin with 'syntax = \"proto2\";'.\n"); + EXPECT_EQ("", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) { + ExpectHasEarlyExitErrors( + "syntax = \"no_such_syntax\";", + "0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser " + "only recognizes \"proto2\".\n"); + EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, SimpleSyntaxError) { + ExpectHasErrors( + "message TestMessage @#$ { blah }", + "0:20: Expected \"{\".\n"); + EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); +} + +TEST_F(ParseErrorTest, ExpectedTopLevel) { + ExpectHasErrors( + "blah;", + "0:0: Expected top-level statement (e.g. \"message\").\n"); +} + +TEST_F(ParseErrorTest, UnmatchedCloseBrace) { + // This used to cause an infinite loop. Doh. + ExpectHasErrors( + "}", + "0:0: Expected top-level statement (e.g. \"message\").\n" + "0:0: Unmatched \"}\".\n"); +} + +// ------------------------------------------------------------------- +// Message errors + +TEST_F(ParseErrorTest, MessageMissingName) { + ExpectHasErrors( + "message {}", + "0:8: Expected message name.\n"); +} + +TEST_F(ParseErrorTest, MessageMissingBody) { + ExpectHasErrors( + "message TestMessage;", + "0:19: Expected \"{\".\n"); +} + +TEST_F(ParseErrorTest, EofInMessage) { + ExpectHasErrors( + "message TestMessage {", + "0:21: Reached end of input in message definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, MissingFieldNumber) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo;\n" + "}\n", + "1:20: Missing field number.\n"); +} + +TEST_F(ParseErrorTest, ExpectedFieldNumber) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = ;\n" + "}\n", + "1:23: Expected field number.\n"); +} + +TEST_F(ParseErrorTest, FieldNumberOutOfRange) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = 0x100000000;\n" + "}\n", + "1:23: Integer out of range.\n"); +} + +TEST_F(ParseErrorTest, MissingLabel) { + ExpectHasErrors( + "message TestMessage {\n" + " int32 foo = 1;\n" + "}\n", + "1:2: Expected \"required\", \"optional\", or \"repeated\".\n"); +} + +TEST_F(ParseErrorTest, ExpectedOptionName) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [];\n" + "}\n", + "1:27: Expected option name.\n"); +} + +TEST_F(ParseErrorTest, UnknownOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [nosuchoption=5];\n" + "}\n", + "1:27: Unknown option: nosuchoption\n"); +} + +TEST_F(ParseErrorTest, DefaultValueTypeMismatch) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=true];\n" + "}\n", + "1:35: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueNotBoolean) { + ExpectHasErrors( + "message TestMessage {\n" + " optional bool foo = 1 [default=blah];\n" + "}\n", + "1:33: Expected \"true\" or \"false\".\n"); +} + +TEST_F(ParseErrorTest, DefaultValueNotString) { + ExpectHasErrors( + "message TestMessage {\n" + " optional string foo = 1 [default=1];\n" + "}\n", + "1:35: Expected string.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueUnsignedNegative) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=-1];\n" + "}\n", + "1:36: Unsigned field can't have negative default value.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueTooLarge) { + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo = 1 [default= 0x80000000];\n" + " optional int32 foo = 1 [default=-0x80000001];\n" + " optional uint32 foo = 1 [default= 0x100000000];\n" + " optional int64 foo = 1 [default= 0x80000000000000000];\n" + " optional int64 foo = 1 [default=-0x80000000000000001];\n" + " optional uint64 foo = 1 [default= 0x100000000000000000];\n" + "}\n", + "1:36: Integer out of range.\n" + "2:36: Integer out of range.\n" + "3:36: Integer out of range.\n" + "4:36: Integer out of range.\n" + "5:36: Integer out of range.\n" + "6:36: Integer out of range.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueMissing) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=];\n" + "}\n", + "1:35: Expected integer.\n"); +} + +TEST_F(ParseErrorTest, DefaultValueForGroup) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group Foo = 1 [default=blah] {}\n" + "}\n", + "1:34: Messages can't have default values.\n"); +} + +TEST_F(ParseErrorTest, DuplicateDefaultValue) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [default=1,default=2];\n" + "}\n", + "1:37: Already set option \"default\".\n"); +} + +TEST_F(ParseErrorTest, GroupNotCapitalized) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group foo = 1 {}\n" + "}\n", + "1:17: Group names must start with a capital letter.\n"); +} + +TEST_F(ParseErrorTest, GroupMissingBody) { + ExpectHasErrors( + "message TestMessage {\n" + " optional group Foo = 1;\n" + "}\n", + "1:24: Missing group body.\n"); +} + +TEST_F(ParseErrorTest, ExtendingPrimitive) { + ExpectHasErrors( + "extend int32 { optional string foo = 4; }\n", + "0:7: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, ErrorInExtension) { + ExpectHasErrors( + "message Foo { extensions 100 to 199; }\n" + "extend Foo { optional string foo; }\n", + "1:32: Missing field number.\n"); +} + +TEST_F(ParseErrorTest, MultipleParseErrors) { + // When a statement has a parse error, the parser should be able to continue + // parsing at the next statement. + ExpectHasErrors( + "message TestMessage {\n" + " optional int32 foo;\n" + " !invalid statement ending in a block { blah blah { blah } blah }\n" + " optional int32 bar = 3 {}\n" + "}\n", + "1:20: Missing field number.\n" + "2:2: Expected \"required\", \"optional\", or \"repeated\".\n" + "2:2: Expected type name.\n" + "3:25: Expected \";\".\n"); +} + +// ------------------------------------------------------------------- +// Enum errors + +TEST_F(ParseErrorTest, EofInEnum) { + ExpectHasErrors( + "enum TestEnum {", + "0:15: Reached end of input in enum definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, EnumValueMissingNumber) { + ExpectHasErrors( + "enum TestEnum {\n" + " FOO;\n" + "}\n", + "1:5: Missing numeric value for enum constant.\n"); +} + +// ------------------------------------------------------------------- +// Service errors + +TEST_F(ParseErrorTest, EofInService) { + ExpectHasErrors( + "service TestService {", + "0:21: Reached end of input in service definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, ServiceMethodPrimitiveParams) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(int32) returns (string);\n" + "}\n", + "1:10: Expected message type.\n" + "1:26: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, EofInMethodOptions) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(Bar) returns(Bar) {", + "1:29: Reached end of input in method options (missing '}').\n" + "1:29: Reached end of input in service definition (missing '}').\n"); +} + +TEST_F(ParseErrorTest, PrimitiveMethodInput) { + ExpectHasErrors( + "service TestService {\n" + " rpc Foo(int32) returns(Bar);\n" + "}\n", + "1:10: Expected message type.\n"); +} + +TEST_F(ParseErrorTest, MethodOptionTypeError) { + // This used to cause an infinite loop. + ExpectHasErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Baz) { option invalid syntax; }\n" + "}\n", + "2:37: Unknown option: invalid\n"); +} + +// ------------------------------------------------------------------- +// Import and package errors + +TEST_F(ParseErrorTest, ImportNotQuoted) { + ExpectHasErrors( + "import foo;\n", + "0:7: Expected a string naming the file to import.\n"); +} + +TEST_F(ParseErrorTest, MultiplePackagesInFile) { + ExpectHasErrors( + "package foo;\n" + "package bar;\n", + "1:0: Multiple package definitions.\n"); +} + +// ------------------------------------------------------------------- +// Option errors + +TEST_F(ParseErrorTest, OptionWrongType) { + ExpectHasErrors( + "message TestMessage {\n" + " optional string foo = 1 [ctype=1];\n" + "}\n", + "1:33: Expected enum value.\n"); +} + +TEST_F(ParseErrorTest, DupOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [ctype=CORD,ctype=CORD];\n" + "}\n", + "1:38: Option \"ctype\" was already set.\n"); +} + +TEST_F(ParseErrorTest, NotMessageOption) { + ExpectHasErrors( + "message TestMessage {\n" + " optional uint32 foo = 1 [ctype.blah=1];\n" + "}\n", + "1:32: Option \"ctype\" is an atomic type, not a message.\n"); +} + +// TODO(kenton): Test errors for all possible option types (see TODO above, +// under the option parsing tests). + +// =================================================================== +// Test that errors detected by DescriptorPool correctly report line and +// column numbers. We have one test for every call to RecordLocation() in +// parser.cc. + +typedef ParserTest ParserValidationErrorTest; + +TEST_F(ParserValidationErrorTest, PackageNameError) { + // Create another file which defines symbol "foo". + FileDescriptorProto other_file; + other_file.set_name("bar.proto"); + other_file.add_message_type()->set_name("foo"); + EXPECT_TRUE(pool_.BuildFile(other_file) != NULL); + + // Now try to define it as a package. + ExpectHasValidationErrors( + "package foo.bar;", + "0:8: \"foo\" is already defined (as something other than a package) " + "in file \"bar.proto\".\n"); +} + +TEST_F(ParserValidationErrorTest, MessageNameError) { + ExpectHasValidationErrors( + "message Foo {}\n" + "message Foo {}\n", + "1:8: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldNameError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional int32 bar = 1;\n" + " optional int32 bar = 2;\n" + "}\n", + "2:17: \"bar\" is already defined in \"Foo\".\n"); +} + +TEST_F(ParserValidationErrorTest, FieldTypeError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional Baz bar = 1;\n" + "}\n", + "1:11: \"Baz\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldNumberError) { + ExpectHasValidationErrors( + "message Foo {\n" + " optional int32 bar = 0;\n" + "}\n", + "1:23: Field numbers must be positive integers.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldExtendeeError) { + ExpectHasValidationErrors( + "extend Baz { optional int32 bar = 1; }\n", + "0:7: \"Baz\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, FieldDefaultValueError) { + ExpectHasValidationErrors( + "enum Baz { QUX = 1; }\n" + "message Foo {\n" + " optional Baz bar = 1 [default=NO_SUCH_VALUE];\n" + "}\n", + "2:32: Enum type \"Baz\" has no value named \"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ParserValidationErrorTest, ExtensionRangeNumberError) { + ExpectHasValidationErrors( + "message Foo {\n" + " extensions 0;\n" + "}\n", + "1:13: Extension numbers must be positive integers.\n"); +} + +TEST_F(ParserValidationErrorTest, EnumNameError) { + ExpectHasValidationErrors( + "enum Foo {A = 1;}\n" + "enum Foo {B = 1;}\n", + "1:5: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, EnumValueNameError) { + ExpectHasValidationErrors( + "enum Foo {\n" + " BAR = 1;\n" + " BAR = 1;\n" + "}\n", + "2:2: \"BAR\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, ServiceNameError) { + ExpectHasValidationErrors( + "service Foo {}\n" + "service Foo {}\n", + "1:8: \"Foo\" is already defined.\n"); +} + +TEST_F(ParserValidationErrorTest, MethodNameError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Baz);\n" + " rpc Bar(Baz) returns(Baz);\n" + "}\n", + "3:6: \"Bar\" is already defined in \"Foo\".\n"); +} + +TEST_F(ParserValidationErrorTest, MethodInputTypeError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Qux) returns(Baz);\n" + "}\n", + "2:10: \"Qux\" is not defined.\n"); +} + +TEST_F(ParserValidationErrorTest, MethodOutputTypeError) { + ExpectHasValidationErrors( + "message Baz {}\n" + "service Foo {\n" + " rpc Bar(Baz) returns(Qux);\n" + "}\n", + "2:23: \"Qux\" is not defined.\n"); +} + +// =================================================================== +// Test that the output from FileDescriptor::DebugString() (and all other +// descriptor types) is parseable, and results in the same Descriptor +// definitions again afoter parsing (not, however, that the order of messages +// cannot be guaranteed to be the same) + +typedef ParserTest ParseDecriptorDebugTest; + +class CompareDescriptorNames { + public: + bool operator()(const DescriptorProto* left, const DescriptorProto* right) { + return left->name() < right->name(); + } +}; + +// Sorts nested DescriptorProtos of a DescriptoProto, by name. +void SortMessages(DescriptorProto *descriptor_proto) { + int size = descriptor_proto->nested_type_size(); + // recursively sort; we can't guarantee the order of nested messages either + for (int i = 0; i < size; ++i) { + SortMessages(descriptor_proto->mutable_nested_type(i)); + } + DescriptorProto **data = + descriptor_proto->mutable_nested_type()->mutable_data(); + sort(data, data + size, CompareDescriptorNames()); +} + +// Sorts DescriptorProtos belonging to a FileDescriptorProto, by name. +void SortMessages(FileDescriptorProto *file_descriptor_proto) { + int size = file_descriptor_proto->message_type_size(); + // recursively sort; we can't guarantee the order of nested messages either + for (int i = 0; i < size; ++i) { + SortMessages(file_descriptor_proto->mutable_message_type(i)); + } + DescriptorProto **data = + file_descriptor_proto->mutable_message_type()->mutable_data(); + sort(data, data + size, CompareDescriptorNames()); +} + +TEST_F(ParseDecriptorDebugTest, TestAllDescriptorTypes) { + const FileDescriptor* original_file = + protobuf_unittest::TestAllTypes::descriptor()->file(); + FileDescriptorProto expected; + original_file->CopyTo(&expected); + + // Get the DebugString of the unittest.proto FileDecriptor, which includes + // all other descriptor types + string debug_string = original_file->DebugString(); + + // Parse the debug string + SetupParser(debug_string.c_str()); + FileDescriptorProto parsed; + parser_->Parse(input_.get(), &parsed); + EXPECT_EQ(io::Tokenizer::TYPE_END, input_->current().type); + ASSERT_EQ("", error_collector_.text_); + + // We now have a FileDescriptorProto, but to compare with the expected we + // need to link to a FileDecriptor, then output back to a proto. We'll + // also need to give it the same name as the original. + parsed.set_name("google/protobuf/unittest.proto"); + // We need the imported dependency before we can build our parsed proto + const FileDescriptor* import = + protobuf_unittest_import::ImportMessage::descriptor()->file(); + FileDescriptorProto import_proto; + import->CopyTo(&import_proto); + ASSERT_TRUE(pool_.BuildFile(import_proto) != NULL); + const FileDescriptor* actual = pool_.BuildFile(parsed); + parsed.Clear(); + actual->CopyTo(&parsed); + ASSERT_TRUE(actual != NULL); + + // The messages might be in different orders, making them hard to compare. + // So, sort the messages in the descriptor protos (including nested messages, + // recursively). + SortMessages(&expected); + SortMessages(&parsed); + + // I really wanted to use StringDiff here for the debug output on fail, + // but the strings are too long for it, and if I increase its max size, + // we get a memory allocation failure :( + EXPECT_EQ(expected.DebugString(), parsed.DebugString()); +} + +// =================================================================== + +} // anonymous namespace + +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/python/python_generator.cc b/src/google/protobuf/compiler/python/python_generator.cc new file mode 100644 index 00000000..e1171382 --- /dev/null +++ b/src/google/protobuf/compiler/python/python_generator.cc @@ -0,0 +1,794 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: robinson@google.com (Will Robinson) +// +// This module outputs pure-Python protocol message classes that will +// largely be constructed at runtime via the metaclass in reflection.py. +// In other words, our job is basically to output a Python equivalent +// of the C++ *Descriptor objects, and fix up all circular references +// within these objects. +// +// Note that the runtime performance of protocol message classes created in +// this way is expected to be lousy. The plan is to create an alternate +// generator that outputs a Python/C extension module that lets +// performance-minded Python code leverage the fast C++ implementation +// directly. + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace compiler { +namespace python { + +namespace { + +// Returns a copy of |filename| with any trailing ".protodevel" or ".proto +// suffix stripped. +// TODO(robinson): Unify with copy in compiler/cpp/internal/helpers.cc. +string StripProto(const string& filename) { + const char* suffix = HasSuffixString(filename, ".protodevel") + ? ".protodevel" : ".proto"; + return StripSuffixString(filename, suffix); +} + + +// Returns the Python module name expected for a given .proto filename. +string ModuleName(const string& filename) { + string basename = StripProto(filename); + StripString(&basename, "-", '_'); + StripString(&basename, "/", '.'); + return basename + "_pb2"; +} + + +// Returns the name of all containing types for descriptor, +// in order from outermost to innermost, followed by descriptor's +// own name. Each name is separated by |separator|. +template +string NamePrefixedWithNestedTypes(const DescriptorT& descriptor, + const string& separator) { + string name = descriptor.name(); + for (const Descriptor* current = descriptor.containing_type(); + current != NULL; current = current->containing_type()) { + name = current->name() + separator + name; + } + return name; +} + + +// Name of the class attribute where we store the Python +// descriptor.Descriptor instance for the generated class. +// Must stay consistent with the _DESCRIPTOR_KEY constant +// in proto2/public/reflection.py. +const char kDescriptorKey[] = "DESCRIPTOR"; + + +// Prints the common boilerplate needed at the top of every .py +// file output by this generator. +void PrintTopBoilerplate( + io::Printer* printer, const FileDescriptor* file, bool descriptor_proto) { + // TODO(robinson): Allow parameterization of Python version? + printer->Print( + "#!/usr/bin/python2.4\n" + "# Generated by the protocol buffer compiler. DO NOT EDIT!\n" + "\n" + "from google.protobuf import descriptor\n" + "from google.protobuf import message\n" + "from google.protobuf import reflection\n" + "from google.protobuf import service\n" + "from google.protobuf import service_reflection\n"); + // Avoid circular imports if this module is descriptor_pb2. + if (!descriptor_proto) { + printer->Print( + "from google.protobuf import descriptor_pb2\n"); + } +} + + +// Returns a Python literal giving the default value for a field. +// If the field specifies no explicit default value, we'll return +// the default default value for the field type (zero for numbers, +// empty string for strings, empty list for repeated fields, and +// None for non-repeated, composite fields). +// +// TODO(robinson): Unify with code from +// //compiler/cpp/internal/primitive_field.cc +// //compiler/cpp/internal/enum_field.cc +// //compiler/cpp/internal/string_field.cc +string StringifyDefaultValue(const FieldDescriptor& field) { + if (field.is_repeated()) { + return "[]"; + } + + switch (field.cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field.default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(field.default_value_uint32()); + case FieldDescriptor::CPPTYPE_INT64: + return SimpleItoa(field.default_value_int64()); + case FieldDescriptor::CPPTYPE_UINT64: + return SimpleItoa(field.default_value_uint64()); + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field.default_value_double()); + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field.default_value_float()); + case FieldDescriptor::CPPTYPE_BOOL: + return field.default_value_bool() ? "True" : "False"; + case FieldDescriptor::CPPTYPE_ENUM: + return SimpleItoa(field.default_value_enum()->number()); + case FieldDescriptor::CPPTYPE_STRING: + return "\"" + CEscape(field.default_value_string()) + "\""; + case FieldDescriptor::CPPTYPE_MESSAGE: + return "None"; + } + // (We could add a default case above but then we wouldn't get the nice + // compiler warning when a new type is added.) + GOOGLE_LOG(FATAL) << "Not reached."; + return ""; +} + + + +} // namespace + + +Generator::Generator() : file_(NULL) { +} + +Generator::~Generator() { +} + +bool Generator::Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const { + + // Completely serialize all Generate() calls on this instance. The + // thread-safety constraints of the CodeGenerator interface aren't clear so + // just be as conservative as possible. It's easier to relax this later if + // we need to, but I doubt it will be an issue. + // TODO(kenton): The proper thing to do would be to allocate any state on + // the stack and use that, so that the Generator class itself does not need + // to have any mutable members. Then it is implicitly thread-safe. + MutexLock lock(&mutex_); + file_ = file; + string module_name = ModuleName(file->name()); + string filename = module_name; + StripString(&filename, ".", '/'); + filename += ".py"; + + + scoped_ptr output(output_directory->Open(filename)); + GOOGLE_CHECK(output.get()); + io::Printer printer(output.get(), '$'); + printer_ = &printer; + + PrintTopBoilerplate(printer_, file_, GeneratingDescriptorProto()); + PrintTopLevelEnums(); + PrintTopLevelExtensions(); + PrintAllNestedEnumsInFile(); + PrintMessageDescriptors(); + // We have to print the imports after the descriptors, so that mutually + // recursive protos in separate files can successfully reference each other. + PrintImports(); + FixForeignFieldsInDescriptors(); + PrintMessages(); + // We have to fix up the extensions after the message classes themselves, + // since they need to call static RegisterExtension() methods on these + // classes. + FixForeignFieldsInExtensions(); + PrintServices(); + return !printer.failed(); +} + +// Prints Python imports for all modules imported by |file|. +void Generator::PrintImports() const { + for (int i = 0; i < file_->dependency_count(); ++i) { + string module_name = ModuleName(file_->dependency(i)->name()); + printer_->Print("import $module$\n", "module", + module_name); + } + printer_->Print("\n"); +} + +// Prints descriptors and module-level constants for all top-level +// enums defined in |file|. +void Generator::PrintTopLevelEnums() const { + vector > top_level_enum_values; + for (int i = 0; i < file_->enum_type_count(); ++i) { + const EnumDescriptor& enum_descriptor = *file_->enum_type(i); + PrintEnum(enum_descriptor); + printer_->Print("\n"); + + for (int j = 0; j < enum_descriptor.value_count(); ++j) { + const EnumValueDescriptor& value_descriptor = *enum_descriptor.value(j); + top_level_enum_values.push_back( + make_pair(value_descriptor.name(), value_descriptor.number())); + } + } + + for (int i = 0; i < top_level_enum_values.size(); ++i) { + printer_->Print("$name$ = $value$\n", + "name", top_level_enum_values[i].first, + "value", SimpleItoa(top_level_enum_values[i].second)); + } + printer_->Print("\n"); +} + +// Prints all enums contained in all message types in |file|. +void Generator::PrintAllNestedEnumsInFile() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintNestedEnums(*file_->message_type(i)); + } +} + +// Prints a Python statement assigning the appropriate module-level +// enum name to a Python EnumDescriptor object equivalent to +// enum_descriptor. +void Generator::PrintEnum(const EnumDescriptor& enum_descriptor) const { + map m; + m["descriptor_name"] = ModuleLevelDescriptorName(enum_descriptor); + m["name"] = enum_descriptor.name(); + m["full_name"] = enum_descriptor.full_name(); + m["filename"] = enum_descriptor.name(); + const char enum_descriptor_template[] = + "$descriptor_name$ = descriptor.EnumDescriptor(\n" + " name='$name$',\n" + " full_name='$full_name$',\n" + " filename='$filename$',\n" + " values=[\n"; + string options_string; + enum_descriptor.options().SerializeToString(&options_string); + printer_->Print(m, enum_descriptor_template); + printer_->Indent(); + printer_->Indent(); + for (int i = 0; i < enum_descriptor.value_count(); ++i) { + PrintEnumValueDescriptor(*enum_descriptor.value(i)); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); + printer_->Print("options=$options_value$,\n", + "options_value", + OptionsValue("EnumOptions", CEscape(options_string))); + printer_->Outdent(); + printer_->Print(")\n"); + printer_->Print("\n"); +} + +// Recursively prints enums in nested types within descriptor, then +// prints enums contained at the top level in descriptor. +void Generator::PrintNestedEnums(const Descriptor& descriptor) const { + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + PrintNestedEnums(*descriptor.nested_type(i)); + } + + for (int i = 0; i < descriptor.enum_type_count(); ++i) { + PrintEnum(*descriptor.enum_type(i)); + } +} + +void Generator::PrintTopLevelExtensions() const { + const bool is_extension = true; + for (int i = 0; i < file_->extension_count(); ++i) { + const FieldDescriptor& extension_field = *file_->extension(i); + printer_->Print("$name$ = ", "name", extension_field.name()); + PrintFieldDescriptor(extension_field, is_extension); + printer_->Print("\n"); + } + printer_->Print("\n"); +} + +// Prints Python equivalents of all Descriptors in |file|. +void Generator::PrintMessageDescriptors() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintDescriptor(*file_->message_type(i)); + printer_->Print("\n"); + } +} + +void Generator::PrintServices() const { + for (int i = 0; i < file_->service_count(); ++i) { + PrintServiceDescriptor(*file_->service(i)); + PrintServiceClass(*file_->service(i)); + PrintServiceStub(*file_->service(i)); + printer_->Print("\n"); + } +} + +void Generator::PrintServiceDescriptor( + const ServiceDescriptor& descriptor) const { + printer_->Print("\n"); + string service_name = ModuleLevelServiceDescriptorName(descriptor); + string options_string; + descriptor.options().SerializeToString(&options_string); + + printer_->Print( + "$service_name$ = descriptor.ServiceDescriptor(\n", + "service_name", service_name); + printer_->Indent(); + map m; + m["name"] = descriptor.name(); + m["full_name"] = descriptor.full_name(); + m["index"] = SimpleItoa(descriptor.index()); + m["options_value"] = OptionsValue("ServiceOptions", options_string); + const char required_function_arguments[] = + "name='$name$',\n" + "full_name='$full_name$',\n" + "index=$index$,\n" + "options=$options_value$,\n" + "methods=[\n"; + printer_->Print(m, required_function_arguments); + for (int i = 0; i < descriptor.method_count(); ++i) { + const MethodDescriptor* method = descriptor.method(i); + string options_string; + method->options().SerializeToString(&options_string); + + m.clear(); + m["name"] = method->name(); + m["full_name"] = method->full_name(); + m["index"] = SimpleItoa(method->index()); + m["serialized_options"] = CEscape(options_string); + m["input_type"] = ModuleLevelDescriptorName(*(method->input_type())); + m["output_type"] = ModuleLevelDescriptorName(*(method->output_type())); + m["options_value"] = OptionsValue("MethodOptions", options_string); + printer_->Print("descriptor.MethodDescriptor(\n"); + printer_->Indent(); + printer_->Print( + m, + "name='$name$',\n" + "full_name='$full_name$',\n" + "index=$index$,\n" + "containing_service=None,\n" + "input_type=$input_type$,\n" + "output_type=$output_type$,\n" + "options=$options_value$,\n"); + printer_->Outdent(); + printer_->Print("),\n"); + } + + printer_->Outdent(); + printer_->Print("])\n\n"); +} + +void Generator::PrintServiceClass(const ServiceDescriptor& descriptor) const { + // Print the service. + printer_->Print("class $class_name$(service.Service):\n", + "class_name", descriptor.name()); + printer_->Indent(); + printer_->Print( + "__metaclass__ = service_reflection.GeneratedServiceType\n" + "$descriptor_key$ = $descriptor_name$\n", + "descriptor_key", kDescriptorKey, + "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Outdent(); +} + +void Generator::PrintServiceStub(const ServiceDescriptor& descriptor) const { + // Print the service stub. + printer_->Print("class $class_name$_Stub($class_name$):\n", + "class_name", descriptor.name()); + printer_->Indent(); + printer_->Print( + "__metaclass__ = service_reflection.GeneratedServiceStubType\n" + "$descriptor_key$ = $descriptor_name$\n", + "descriptor_key", kDescriptorKey, + "descriptor_name", ModuleLevelServiceDescriptorName(descriptor)); + printer_->Outdent(); +} + +// Prints statement assigning ModuleLevelDescriptorName(message_descriptor) +// to a Python Descriptor object for message_descriptor. +// +// Mutually recursive with PrintNestedDescriptors(). +void Generator::PrintDescriptor(const Descriptor& message_descriptor) const { + PrintNestedDescriptors(message_descriptor); + + printer_->Print("\n"); + printer_->Print("$descriptor_name$ = descriptor.Descriptor(\n", + "descriptor_name", + ModuleLevelDescriptorName(message_descriptor)); + printer_->Indent(); + map m; + m["name"] = message_descriptor.name(); + m["full_name"] = message_descriptor.full_name(); + m["filename"] = message_descriptor.file()->name(); + const char required_function_arguments[] = + "name='$name$',\n" + "full_name='$full_name$',\n" + "filename='$filename$',\n" + "containing_type=None,\n"; // TODO(robinson): Implement containing_type. + printer_->Print(m, required_function_arguments); + PrintFieldsInDescriptor(message_descriptor); + PrintExtensionsInDescriptor(message_descriptor); + // TODO(robinson): implement printing of nested_types. + printer_->Print("nested_types=[], # TODO(robinson): Implement.\n"); + printer_->Print("enum_types=[\n"); + printer_->Indent(); + for (int i = 0; i < message_descriptor.enum_type_count(); ++i) { + const string descriptor_name = ModuleLevelDescriptorName( + *message_descriptor.enum_type(i)); + printer_->Print(descriptor_name.c_str()); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); + string options_string; + message_descriptor.options().SerializeToString(&options_string); + printer_->Print( + "options=$options_value$", + "options_value", OptionsValue("MessageOptions", options_string)); + printer_->Outdent(); + printer_->Print(")\n"); +} + +// Prints Python Descriptor objects for all nested types contained in +// message_descriptor. +// +// Mutually recursive with PrintDescriptor(). +void Generator::PrintNestedDescriptors( + const Descriptor& containing_descriptor) const { + for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) { + PrintDescriptor(*containing_descriptor.nested_type(i)); + } +} + +// Prints all messages in |file|. +void Generator::PrintMessages() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + PrintMessage(*file_->message_type(i)); + printer_->Print("\n"); + } +} + +// Prints a Python class for the given message descriptor. We defer to the +// metaclass to do almost all of the work of actually creating a useful class. +// The purpose of this function and its many helper functions above is merely +// to output a Python version of the descriptors, which the metaclass in +// reflection.py will use to construct the meat of the class itself. +// +// Mutually recursive with PrintNestedMessages(). +void Generator::PrintMessage( + const Descriptor& message_descriptor) const { + printer_->Print("class $name$(message.Message):\n", "name", + message_descriptor.name()); + printer_->Indent(); + printer_->Print("__metaclass__ = reflection.GeneratedProtocolMessageType\n"); + PrintNestedMessages(message_descriptor); + map m; + m["descriptor_key"] = kDescriptorKey; + m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor); + printer_->Print(m, "$descriptor_key$ = $descriptor_name$\n"); + printer_->Outdent(); +} + +// Prints all nested messages within |containing_descriptor|. +// Mutually recursive with PrintMessage(). +void Generator::PrintNestedMessages( + const Descriptor& containing_descriptor) const { + for (int i = 0; i < containing_descriptor.nested_type_count(); ++i) { + printer_->Print("\n"); + PrintMessage(*containing_descriptor.nested_type(i)); + } +} + +// Recursively fixes foreign fields in all nested types in |descriptor|, then +// sets the message_type and enum_type of all message and enum fields to point +// to their respective descriptors. +void Generator::FixForeignFieldsInDescriptor( + const Descriptor& descriptor) const { + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + FixForeignFieldsInDescriptor(*descriptor.nested_type(i)); + } + + for (int i = 0; i < descriptor.field_count(); ++i) { + const FieldDescriptor& field_descriptor = *descriptor.field(i); + FixForeignFieldsInField(&descriptor, field_descriptor, "fields_by_name"); + } +} + +// Sets any necessary message_type and enum_type attributes +// for the Python version of |field|. +// +// containing_type may be NULL, in which case this is a module-level field. +// +// python_dict_name is the name of the Python dict where we should +// look the field up in the containing type. (e.g., fields_by_name +// or extensions_by_name). We ignore python_dict_name if containing_type +// is NULL. +void Generator::FixForeignFieldsInField(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const { + const string field_referencing_expression = FieldReferencingExpression( + containing_type, field, python_dict_name); + map m; + m["field_ref"] = field_referencing_expression; + const Descriptor* foreign_message_type = field.message_type(); + if (foreign_message_type) { + m["foreign_type"] = ModuleLevelDescriptorName(*foreign_message_type); + printer_->Print(m, "$field_ref$.message_type = $foreign_type$\n"); + } + const EnumDescriptor* enum_type = field.enum_type(); + if (enum_type) { + m["enum_type"] = ModuleLevelDescriptorName(*enum_type); + printer_->Print(m, "$field_ref$.enum_type = $enum_type$\n"); + } +} + +// Returns the module-level expression for the given FieldDescriptor. +// Only works for fields in the .proto file this Generator is generating for. +// +// containing_type may be NULL, in which case this is a module-level field. +// +// python_dict_name is the name of the Python dict where we should +// look the field up in the containing type. (e.g., fields_by_name +// or extensions_by_name). We ignore python_dict_name if containing_type +// is NULL. +string Generator::FieldReferencingExpression( + const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const { + // We should only ever be looking up fields in the current file. + // The only things we refer to from other files are message descriptors. + GOOGLE_CHECK_EQ(field.file(), file_) << field.file()->name() << " vs. " + << file_->name(); + if (!containing_type) { + return field.name(); + } + return strings::Substitute( + "$0.$1['$2']", + ModuleLevelDescriptorName(*containing_type), + python_dict_name, field.name()); +} + +// Prints statements setting the message_type and enum_type fields in the +// Python descriptor objects we've already output in ths file. We must +// do this in a separate step due to circular references (otherwise, we'd +// just set everything in the initial assignment statements). +void Generator::FixForeignFieldsInDescriptors() const { + for (int i = 0; i < file_->message_type_count(); ++i) { + FixForeignFieldsInDescriptor(*file_->message_type(i)); + } + printer_->Print("\n"); +} + +// We need to not only set any necessary message_type fields, but +// also need to call RegisterExtension() on each message we're +// extending. +void Generator::FixForeignFieldsInExtensions() const { + // Top-level extensions. + for (int i = 0; i < file_->extension_count(); ++i) { + FixForeignFieldsInExtension(*file_->extension(i)); + } + // Nested extensions. + for (int i = 0; i < file_->message_type_count(); ++i) { + FixForeignFieldsInNestedExtensions(*file_->message_type(i)); + } +} + +void Generator::FixForeignFieldsInExtension( + const FieldDescriptor& extension_field) const { + GOOGLE_CHECK(extension_field.is_extension()); + // extension_scope() will be NULL for top-level extensions, which is + // exactly what FixForeignFieldsInField() wants. + FixForeignFieldsInField(extension_field.extension_scope(), extension_field, + "extensions_by_name"); + + map m; + // Confusingly, for FieldDescriptors that happen to be extensions, + // containing_type() means "extended type." + // On the other hand, extension_scope() will give us what we normally + // mean by containing_type(). + m["extended_message_class"] = ModuleLevelMessageName( + *extension_field.containing_type()); + m["field"] = FieldReferencingExpression(extension_field.extension_scope(), + extension_field, + "extensions_by_name"); + printer_->Print(m, "$extended_message_class$.RegisterExtension($field$)\n"); +} + +void Generator::FixForeignFieldsInNestedExtensions( + const Descriptor& descriptor) const { + // Recursively fix up extensions in all nested types. + for (int i = 0; i < descriptor.nested_type_count(); ++i) { + FixForeignFieldsInNestedExtensions(*descriptor.nested_type(i)); + } + // Fix up extensions directly contained within this type. + for (int i = 0; i < descriptor.extension_count(); ++i) { + FixForeignFieldsInExtension(*descriptor.extension(i)); + } +} + +// Returns a Python expression that instantiates a Python EnumValueDescriptor +// object for the given C++ descriptor. +void Generator::PrintEnumValueDescriptor( + const EnumValueDescriptor& descriptor) const { + // TODO(robinson): Fix up EnumValueDescriptor "type" fields. + // More circular references. ::sigh:: + string options_string; + descriptor.options().SerializeToString(&options_string); + map m; + m["name"] = descriptor.name(); + m["index"] = SimpleItoa(descriptor.index()); + m["number"] = SimpleItoa(descriptor.number()); + m["options"] = OptionsValue("EnumValueOptions", options_string); + printer_->Print( + m, + "descriptor.EnumValueDescriptor(\n" + " name='$name$', index=$index$, number=$number$,\n" + " options=$options$,\n" + " type=None)"); +} + +string Generator::OptionsValue( + const string& class_name, const string& serialized_options) const { + if (serialized_options.length() == 0 || GeneratingDescriptorProto()) { + return "None"; + } else { + string full_class_name = "descriptor_pb2." + class_name; + return "descriptor._ParseOptions(" + full_class_name + "(), '" + + CEscape(serialized_options)+ "')"; + } +} + +// Prints an expression for a Python FieldDescriptor for |field|. +void Generator::PrintFieldDescriptor( + const FieldDescriptor& field, bool is_extension) const { + string options_string; + field.options().SerializeToString(&options_string); + map m; + m["name"] = field.name(); + m["full_name"] = field.full_name(); + m["index"] = SimpleItoa(field.index()); + m["number"] = SimpleItoa(field.number()); + m["type"] = SimpleItoa(field.type()); + m["cpp_type"] = SimpleItoa(field.cpp_type()); + m["label"] = SimpleItoa(field.label()); + m["default_value"] = StringifyDefaultValue(field); + m["is_extension"] = is_extension ? "True" : "False"; + m["options"] = OptionsValue("FieldOptions", options_string); + // We always set message_type and enum_type to None at this point, and then + // these fields in correctly after all referenced descriptors have been + // defined and/or imported (see FixForeignFieldsInDescriptors()). + const char field_descriptor_decl[] = + "descriptor.FieldDescriptor(\n" + " name='$name$', full_name='$full_name$', index=$index$,\n" + " number=$number$, type=$type$, cpp_type=$cpp_type$, label=$label$,\n" + " default_value=$default_value$,\n" + " message_type=None, enum_type=None, containing_type=None,\n" + " is_extension=$is_extension$, extension_scope=None,\n" + " options=$options$)"; + printer_->Print(m, field_descriptor_decl); +} + +// Helper for Print{Fields,Extensions}InDescriptor(). +void Generator::PrintFieldDescriptorsInDescriptor( + const Descriptor& message_descriptor, + bool is_extension, + const string& list_variable_name, + int (Descriptor::*CountFn)() const, + const FieldDescriptor* (Descriptor::*GetterFn)(int) const) const { + printer_->Print("$list$=[\n", "list", list_variable_name); + printer_->Indent(); + for (int i = 0; i < (message_descriptor.*CountFn)(); ++i) { + PrintFieldDescriptor(*(message_descriptor.*GetterFn)(i), + is_extension); + printer_->Print(",\n"); + } + printer_->Outdent(); + printer_->Print("],\n"); +} + +// Prints a statement assigning "fields" to a list of Python FieldDescriptors, +// one for each field present in message_descriptor. +void Generator::PrintFieldsInDescriptor( + const Descriptor& message_descriptor) const { + const bool is_extension = false; + PrintFieldDescriptorsInDescriptor( + message_descriptor, is_extension, "fields", + &Descriptor::field_count, &Descriptor::field); +} + +// Prints a statement assigning "extensions" to a list of Python +// FieldDescriptors, one for each extension present in message_descriptor. +void Generator::PrintExtensionsInDescriptor( + const Descriptor& message_descriptor) const { + const bool is_extension = true; + PrintFieldDescriptorsInDescriptor( + message_descriptor, is_extension, "extensions", + &Descriptor::extension_count, &Descriptor::extension); +} + +bool Generator::GeneratingDescriptorProto() const { + return file_->name() == "google/protobuf/descriptor.proto"; +} + +// Returns the unique Python module-level identifier given to a descriptor. +// This name is module-qualified iff the given descriptor describes an +// entity that doesn't come from the current file. +template +string Generator::ModuleLevelDescriptorName( + const DescriptorT& descriptor) const { + // FIXME(robinson): + // We currently don't worry about collisions with underscores in the type + // names, so these would collide in nasty ways if found in the same file: + // OuterProto.ProtoA.ProtoB + // OuterProto_ProtoA.ProtoB # Underscore instead of period. + // As would these: + // OuterProto.ProtoA_.ProtoB + // OuterProto.ProtoA._ProtoB # Leading vs. trailing underscore. + // (Contrived, but certainly possible). + // + // The C++ implementation doesn't guard against this either. Leaving + // it for now... + string name = NamePrefixedWithNestedTypes(descriptor, "_"); + UpperString(&name); + // Module-private for now. Easy to make public later; almost impossible + // to make private later. + name = "_" + name; + // We now have the name relative to its own module. Also qualify with + // the module name iff this descriptor is from a different .proto file. + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +// Returns the name of the message class itself, not the descriptor. +// Like ModuleLevelDescriptorName(), module-qualifies the name iff +// the given descriptor describes an entity that doesn't come from +// the current file. +string Generator::ModuleLevelMessageName(const Descriptor& descriptor) const { + string name = NamePrefixedWithNestedTypes(descriptor, "."); + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +// Returns the unique Python module-level identifier given to a service +// descriptor. +string Generator::ModuleLevelServiceDescriptorName( + const ServiceDescriptor& descriptor) const { + string name = descriptor.name(); + UpperString(&name); + name = "_" + name; + if (descriptor.file() != file_) { + name = ModuleName(descriptor.file()->name()) + "." + name; + } + return name; +} + +} // namespace python +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h new file mode 100644 index 00000000..98ee0c6d --- /dev/null +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -0,0 +1,129 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: robinson@google.com (Will Robinson) +// +// Generates Python code for a given .proto file. + +#ifndef GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ +#define GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ + +#include + +#include +#include + +namespace google { +namespace protobuf { + +class Descriptor; +class EnumDescriptor; +class EnumValueDescriptor; +class FieldDescriptor; +class ServiceDescriptor; + +namespace io { class Printer; } + +namespace compiler { +namespace python { + +// CodeGenerator implementation for generated Python protocol buffer classes. +// If you create your own protocol compiler binary and you want it to support +// Python output, you can do so by registering an instance of this +// CodeGenerator with the CommandLineInterface in your main() function. +class LIBPROTOC_EXPORT Generator : public CodeGenerator { + public: + Generator(); + virtual ~Generator(); + + // CodeGenerator methods. + virtual bool Generate(const FileDescriptor* file, + const string& parameter, + OutputDirectory* output_directory, + string* error) const; + + private: + void PrintImports() const; + void PrintTopLevelEnums() const; + void PrintAllNestedEnumsInFile() const; + void PrintNestedEnums(const Descriptor& descriptor) const; + void PrintEnum(const EnumDescriptor& enum_descriptor) const; + + void PrintTopLevelExtensions() const; + + void PrintFieldDescriptor( + const FieldDescriptor& field, bool is_extension) const; + void PrintFieldDescriptorsInDescriptor( + const Descriptor& message_descriptor, + bool is_extension, + const string& list_variable_name, + int (Descriptor::*CountFn)() const, + const FieldDescriptor* (Descriptor::*GetterFn)(int) const) const; + void PrintFieldsInDescriptor(const Descriptor& message_descriptor) const; + void PrintExtensionsInDescriptor(const Descriptor& message_descriptor) const; + void PrintMessageDescriptors() const; + void PrintDescriptor(const Descriptor& message_descriptor) const; + void PrintNestedDescriptors(const Descriptor& containing_descriptor) const; + + void PrintMessages() const; + void PrintMessage(const Descriptor& message_descriptor) const; + void PrintNestedMessages(const Descriptor& containing_descriptor) const; + + void FixForeignFieldsInDescriptors() const; + void FixForeignFieldsInDescriptor(const Descriptor& descriptor) const; + void FixForeignFieldsInField(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const; + string FieldReferencingExpression(const Descriptor* containing_type, + const FieldDescriptor& field, + const string& python_dict_name) const; + + void FixForeignFieldsInExtensions() const; + void FixForeignFieldsInExtension( + const FieldDescriptor& extension_field) const; + void FixForeignFieldsInNestedExtensions(const Descriptor& descriptor) const; + + void PrintServices() const; + void PrintServiceDescriptor(const ServiceDescriptor& descriptor) const; + void PrintServiceClass(const ServiceDescriptor& descriptor) const; + void PrintServiceStub(const ServiceDescriptor& descriptor) const; + + void PrintEnumValueDescriptor(const EnumValueDescriptor& descriptor) const; + string OptionsValue(const string& class_name, + const string& serialized_options) const; + bool GeneratingDescriptorProto() const; + + template + string ModuleLevelDescriptorName(const DescriptorT& descriptor) const; + string ModuleLevelMessageName(const Descriptor& descriptor) const; + string ModuleLevelServiceDescriptorName( + const ServiceDescriptor& descriptor) const; + + // Very coarse-grained lock to ensure that Generate() is reentrant. + // Guards file_ and printer_. + mutable Mutex mutex_; + mutable const FileDescriptor* file_; // Set in Generate(). Under mutex_. + mutable io::Printer* printer_; // Set in Generate(). Under mutex_. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator); +}; + +} // namespace python +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_PYTHON_GENERATOR_H__ diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc new file mode 100644 index 00000000..8b808565 --- /dev/null +++ b/src/google/protobuf/descriptor.cc @@ -0,0 +1,2878 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef PACKAGE // autoheader #defines this. :( + +namespace google { +namespace protobuf { + +const FieldDescriptor::CppType +FieldDescriptor::kTypeToCppTypeMap[MAX_TYPE + 1] = { + static_cast(0), // 0 is reserved for errors + + CPPTYPE_DOUBLE, // TYPE_DOUBLE + CPPTYPE_FLOAT, // TYPE_FLOAT + CPPTYPE_INT64, // TYPE_INT64 + CPPTYPE_UINT64, // TYPE_UINT64 + CPPTYPE_INT32, // TYPE_INT32 + CPPTYPE_UINT64, // TYPE_FIXED64 + CPPTYPE_UINT32, // TYPE_FIXED32 + CPPTYPE_BOOL, // TYPE_BOOL + CPPTYPE_STRING, // TYPE_STRING + CPPTYPE_MESSAGE, // TYPE_GROUP + CPPTYPE_MESSAGE, // TYPE_MESSAGE + CPPTYPE_STRING, // TYPE_BYTES + CPPTYPE_UINT32, // TYPE_UINT32 + CPPTYPE_ENUM, // TYPE_ENUM + CPPTYPE_INT32, // TYPE_SFIXED32 + CPPTYPE_INT64, // TYPE_SFIXED64 + CPPTYPE_INT32, // TYPE_SINT32 + CPPTYPE_INT64, // TYPE_SINT64 +}; + +const char * const FieldDescriptor::kTypeToName[MAX_TYPE + 1] = { + "ERROR", // 0 is reserved for errors + + "double", // TYPE_DOUBLE + "float", // TYPE_FLOAT + "int64", // TYPE_INT64 + "uint64", // TYPE_UINT64 + "int32", // TYPE_INT32 + "fixed64", // TYPE_FIXED64 + "fixed32", // TYPE_FIXED32 + "bool", // TYPE_BOOL + "string", // TYPE_STRING + "group", // TYPE_GROUP + "message", // TYPE_MESSAGE + "bytes", // TYPE_BYTES + "uint32", // TYPE_UINT32 + "enum", // TYPE_ENUM + "sfixed32", // TYPE_SFIXED32 + "sfixed64", // TYPE_SFIXED64 + "sint32", // TYPE_SINT32 + "sint64", // TYPE_SINT64 +}; + +const char * const FieldDescriptor::kLabelToName[MAX_LABEL + 1] = { + "ERROR", // 0 is reserved for errors + + "optional", // LABEL_OPTIONAL + "required", // LABEL_REQUIRED + "repeated", // LABEL_REPEATED +}; + +#ifndef _MSC_VER // MSVC doesn't need these and won't even accept them. +const int FieldDescriptor::kMaxNumber; +const int FieldDescriptor::kFirstReservedNumber; +const int FieldDescriptor::kLastReservedNumber; +#endif + +namespace { + +const string kEmptyString; + +// A DescriptorPool contains a bunch of hash_maps to implement the +// various Find*By*() methods. Since hashtable lookups are O(1), it's +// most efficient to construct a fixed set of large hash_maps used by +// all objects in the pool rather than construct one or more small +// hash_maps for each object. +// +// The keys to these hash_maps are (parent, name) or (parent, number) +// pairs. Unfortunately STL doesn't provide hash functions for pair<>, +// so we must invent our own. +// +// TODO(kenton): Use StringPiece rather than const char* in keys? It would +// be a lot cleaner but we'd just have to convert it back to const char* +// for the open source release. + +typedef pair PointerStringPair; + +// Used by GCC/SGI STL only. (Why isn't this provided by the standard +// library? :( ) +struct CStringEqual { + inline bool operator()(const char* a, const char* b) const { + return strcmp(a, b) == 0; + } +}; + +struct PointerStringPairEqual { + inline bool operator()(const PointerStringPair& a, + const PointerStringPair& b) const { + return a.first == b.first && strcmp(a.second, b.second) == 0; + } +}; + +template +struct PointerIntegerPairHash { + size_t operator()(const PairType& p) const { + // FIXME(kenton): What is the best way to compute this hash? I have + // no idea! This seems a bit better than an XOR. + return reinterpret_cast(p.first) * ((1 << 16) - 1) + p.second; + } + + // Used only by MSVC and platforms where hash_map is not available. + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline bool operator()(const PairType& a, const PairType& b) const { + return a.first < b.first || + (a.first == b.first && a.second < b.second); + } +}; + +typedef pair DescriptorIntPair; +typedef pair EnumIntPair; + +struct PointerStringPairHash { + size_t operator()(const PointerStringPair& p) const { + // FIXME(kenton): What is the best way to compute this hash? I have + // no idea! This seems a bit better than an XOR. + hash cstring_hash; + return reinterpret_cast(p.first) * ((1 << 16) - 1) + + cstring_hash(p.second); + } + + // Used only by MSVC and platforms where hash_map is not available. + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline bool operator()(const PointerStringPair& a, + const PointerStringPair& b) const { + if (a.first < b.first) return true; + if (a.first > b.first) return false; + return strcmp(a.second, b.second) < 0; + } +}; + + +struct Symbol { + enum Type { + NULL_SYMBOL, MESSAGE, FIELD, ENUM, ENUM_VALUE, SERVICE, METHOD, PACKAGE + }; + Type type; + union { + const Descriptor* descriptor; + const FieldDescriptor* field_descriptor; + const EnumDescriptor* enum_descriptor; + const EnumValueDescriptor* enum_value_descriptor; + const ServiceDescriptor* service_descriptor; + const MethodDescriptor* method_descriptor; + const FileDescriptor* package_file_descriptor; + }; + + inline Symbol() : type(NULL_SYMBOL) { descriptor = NULL; } + inline bool IsNull() const { return type == NULL_SYMBOL; } + +#define CONSTRUCTOR(TYPE, TYPE_CONSTANT, FIELD) \ + inline explicit Symbol(const TYPE* value) { \ + type = TYPE_CONSTANT; \ + this->FIELD = value; \ + } + + CONSTRUCTOR(Descriptor , MESSAGE , descriptor ) + CONSTRUCTOR(FieldDescriptor , FIELD , field_descriptor ) + CONSTRUCTOR(EnumDescriptor , ENUM , enum_descriptor ) + CONSTRUCTOR(EnumValueDescriptor, ENUM_VALUE, enum_value_descriptor ) + CONSTRUCTOR(ServiceDescriptor , SERVICE , service_descriptor ) + CONSTRUCTOR(MethodDescriptor , METHOD , method_descriptor ) + CONSTRUCTOR(FileDescriptor , PACKAGE , package_file_descriptor) +#undef CONSTRUCTOR + + const FileDescriptor* GetFile() const { + switch (type) { + case NULL_SYMBOL: return NULL; + case MESSAGE : return descriptor ->file(); + case FIELD : return field_descriptor ->file(); + case ENUM : return enum_descriptor ->file(); + case ENUM_VALUE : return enum_value_descriptor->type()->file(); + case SERVICE : return service_descriptor ->file(); + case METHOD : return method_descriptor ->service()->file(); + case PACKAGE : return package_file_descriptor; + } + return NULL; + } +}; + +const Symbol kNullSymbol; + +typedef hash_map, CStringEqual> + SymbolsByNameMap; +typedef hash_map + SymbolsByParentMap; +typedef hash_map, CStringEqual> + FilesByNameMap; +typedef hash_map > + FieldsByNumberMap; +typedef hash_map > + EnumValuesByNumberMap; + +} // anonymous namespace + +// =================================================================== +// DescriptorPool::Tables + +class DescriptorPool::Tables { + public: + Tables(); + ~Tables(); + + // Checkpoint the state of the tables. Future calls to Rollback() will + // return the Tables to this state. This is used when building files, since + // some kinds of validation errors cannot be detected until the file's + // descriptors have already been added to the tables. BuildFile() calls + // Checkpoint() before it starts building and Rollback() if it encounters + // an error. + void Checkpoint(); + + // Roll back the Tables to the state of the last Checkpoint(), removing + // everything that was added after that point. + void Rollback(); + + // The stack of files which are currently being built. Used to detect + // cyclic dependencies when loading files from a DescriptorDatabase. Not + // used when fallback_database_ == NULL. + vector pending_files_; + + // A set of files which we have tried to load from the fallback database + // and encountered errors. We will not attempt to load them again. + // Not used when fallback_database_ == NULL. + hash_set known_bad_files_; + + // ----------------------------------------------------------------- + // Finding items. + + // Find symbols. These return a null Symbol (symbol.IsNull() is true) + // if not found. FindSymbolOfType() additionally returns null if the + // symbol is not of the given type. + inline Symbol FindSymbol(const string& key) const; + inline Symbol FindSymbolOfType(const string& key, + const Symbol::Type type) const; + inline Symbol FindNestedSymbol(const void* parent, + const string& name) const; + inline Symbol FindNestedSymbolOfType(const void* parent, + const string& name, + const Symbol::Type type) const; + + // These return NULL if not found. + inline const FileDescriptor* FindFile(const string& key) const; + inline const FieldDescriptor* FindFieldByNumber( + const Descriptor* parent, int number) const; + inline const EnumValueDescriptor* FindEnumValueByNumber( + const EnumDescriptor* parent, int number) const; + + // ----------------------------------------------------------------- + // Adding items. + + // These add items to the corresponding tables. They return false if + // the key already exists in the table. For AddSymbol(), the strings passed + // in must be ones that were constructed using AllocateString(), as they will + // be used as keys in the symbols_by_name_ and symbols_by_parent_ maps + // without copying. (If parent is NULL, nothing is added to + // symbols_by_parent_.) + bool AddSymbol(const string& full_name, + const void* parent, const string& name, + Symbol symbol); + bool AddFile(const FileDescriptor* file); + bool AddFieldByNumber(const FieldDescriptor* field); + bool AddEnumValueByNumber(const EnumValueDescriptor* value); + + // Like AddSymbol(), but only adds to symbols_by_parent_, not + // symbols_by_name_. Used for enum values, which need to be registered + // under multiple parents (their type and its parent). + bool AddAliasUnderParent(const void* parent, const string& name, + Symbol symbol); + + // ----------------------------------------------------------------- + // Allocating memory. + + // Allocate an object which will be reclaimed when the pool is + // destroyed. Note that the object's destructor will never be called, + // so its fields must be plain old data (primitive data types and + // pointers). All of the descriptor types are such objects. + template Type* Allocate(); + + // Allocate an array of objects which will be reclaimed when the + // pool in destroyed. Again, destructors are never called. + template Type* AllocateArray(int count); + + // Allocate a string which will be destroyed when the pool is destroyed. + // The string is initialized to the given value for convenience. + string* AllocateString(const string& value); + + // Allocate a protocol message object. + template Type* AllocateMessage(); + + private: + vector strings_; // All strings in the pool. + vector messages_; // All messages in the pool. + vector allocations_; // All other memory allocated in the pool. + + SymbolsByNameMap symbols_by_name_; + SymbolsByParentMap symbols_by_parent_; + FilesByNameMap files_by_name_; + FieldsByNumberMap fields_by_number_; // Includes extensions. + EnumValuesByNumberMap enum_values_by_number_; + + int strings_before_checkpoint_; + int messages_before_checkpoint_; + int allocations_before_checkpoint_; + vector symbols_after_checkpoint_; + vector symbols_by_parent_after_checkpoint_; + vector files_after_checkpoint_; + vector field_numbers_after_checkpoint_; + vector enum_numbers_after_checkpoint_; + + // Allocate some bytes which will be reclaimed when the pool is + // destroyed. + void* AllocateBytes(int size); +}; + +DescriptorPool::Tables::Tables() + : strings_before_checkpoint_(0), + messages_before_checkpoint_(0), + allocations_before_checkpoint_(0) {} + +DescriptorPool::Tables::~Tables() { + for (int i = 0; i < allocations_.size(); i++) { + operator delete(allocations_[i]); + } + STLDeleteElements(&strings_); + STLDeleteElements(&messages_); +} + +void DescriptorPool::Tables::Checkpoint() { + strings_before_checkpoint_ = strings_.size(); + messages_before_checkpoint_ = messages_.size(); + allocations_before_checkpoint_ = allocations_.size(); + + symbols_after_checkpoint_.clear(); + symbols_by_parent_after_checkpoint_.clear(); + files_after_checkpoint_.clear(); + field_numbers_after_checkpoint_.clear(); + enum_numbers_after_checkpoint_.clear(); +} + +void DescriptorPool::Tables::Rollback() { + for (int i = 0; i < symbols_after_checkpoint_.size(); i++) { + symbols_by_name_.erase(symbols_after_checkpoint_[i]); + } + for (int i = 0; i < symbols_by_parent_after_checkpoint_.size(); i++) { + symbols_by_parent_.erase(symbols_by_parent_after_checkpoint_[i]); + } + for (int i = 0; i < files_after_checkpoint_.size(); i++) { + files_by_name_.erase(files_after_checkpoint_[i]); + } + for (int i = 0; i < field_numbers_after_checkpoint_.size(); i++) { + fields_by_number_.erase(field_numbers_after_checkpoint_[i]); + } + for (int i = 0; i < enum_numbers_after_checkpoint_.size(); i++) { + enum_values_by_number_.erase(enum_numbers_after_checkpoint_[i]); + } + + symbols_after_checkpoint_.clear(); + symbols_by_parent_after_checkpoint_.clear(); + files_after_checkpoint_.clear(); + field_numbers_after_checkpoint_.clear(); + enum_numbers_after_checkpoint_.clear(); + + STLDeleteContainerPointers( + strings_.begin() + strings_before_checkpoint_, strings_.end()); + STLDeleteContainerPointers( + messages_.begin() + messages_before_checkpoint_, messages_.end()); + for (int i = allocations_before_checkpoint_; i < allocations_.size(); i++) { + operator delete(allocations_[i]); + } + + strings_.resize(strings_before_checkpoint_); + messages_.resize(messages_before_checkpoint_); + allocations_.resize(allocations_before_checkpoint_); +} + +// ------------------------------------------------------------------- + +inline Symbol DescriptorPool::Tables::FindSymbol(const string& key) const { + const Symbol* result = FindOrNull(symbols_by_name_, key.c_str()); + if (result == NULL) { + return kNullSymbol; + } else { + return *result; + } +} + +inline Symbol DescriptorPool::Tables::FindSymbolOfType( + const string& key, const Symbol::Type type) const { + Symbol result = FindSymbol(key); + if (result.type != type) return kNullSymbol; + return result; +} + +inline Symbol DescriptorPool::Tables::FindNestedSymbol( + const void* parent, const string& name) const { + const Symbol* result = + FindOrNull(symbols_by_parent_, PointerStringPair(parent, name.c_str())); + if (result == NULL) { + return kNullSymbol; + } else { + return *result; + } +} + +inline Symbol DescriptorPool::Tables::FindNestedSymbolOfType( + const void* parent, const string& name, const Symbol::Type type) const { + Symbol result = FindNestedSymbol(parent, name); + if (result.type != type) return kNullSymbol; + return result; +} + +inline const FileDescriptor* DescriptorPool::Tables::FindFile( + const string& key) const { + return FindPtrOrNull(files_by_name_, key.c_str()); +} + +inline const FieldDescriptor* DescriptorPool::Tables::FindFieldByNumber( + const Descriptor* parent, int number) const { + return FindPtrOrNull(fields_by_number_, make_pair(parent, number)); +} + +inline const EnumValueDescriptor* DescriptorPool::Tables::FindEnumValueByNumber( + const EnumDescriptor* parent, int number) const { + return FindPtrOrNull(enum_values_by_number_, make_pair(parent, number)); +} + +// ------------------------------------------------------------------- + +bool DescriptorPool::Tables::AddSymbol( + const string& full_name, + const void* parent, const string& name, + Symbol symbol) { + if (InsertIfNotPresent(&symbols_by_name_, full_name.c_str(), symbol)) { + symbols_after_checkpoint_.push_back(full_name.c_str()); + + if (parent != NULL && !AddAliasUnderParent(parent, name, symbol)) { + GOOGLE_LOG(DFATAL) << "\"" << full_name << "\" not previously defined in " + "symbols_by_name_, but was defined in symbols_by_parent_; " + "this shouldn't be possible."; + return false; + } + + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddAliasUnderParent( + const void* parent, const string& name, Symbol symbol) { + PointerStringPair by_parent_key(parent, name.c_str()); + if (InsertIfNotPresent(&symbols_by_parent_, by_parent_key, symbol)) { + symbols_by_parent_after_checkpoint_.push_back(by_parent_key); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddFile(const FileDescriptor* file) { + if (InsertIfNotPresent(&files_by_name_, file->name().c_str(), file)) { + files_after_checkpoint_.push_back(file->name().c_str()); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddFieldByNumber(const FieldDescriptor* field) { + DescriptorIntPair key(field->containing_type(), field->number()); + if (InsertIfNotPresent(&fields_by_number_, key, field)) { + field_numbers_after_checkpoint_.push_back(key); + return true; + } else { + return false; + } +} + +bool DescriptorPool::Tables::AddEnumValueByNumber( + const EnumValueDescriptor* value) { + EnumIntPair key(value->type(), value->number()); + if (InsertIfNotPresent(&enum_values_by_number_, key, value)) { + enum_numbers_after_checkpoint_.push_back(key); + return true; + } else { + return false; + } +} + +// ------------------------------------------------------------------- + +template +Type* DescriptorPool::Tables::Allocate() { + return reinterpret_cast(AllocateBytes(sizeof(Type))); +} + +template +Type* DescriptorPool::Tables::AllocateArray(int count) { + return reinterpret_cast(AllocateBytes(sizeof(Type) * count)); +} + +string* DescriptorPool::Tables::AllocateString(const string& value) { + string* result = new string(value); + strings_.push_back(result); + return result; +} + +template +Type* DescriptorPool::Tables::AllocateMessage() { + Type* result = new Type; + messages_.push_back(result); + return result; +} + +void* DescriptorPool::Tables::AllocateBytes(int size) { + // TODO(kenton): Would it be worthwhile to implement this in some more + // sophisticated way? Probably not for the open source release, but for + // internal use we could easily plug in one of our existing memory pool + // allocators... + if (size == 0) return NULL; + + void* result = operator new(size); + allocations_.push_back(result); + return result; +} + +// =================================================================== +// DescriptorPool + +DescriptorPool::ErrorCollector::~ErrorCollector() {} + +DescriptorPool::DescriptorPool() + : mutex_(NULL), + fallback_database_(NULL), + default_error_collector_(NULL), + underlay_(NULL), + tables_(new Tables), + enforce_dependencies_(true), + last_internal_build_generated_file_call_(NULL) {} + +DescriptorPool::DescriptorPool(DescriptorDatabase* fallback_database, + ErrorCollector* error_collector) + : mutex_(new Mutex), + fallback_database_(fallback_database), + default_error_collector_(error_collector), + underlay_(NULL), + tables_(new Tables), + enforce_dependencies_(true), + last_internal_build_generated_file_call_(NULL) { +} + +DescriptorPool::DescriptorPool(const DescriptorPool* underlay) + : mutex_(NULL), + fallback_database_(NULL), + default_error_collector_(NULL), + underlay_(underlay), + tables_(new Tables), + last_internal_build_generated_file_call_(NULL) {} + +DescriptorPool::~DescriptorPool() { + if (mutex_ != NULL) delete mutex_; +} + +// DescriptorPool::BuildFile() defined later. +// DescriptorPool::BuildFileCollectingErrors() defined later. +// DescriptorPool::InternalBuildGeneratedFile() defined later. + +const DescriptorPool* DescriptorPool::generated_pool() { + return internal_generated_pool(); +} + +DescriptorPool* DescriptorPool::internal_generated_pool() { + static DescriptorPool singleton; + return &singleton; +} + +void DescriptorPool::InternalDontEnforceDependencies() { + enforce_dependencies_ = false; +} + +// Find*By* methods ================================================== + +// TODO(kenton): There's a lot of repeated code here, but I'm not sure if +// there's any good way to factor it out. Think about this some time when +// there's nothing more important to do (read: never). + +const FileDescriptor* DescriptorPool::FindFileByName(const string& name) const { + MutexLockMaybe lock(mutex_); + const FileDescriptor* result = tables_->FindFile(name); + if (result != NULL) return result; + if (underlay_ != NULL) { + const FileDescriptor* result = underlay_->FindFileByName(name); + if (result != NULL) return result; + } + if (TryFindFileInFallbackDatabase(name)) { + const FileDescriptor* result = tables_->FindFile(name); + if (result != NULL) return result; + } + return NULL; +} + +const FileDescriptor* DescriptorPool::FindFileContainingSymbol( + const string& symbol_name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbol(symbol_name); + if (!result.IsNull()) return result.GetFile(); + if (underlay_ != NULL) { + const FileDescriptor* result = + underlay_->FindFileContainingSymbol(symbol_name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(symbol_name)) { + Symbol result = tables_->FindSymbol(symbol_name); + if (!result.IsNull()) return result.GetFile(); + } + return NULL; +} + +const Descriptor* DescriptorPool::FindMessageTypeByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); + if (!result.IsNull()) return result.descriptor; + if (underlay_ != NULL) { + const Descriptor* result = underlay_->FindMessageTypeByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::MESSAGE); + if (!result.IsNull()) return result.descriptor; + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindFieldByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = underlay_->FindFieldByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindExtensionByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = underlay_->FindExtensionByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } + } + return NULL; +} + +const EnumDescriptor* DescriptorPool::FindEnumTypeByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); + if (!result.IsNull()) return result.enum_descriptor; + if (underlay_ != NULL) { + const EnumDescriptor* result = underlay_->FindEnumTypeByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM); + if (!result.IsNull()) return result.enum_descriptor; + } + return NULL; +} + +const EnumValueDescriptor* DescriptorPool::FindEnumValueByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); + if (!result.IsNull()) return result.enum_value_descriptor; + if (underlay_ != NULL) { + const EnumValueDescriptor* result = underlay_->FindEnumValueByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::ENUM_VALUE); + if (!result.IsNull()) return result.enum_value_descriptor; + } + return NULL; +} + +const ServiceDescriptor* DescriptorPool::FindServiceByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); + if (!result.IsNull()) return result.service_descriptor; + if (underlay_ != NULL) { + const ServiceDescriptor* result = underlay_->FindServiceByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::SERVICE); + if (!result.IsNull()) return result.service_descriptor; + } + return NULL; +} + +const MethodDescriptor* DescriptorPool::FindMethodByName( + const string& name) const { + MutexLockMaybe lock(mutex_); + Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); + if (!result.IsNull()) return result.method_descriptor; + if (underlay_ != NULL) { + const MethodDescriptor* result = underlay_->FindMethodByName(name); + if (result != NULL) return result; + } + if (TryFindSymbolInFallbackDatabase(name)) { + Symbol result = tables_->FindSymbolOfType(name, Symbol::METHOD); + if (!result.IsNull()) return result.method_descriptor; + } + return NULL; +} + +const FieldDescriptor* DescriptorPool::FindExtensionByNumber( + const Descriptor* extendee, int number) const { + MutexLockMaybe lock(mutex_); + const FieldDescriptor* result = tables_->FindFieldByNumber(extendee, number); + if (result != NULL && result->is_extension()) { + return result; + } + if (underlay_ != NULL) { + const FieldDescriptor* result = + underlay_->FindExtensionByNumber(extendee, number); + if (result != NULL) return result; + } + if (TryFindExtensionInFallbackDatabase(extendee, number)) { + const FieldDescriptor* result = + tables_->FindFieldByNumber(extendee, number); + if (result != NULL && result->is_extension()) { + return result; + } + } + return NULL; +} + +// ------------------------------------------------------------------- + +const FieldDescriptor* +Descriptor::FindFieldByNumber(int key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + const FieldDescriptor* result = + file()->pool()->tables_->FindFieldByNumber(this, key); + if (result == NULL || result->is_extension()) { + return NULL; + } else { + return result; + } +} + +const FieldDescriptor* +Descriptor::FindFieldByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && !result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +const FieldDescriptor* +Descriptor::FindExtensionByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +const Descriptor* +Descriptor::FindNestedTypeByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::MESSAGE); + if (!result.IsNull()) { + return result.descriptor; + } else { + return NULL; + } +} + +const EnumDescriptor* +Descriptor::FindEnumTypeByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM); + if (!result.IsNull()) { + return result.enum_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +Descriptor::FindEnumValueByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType( + this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +EnumDescriptor::FindValueByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType( + this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +EnumDescriptor::FindValueByNumber(int key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + return file()->pool()->tables_->FindEnumValueByNumber(this, key); +} + +const MethodDescriptor* +ServiceDescriptor::FindMethodByName(const string& key) const { + MutexLockMaybe lock(file()->pool()->mutex_); + Symbol result = + file()->pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::METHOD); + if (!result.IsNull()) { + return result.method_descriptor; + } else { + return NULL; + } +} + +const Descriptor* +FileDescriptor::FindMessageTypeByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::MESSAGE); + if (!result.IsNull()) { + return result.descriptor; + } else { + return NULL; + } +} + +const EnumDescriptor* +FileDescriptor::FindEnumTypeByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM); + if (!result.IsNull()) { + return result.enum_descriptor; + } else { + return NULL; + } +} + +const EnumValueDescriptor* +FileDescriptor::FindEnumValueByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::ENUM_VALUE); + if (!result.IsNull()) { + return result.enum_value_descriptor; + } else { + return NULL; + } +} + +const ServiceDescriptor* +FileDescriptor::FindServiceByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::SERVICE); + if (!result.IsNull()) { + return result.service_descriptor; + } else { + return NULL; + } +} + +const FieldDescriptor* +FileDescriptor::FindExtensionByName(const string& key) const { + MutexLockMaybe lock(pool()->mutex_); + Symbol result = + pool()->tables_->FindNestedSymbolOfType(this, key, Symbol::FIELD); + if (!result.IsNull() && result.field_descriptor->is_extension()) { + return result.field_descriptor; + } else { + return NULL; + } +} + +bool Descriptor::IsExtensionNumber(int number) const { + // Linear search should be fine because we don't expect a message to have + // more than a couple extension ranges. + for (int i = 0; i < extension_range_count(); i++) { + if (number >= extension_range(i)->start && + number < extension_range(i)->end) { + return true; + } + } + return false; +} + +// ------------------------------------------------------------------- + +bool DescriptorPool::TryFindFileInFallbackDatabase(const string& name) const { + if (fallback_database_ == NULL) return false; + + if (tables_->known_bad_files_.count(name) > 0) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileByName(name, &file_proto) || + BuildFileFromDatabase(file_proto) == NULL) { + tables_->known_bad_files_.insert(name); + return false; + } + + return true; +} + +bool DescriptorPool::TryFindSymbolInFallbackDatabase(const string& name) const { + if (fallback_database_ == NULL) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileContainingSymbol(name, &file_proto)) { + return false; + } + + if (tables_->FindFile(file_proto.name()) != NULL) { + // We've already loaded this file, and it apparently doesn't contain the + // symbol we're looking for. Some DescriptorDatabases return false + // positives. + return false; + } + + if (BuildFileFromDatabase(file_proto) == NULL) { + return false; + } + + return true; +} + +bool DescriptorPool::TryFindExtensionInFallbackDatabase( + const Descriptor* containing_type, int field_number) const { + if (fallback_database_ == NULL) return false; + + FileDescriptorProto file_proto; + if (!fallback_database_->FindFileContainingExtension( + containing_type->full_name(), field_number, &file_proto)) { + return false; + } + + if (tables_->FindFile(file_proto.name()) != NULL) { + // We've already loaded this file, and it apparently doesn't contain the + // extension we're looking for. Some DescriptorDatabases return false + // positives. + return false; + } + + if (BuildFileFromDatabase(file_proto) == NULL) { + return false; + } + + return true; +} + +// =================================================================== + +string FieldDescriptor::DefaultValueAsString(bool quote_string_type) const { + GOOGLE_CHECK(has_default_value()) << "No default value"; + switch (cpp_type()) { + case CPPTYPE_INT32: + return SimpleItoa(default_value_int32()); + break; + case CPPTYPE_INT64: + return SimpleItoa(default_value_int64()); + break; + case CPPTYPE_UINT32: + return SimpleItoa(default_value_uint32()); + break; + case CPPTYPE_UINT64: + return SimpleItoa(default_value_uint64()); + break; + case CPPTYPE_FLOAT: + return SimpleFtoa(default_value_float()); + break; + case CPPTYPE_DOUBLE: + return SimpleDtoa(default_value_double()); + break; + case CPPTYPE_BOOL: + return default_value_bool() ? "true" : "false"; + break; + case CPPTYPE_STRING: + if (quote_string_type) { + return "\"" + CEscape(default_value_string()) + "\""; + } else { + if (type() == TYPE_BYTES) { + return CEscape(default_value_string()); + } else { + return default_value_string(); + } + } + break; + case CPPTYPE_ENUM: + return default_value_enum()->name(); + break; + case CPPTYPE_MESSAGE: + GOOGLE_LOG(DFATAL) << "Messages can't have default values!"; + break; + } + GOOGLE_LOG(FATAL) << "Can't get here: failed to get default value as string"; + return ""; +} + +// CopyTo methods ==================================================== + +void FileDescriptor::CopyTo(FileDescriptorProto* proto) const { + proto->set_name(name()); + if (!package().empty()) proto->set_package(package()); + + for (int i = 0; i < dependency_count(); i++) { + proto->add_dependency(dependency(i)->name()); + } + + for (int i = 0; i < message_type_count(); i++) { + message_type(i)->CopyTo(proto->add_message_type()); + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->CopyTo(proto->add_enum_type()); + } + for (int i = 0; i < service_count(); i++) { + service(i)->CopyTo(proto->add_service()); + } + for (int i = 0; i < extension_count(); i++) { + extension(i)->CopyTo(proto->add_extension()); + } + + if (&options() != &FileOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void Descriptor::CopyTo(DescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < field_count(); i++) { + field(i)->CopyTo(proto->add_field()); + } + for (int i = 0; i < nested_type_count(); i++) { + nested_type(i)->CopyTo(proto->add_nested_type()); + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->CopyTo(proto->add_enum_type()); + } + for (int i = 0; i < extension_range_count(); i++) { + DescriptorProto::ExtensionRange* range = proto->add_extension_range(); + range->set_start(extension_range(i)->start); + range->set_end(extension_range(i)->end); + } + for (int i = 0; i < extension_count(); i++) { + extension(i)->CopyTo(proto->add_extension()); + } + + if (&options() != &MessageOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void FieldDescriptor::CopyTo(FieldDescriptorProto* proto) const { + proto->set_name(name()); + proto->set_number(number()); + proto->set_label(static_cast(label())); + proto->set_type(static_cast(type())); + + if (is_extension()) { + proto->set_extendee("."); + proto->mutable_extendee()->append(containing_type()->full_name()); + } + + if (cpp_type() == CPPTYPE_MESSAGE) { + proto->set_type_name("."); + proto->mutable_type_name()->append(message_type()->full_name()); + } else if (cpp_type() == CPPTYPE_ENUM) { + proto->set_type_name("."); + proto->mutable_type_name()->append(enum_type()->full_name()); + } + + if (has_default_value()) { + proto->set_default_value(DefaultValueAsString(false)); + } + + if (&options() != &FieldOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void EnumDescriptor::CopyTo(EnumDescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < value_count(); i++) { + value(i)->CopyTo(proto->add_value()); + } + + if (&options() != &EnumOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void EnumValueDescriptor::CopyTo(EnumValueDescriptorProto* proto) const { + proto->set_name(name()); + proto->set_number(number()); + + if (&options() != &EnumValueOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void ServiceDescriptor::CopyTo(ServiceDescriptorProto* proto) const { + proto->set_name(name()); + + for (int i = 0; i < method_count(); i++) { + method(i)->CopyTo(proto->add_method()); + } + + if (&options() != &ServiceOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +void MethodDescriptor::CopyTo(MethodDescriptorProto* proto) const { + proto->set_name(name()); + + proto->set_input_type("."); + proto->mutable_input_type()->append(input_type()->full_name()); + proto->set_output_type("."); + proto->mutable_output_type()->append(output_type()->full_name()); + + if (&options() != &MethodOptions::default_instance()) { + proto->mutable_options()->CopyFrom(options()); + } +} + +// DebugString methods =============================================== + +namespace { + +// Used by each of the option formatters. +bool RetrieveOptions(const Message &options, vector *option_entries) { + option_entries->clear(); + const Reflection* reflection = options.GetReflection(); + vector fields; + reflection->ListFields(options, &fields); + for (int i = 0; i < fields.size(); i++) { + // Doesn't make sense to have message type fields here + if (fields[i]->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + continue; + } + int count = 1; + bool repeated = false; + if (fields[i]->is_repeated()) { + count = reflection->FieldSize(options, fields[i]); + repeated = true; + } + for (int j = 0; j < count; j++) { + string fieldval; + TextFormat::PrintFieldValueToString(options, fields[i], + repeated ? count : -1, &fieldval); + option_entries->push_back(fields[i]->name() + " = " + fieldval); + } + } + return !option_entries->empty(); +} + +// Formats options that all appear together in brackets. Does not include +// brackets. +bool FormatBracketedOptions(const Message &options, string *output) { + vector all_options; + if (RetrieveOptions(options, &all_options)) { + output->append(JoinStrings(all_options, ", ")); + } + return !all_options.empty(); +} + +// Formats options one per line +bool FormatLineOptions(int depth, const Message &options, string *output) { + string prefix(depth * 2, ' '); + vector all_options; + if (RetrieveOptions(options, &all_options)) { + for (int i = 0; i < all_options.size(); i++) { + strings::SubstituteAndAppend(output, "$0option $1;\n", + prefix, all_options[i]); + } + } + return !all_options.empty(); +} + +} // anonymous namespace + +string FileDescriptor::DebugString() const { + string contents = "syntax = \"proto2\";\n\n"; + + for (int i = 0; i < dependency_count(); i++) { + strings::SubstituteAndAppend(&contents, "import \"$0\";\n", + dependency(i)->name()); + } + + if (!package().empty()) { + strings::SubstituteAndAppend(&contents, "package $0;\n\n", package()); + } + + if (FormatLineOptions(0, options(), &contents)) { + contents.append("\n"); // add some space if we had options + } + + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->DebugString(0, &contents); + contents.append("\n"); + } + + // Find all the 'group' type extensions; we will not output their nested + // definitions (those will be done with their group field descriptor). + set groups; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(extension(i)->message_type()); + } + } + + for (int i = 0; i < message_type_count(); i++) { + if (groups.count(message_type(i)) == 0) { + strings::SubstituteAndAppend(&contents, "message $0", + message_type(i)->name()); + message_type(i)->DebugString(0, &contents); + contents.append("\n"); + } + } + + for (int i = 0; i < service_count(); i++) { + service(i)->DebugString(&contents); + contents.append("\n"); + } + + const Descriptor* containing_type = NULL; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->containing_type() != containing_type) { + if (i > 0) contents.append("}\n\n"); + containing_type = extension(i)->containing_type(); + strings::SubstituteAndAppend(&contents, "extend .$0 {\n", + containing_type->full_name()); + } + extension(i)->DebugString(1, &contents); + } + if (extension_count() > 0) contents.append("}\n\n"); + + return contents; +} + +string Descriptor::DebugString() const { + string contents; + strings::SubstituteAndAppend(&contents, "message $0", name()); + DebugString(0, &contents); + return contents; +} + +void Descriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + contents->append(" {\n"); + + FormatLineOptions(depth, options(), contents); + + // Find all the 'group' types for fields and extensions; we will not output + // their nested definitions (those will be done with their group field + // descriptor). + set groups; + for (int i = 0; i < field_count(); i++) { + if (field(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(field(i)->message_type()); + } + } + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->type() == FieldDescriptor::TYPE_GROUP) { + groups.insert(extension(i)->message_type()); + } + } + + for (int i = 0; i < nested_type_count(); i++) { + if (groups.count(nested_type(i)) == 0) { + strings::SubstituteAndAppend(contents, "$0 message $1", + prefix, nested_type(i)->name()); + nested_type(i)->DebugString(depth, contents); + } + } + for (int i = 0; i < enum_type_count(); i++) { + enum_type(i)->DebugString(depth, contents); + } + for (int i = 0; i < field_count(); i++) { + field(i)->DebugString(depth, contents); + } + + for (int i = 0; i < extension_range_count(); i++) { + strings::SubstituteAndAppend(contents, "$0 extensions $1 to $2;\n", + prefix, + extension_range(i)->start, + extension_range(i)->end - 1); + } + + // Group extensions by what they extend, so they can be printed out together. + const Descriptor* containing_type = NULL; + for (int i = 0; i < extension_count(); i++) { + if (extension(i)->containing_type() != containing_type) { + if (i > 0) strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + containing_type = extension(i)->containing_type(); + strings::SubstituteAndAppend(contents, "$0 extend .$1 {\n", + prefix, containing_type->full_name()); + } + extension(i)->DebugString(depth + 1, contents); + } + if (extension_count() > 0) + strings::SubstituteAndAppend(contents, "$0 }\n", prefix); + + strings::SubstituteAndAppend(contents, "$0}\n", prefix); +} + +string FieldDescriptor::DebugString() const { + string contents; + int depth = 0; + if (is_extension()) { + strings::SubstituteAndAppend(&contents, "extend .$0 {\n", + containing_type()->full_name()); + depth = 1; + } + DebugString(depth, &contents); + if (is_extension()) { + contents.append("}\n"); + } + return contents; +} + +void FieldDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + string field_type; + switch (type()) { + case TYPE_MESSAGE: + field_type = "." + message_type()->full_name(); + break; + case TYPE_ENUM: + field_type = "." + enum_type()->full_name(); + break; + default: + field_type = kTypeToName[type()]; + } + + strings::SubstituteAndAppend(contents, "$0$1 $2 $3 = $4", + prefix, + kLabelToName[label()], + field_type, + type() == TYPE_GROUP ? message_type()->name() : + name(), + number()); + + bool bracketed = false; + if (has_default_value()) { + bracketed = true; + strings::SubstituteAndAppend(contents, " [default = $0", + DefaultValueAsString(true)); + } + + string formatted_options; + if (FormatBracketedOptions(options(), &formatted_options)) { + contents->append(bracketed ? ", " : " ["); + bracketed = true; + contents->append(formatted_options); + } + + if (bracketed) { + contents->append("]"); + } + + if (type() == TYPE_GROUP) { + message_type()->DebugString(depth, contents); + } else { + contents->append(";\n"); + } +} + +string EnumDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void EnumDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + strings::SubstituteAndAppend(contents, "$0enum $1 {\n", + prefix, name()); + + FormatLineOptions(depth, options(), contents); + + for (int i = 0; i < value_count(); i++) { + value(i)->DebugString(depth, contents); + } + strings::SubstituteAndAppend(contents, "$0}\n", prefix); +} + +string EnumValueDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void EnumValueDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + strings::SubstituteAndAppend(contents, "$0$1 = $2", + prefix, name(), number()); + + string formatted_options; + if (FormatBracketedOptions(options(), &formatted_options)) { + strings::SubstituteAndAppend(contents, " [$0]", formatted_options); + } + contents->append(";\n"); +} + +string ServiceDescriptor::DebugString() const { + string contents; + DebugString(&contents); + return contents; +} + +void ServiceDescriptor::DebugString(string *contents) const { + strings::SubstituteAndAppend(contents, "service $0 {\n", name()); + + FormatLineOptions(1, options(), contents); + + for (int i = 0; i < method_count(); i++) { + method(i)->DebugString(1, contents); + } + + contents->append("}\n"); +} + +string MethodDescriptor::DebugString() const { + string contents; + DebugString(0, &contents); + return contents; +} + +void MethodDescriptor::DebugString(int depth, string *contents) const { + string prefix(depth * 2, ' '); + ++depth; + strings::SubstituteAndAppend(contents, "$0rpc $1(.$2) returns (.$3)", + prefix, name(), + input_type()->full_name(), + output_type()->full_name()); + + string formatted_options; + if (FormatLineOptions(depth, options(), &formatted_options)) { + strings::SubstituteAndAppend(contents, " {\n$0$1}\n", + formatted_options, prefix); + } else { + contents->append(";\n"); + } +} +// =================================================================== + +class DescriptorBuilder { + public: + DescriptorBuilder(const DescriptorPool* pool, + DescriptorPool::Tables* tables, + DescriptorPool::ErrorCollector* error_collector); + ~DescriptorBuilder(); + + const FileDescriptor* BuildFile(const FileDescriptorProto& proto); + + private: + const DescriptorPool* pool_; + DescriptorPool::Tables* tables_; // for convenience + DescriptorPool::ErrorCollector* error_collector_; + bool had_errors_; + string filename_; + FileDescriptor* file_; + + // If LookupSymbol() finds a symbol that is in a file which is not a declared + // dependency of this file, it will fail, but will set + // possible_undeclared_dependency_ to point at that file. This is only used + // by AddNotDefinedError() to report a more useful error message. + const FileDescriptor* possible_undeclared_dependency_; + + void AddError(const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error); + + // Adds an error indicating that undefined_symbol was not defined. Must + // only be called after LookupSymbol() fails. + void AddNotDefinedError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& undefined_symbol); + + // Silly helper which determines if the given file is in the given package. + // I.e., either file->package() == package_name or file->package() is a + // nested package within package_name. + bool IsInPackage(const FileDescriptor* file, const string& package_name); + + // Like tables_->FindSymbol(), but additionally: + // - Search the pool's underlay if not found in tables_. + // - Insure that the resulting Symbol is from one of the file's declared + // dependencies. + Symbol FindSymbol(const string& name); + + // Like FindSymbol(), but looks up the name relative to some other symbol + // name. This first searches syblings of relative_to, then siblings of its + // parents, etc. For example, LookupSymbol("foo.bar", "baz.qux.corge") makes + // the following calls, returning the first non-null result: + // FindSymbol("baz.qux.foo.bar"), FindSymbol("baz.foo.bar"), + // FindSymbol("foo.bar"). + Symbol LookupSymbol(const string& name, const string& relative_to); + + // Calls tables_->AddSymbol() and records an error if it fails. Returns + // true if successful or false if failed, though most callers can ignore + // the return value since an error has already been recorded. + bool AddSymbol(const string& full_name, + const void* parent, const string& name, + const Message& proto, Symbol symbol); + + // Like AddSymbol(), but succeeds if the symbol is already defined as long + // as the existing definition is also a package (because it's OK to define + // the same package in two different files). Also adds all parents of the + // packgae to the symbol table (e.g. AddPackage("foo.bar", ...) will add + // "foo.bar" and "foo" to the table). + void AddPackage(const string& name, const Message& proto, + const FileDescriptor* file); + + // Checks that the symbol name contains only alphanumeric characters and + // underscores. Records an error otherwise. + void ValidateSymbolName(const string& name, const string& full_name, + const Message& proto); + + // Used by BUILD_ARRAY macro (below) to avoid having to have the type + // specified as a macro parameter. + template + inline void AllocateArray(int size, Type** output) { + *output = tables_->AllocateArray(size); + } + + // These methods all have the same signature for the sake of the BUILD_ARRAY + // macro, below. + void BuildMessage(const DescriptorProto& proto, + const Descriptor* parent, + Descriptor* result); + void BuildFieldOrExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result, + bool is_extension); + void BuildField(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result) { + BuildFieldOrExtension(proto, parent, result, false); + } + void BuildExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result) { + BuildFieldOrExtension(proto, parent, result, true); + } + void BuildExtensionRange(const DescriptorProto::ExtensionRange& proto, + const Descriptor* parent, + Descriptor::ExtensionRange* result); + void BuildEnum(const EnumDescriptorProto& proto, + const Descriptor* parent, + EnumDescriptor* result); + void BuildEnumValue(const EnumValueDescriptorProto& proto, + const EnumDescriptor* parent, + EnumValueDescriptor* result); + void BuildService(const ServiceDescriptorProto& proto, + const void* dummy, + ServiceDescriptor* result); + void BuildMethod(const MethodDescriptorProto& proto, + const ServiceDescriptor* parent, + MethodDescriptor* result); + + void CrossLinkFile(FileDescriptor* file, const FileDescriptorProto& proto); + void CrossLinkMessage(Descriptor* message, const DescriptorProto& proto); + void CrossLinkField(FieldDescriptor* field, + const FieldDescriptorProto& proto); + void CrossLinkService(ServiceDescriptor* service, + const ServiceDescriptorProto& proto); + void CrossLinkMethod(MethodDescriptor* method, + const MethodDescriptorProto& proto); + void CrossLinkMapKey(FieldDescriptor* field, + const FieldDescriptorProto& proto); +}; + +const FileDescriptor* DescriptorPool::BuildFile( + const FileDescriptorProto& proto) { + GOOGLE_CHECK(fallback_database_ == NULL) + << "Cannot call BuildFile on a DescriptorPool that uses a " + "DescriptorDatabase. You must instead find a way to get your file " + "into the underlying database."; + GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + return DescriptorBuilder(this, tables_.get(), NULL).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::BuildFileCollectingErrors( + const FileDescriptorProto& proto, + ErrorCollector* error_collector) { + GOOGLE_CHECK(fallback_database_ == NULL) + << "Cannot call BuildFile on a DescriptorPool that uses a " + "DescriptorDatabase. You must instead find a way to get your file " + "into the underlying database."; + GOOGLE_CHECK(mutex_ == NULL); // Implied by the above GOOGLE_CHECK. + return DescriptorBuilder(this, tables_.get(), + error_collector).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::BuildFileFromDatabase( + const FileDescriptorProto& proto) const { + mutex_->AssertHeld(); + return DescriptorBuilder(this, tables_.get(), + default_error_collector_).BuildFile(proto); +} + +const FileDescriptor* DescriptorPool::InternalBuildGeneratedFile( + const void* data, int size) { + // So, this function is called in the process of initializing the + // descriptors for generated proto classes. Each generated .pb.cc file + // has an internal procedure called BuildDescriptors() which is called the + // first time one of its descriptors is accessed, and that function calls + // this one in order to parse the raw bytes of the FileDescriptorProto + // representing the file. + // + // Note, though, that FileDescriptorProto is itself a generated protocol + // message. So, when we attempt to construct one below, it will attempt + // to initialize its own descriptors via its own BuildDescriptors() method. + // This will in turn cause InternalBuildGeneratedFile() to build + // descriptor.proto's descriptors. + // + // We are saved from an infinite loop by the fact that BuildDescriptors() + // only does anything the first time it is called. That is, the first few + // lines of any BuildDescriptors() procedure look like this: + // void BuildDescriptors() { + // static bool already_here = false; + // if (already_here) return; + // already_here = true; + // ... + // So, when descriptor.pb.cc's BuildDescriptors() is called recursively, it + // will end up just returning without doing anything. The result is that + // all of the descriptors for FileDescriptorProto and friends will just be + // NULL. + // + // Luckily, it turns out that our limited use of FileDescriptorProto within + // InternalBuildGeneratedFile() does not require that its descriptors be + // initialized. So, this ends up working. As soon as + // InternalBuildGeneratedFile() returns, the descriptors will be initialized + // by the original call to BuildDescriptors(), and everything will be happy + // again. + // + // If this turns out to be too fragile a hack, there are other ways we + // can accomplish bootstrapping here (like building the descriptor for + // descriptor.proto manually), but if this works then it's a lot easier. + // + // Note that because this is only triggered at static initialization time, + // there are no thread-safety concerns here. + FileDescriptorProto proto; + GOOGLE_CHECK(proto.ParseFromArray(data, size)); + const FileDescriptor* result = BuildFile(proto); + GOOGLE_CHECK(result != NULL); + + return result; +} + +DescriptorBuilder::DescriptorBuilder( + const DescriptorPool* pool, + DescriptorPool::Tables* tables, + DescriptorPool::ErrorCollector* error_collector) + : pool_(pool), + tables_(tables), + error_collector_(error_collector), + had_errors_(false), + possible_undeclared_dependency_(NULL) {} + +DescriptorBuilder::~DescriptorBuilder() {} + +void DescriptorBuilder::AddError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& error) { + if (error_collector_ == NULL) { + if (!had_errors_) { + GOOGLE_LOG(ERROR) << "Invalid proto descriptor for file \"" << filename_ + << "\":"; + } + GOOGLE_LOG(ERROR) << " " << element_name << ": " << error; + } else { + error_collector_->AddError(filename_, element_name, + &descriptor, location, error); + } + had_errors_ = true; +} + +void DescriptorBuilder::AddNotDefinedError( + const string& element_name, + const Message& descriptor, + DescriptorPool::ErrorCollector::ErrorLocation location, + const string& undefined_symbol) { + if (possible_undeclared_dependency_ == NULL) { + AddError(element_name, descriptor, location, + "\"" + undefined_symbol + "\" is not defined."); + } else { + AddError(element_name, descriptor, location, + "\"" + undefined_symbol + "\" seems to be defined in \"" + + possible_undeclared_dependency_->name() + "\", which is not " + "imported by \"" + filename_ + "\". To use it here, please " + "add the necessary import."); + } +} + +bool DescriptorBuilder::IsInPackage(const FileDescriptor* file, + const string& package_name) { + return HasPrefixString(file->package(), package_name) && + (file->package().size() == package_name.size() || + file->package()[package_name.size()] == '.'); +} + +Symbol DescriptorBuilder::FindSymbol(const string& name) { + Symbol result; + + // We need to search our pool and all its underlays. + const DescriptorPool* pool = pool_; + while (true) { + // If we are looking at an underlay, we must lock its mutex_, since we are + // accessing the underlay's tables_ dircetly. + MutexLockMaybe lock((pool == pool_) ? NULL : pool->mutex_); + + // Note that we don't have to check fallback_database_ here because the + // symbol has to be in one of its file's direct dependencies, and we have + // already loaded those by the time we get here. + result = pool->tables_->FindSymbol(name); + if (!result.IsNull()) break; + if (pool->underlay_ == NULL) return kNullSymbol; + pool = pool->underlay_; + } + + if (!pool_->enforce_dependencies_) { + // Hack for CompilerUpgrader. + return result; + } + + // Only find symbols which were defined in this file or one of its + // dependencies. + const FileDescriptor* file = result.GetFile(); + if (file == file_) return result; + for (int i = 0; i < file_->dependency_count(); i++) { + if (file == file_->dependency(i)) return result; + } + + if (result.type == Symbol::PACKAGE) { + // Arg, this is overcomplicated. The symbol is a package name. It could + // be that the package was defined in multiple files. result.GetFile() + // returns the first file we saw that used this package. We've determined + // that that file is not a direct dependency of the file we are currently + // building, but it could be that some other file which *is* a direct + // dependency also defines the same package. We can't really rule out this + // symbol unless none of the dependencies define it. + if (IsInPackage(file_, name)) return result; + for (int i = 0; i < file_->dependency_count(); i++) { + if (IsInPackage(file_->dependency(i), name)) return result; + } + } + + possible_undeclared_dependency_ = file; + return kNullSymbol; +} + +Symbol DescriptorBuilder::LookupSymbol( + const string& name, const string& relative_to) { + possible_undeclared_dependency_ = NULL; + + if (name.size() > 0 && name[0] == '.') { + // Fully-qualified name. + return FindSymbol(name.substr(1)); + } + + // If name is something like "Foo.Bar.baz", and symbols named "Foo" are + // defined in multiple parent scopes, we only want to find "Bar.baz" in the + // innermost one. E.g., the following should produce an error: + // message Bar { message Baz {} } + // message Foo { + // message Bar { + // } + // optional Bar.Baz baz = 1; + // } + // So, we look for just "Foo" first, then look for "Bar.baz" within it if + // found. + int name_dot_pos = name.find_first_of('.'); + string first_part_of_name; + if (name_dot_pos == string::npos) { + first_part_of_name = name; + } else { + first_part_of_name = name.substr(0, name_dot_pos); + } + + string scope_to_try(relative_to); + + while (true) { + // Chop off the last component of the scope. + string::size_type dot_pos = scope_to_try.find_last_of('.'); + if (dot_pos == string::npos) { + return FindSymbol(name); + } else { + scope_to_try.erase(dot_pos); + } + + // Append ".first_part_of_name" and try to find. + string::size_type old_size = scope_to_try.size(); + scope_to_try.append(1, '.'); + scope_to_try.append(first_part_of_name); + Symbol result = FindSymbol(scope_to_try); + if (!result.IsNull()) { + if (first_part_of_name.size() < name.size()) { + // name is a compound symbol, of which we only found the first part. + // Now try to look up the rest of it. + scope_to_try.append(name, first_part_of_name.size(), + name.size() - first_part_of_name.size()); + result = FindSymbol(scope_to_try); + } + return result; + } + + // Not found. Remove the name so we can try again. + scope_to_try.erase(old_size); + } +} + +bool DescriptorBuilder::AddSymbol( + const string& full_name, const void* parent, const string& name, + const Message& proto, Symbol symbol) { + // If the caller passed NULL for the parent, the symbol is at file scope. + // Use its file as the parent instead. + if (parent == NULL) parent = file_; + + if (tables_->AddSymbol(full_name, parent, name, symbol)) { + return true; + } else { + const FileDescriptor* other_file = tables_->FindSymbol(full_name).GetFile(); + if (other_file == file_) { + string::size_type dot_pos = full_name.find_last_of('.'); + if (dot_pos == string::npos) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name + "\" is already defined."); + } else { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name.substr(dot_pos + 1) + + "\" is already defined in \"" + + full_name.substr(0, dot_pos) + "\"."); + } + } else { + // Symbol seems to have been defined in a different file. + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + full_name + "\" is already defined in file \"" + + other_file->name() + "\"."); + } + return false; + } +} + +void DescriptorBuilder::AddPackage( + const string& name, const Message& proto, const FileDescriptor* file) { + if (tables_->AddSymbol(name, NULL, name, Symbol(file))) { + // Success. Also add parent package, if any. + string::size_type dot_pos = name.find_last_of('.'); + if (dot_pos == string::npos) { + // No parents. + ValidateSymbolName(name, name, proto); + } else { + // Has parent. + string* parent_name = tables_->AllocateString(name.substr(0, dot_pos)); + AddPackage(*parent_name, proto, file); + ValidateSymbolName(name.substr(dot_pos + 1), name, proto); + } + } else { + Symbol existing_symbol = tables_->FindSymbol(name); + // It's OK to redefine a package. + if (existing_symbol.type != Symbol::PACKAGE) { + // Symbol seems to have been defined in a different file. + AddError(name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + name + "\" is already defined (as something other than " + "a package) in file \"" + existing_symbol.GetFile()->name() + + "\"."); + } + } +} + +void DescriptorBuilder::ValidateSymbolName( + const string& name, const string& full_name, const Message& proto) { + if (name.empty()) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "Missing name."); + } else { + for (int i = 0; i < name.size(); i++) { + // I don't trust isalnum() due to locales. :( + if ((name[i] < 'a' || 'z' < name[i]) && + (name[i] < 'A' || 'Z' < name[i]) && + (name[i] < '0' || '9' < name[i]) && + (name[i] != '_')) { + AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, + "\"" + name + "\" is not a valid identifier."); + } + } + } +} + +// ------------------------------------------------------------------- + +// A common pattern: We want to convert a repeated field in the descriptor +// to an array of values, calling some method to build each value. +#define BUILD_ARRAY(INPUT, OUTPUT, NAME, METHOD, PARENT) \ + OUTPUT->NAME##_count_ = INPUT.NAME##_size(); \ + AllocateArray(INPUT.NAME##_size(), &OUTPUT->NAME##s_); \ + for (int i = 0; i < INPUT.NAME##_size(); i++) { \ + METHOD(INPUT.NAME(i), PARENT, OUTPUT->NAME##s_ + i); \ + } + +const FileDescriptor* DescriptorBuilder::BuildFile( + const FileDescriptorProto& proto) { + filename_ = proto.name(); + + // Check to see if this file is already on the pending files list. + // TODO(kenton): Allow recursive imports? It may not work with some + // (most?) programming languages. E.g., in C++, a forward declaration + // of a type is not sufficient to allow it to be used even in a + // generated header file due to inlining. This could perhaps be + // worked around using tricks involving inserting #include statements + // mid-file, but that's pretty ugly, and I'm pretty sure there are + // some languages out there that do not allow recursive dependencies + // at all. + for (int i = 0; i < tables_->pending_files_.size(); i++) { + if (tables_->pending_files_[i] == proto.name()) { + string error_message("File recursively imports itself: "); + for (; i < tables_->pending_files_.size(); i++) { + error_message.append(tables_->pending_files_[i]); + error_message.append(" -> "); + } + error_message.append(proto.name()); + + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + error_message); + return NULL; + } + } + + // If we have a fallback_database_, attempt to load all dependencies now, + // before checkpointing tables_. This avoids confusion with recursive + // checkpoints. + if (pool_->fallback_database_ != NULL) { + tables_->pending_files_.push_back(proto.name()); + for (int i = 0; i < proto.dependency_size(); i++) { + if (tables_->FindFile(proto.dependency(i)) == NULL && + (pool_->underlay_ == NULL || + pool_->underlay_->FindFileByName(proto.dependency(i)) == NULL)) { + // We don't care what this returns since we'll find out below anyway. + pool_->TryFindFileInFallbackDatabase(proto.dependency(i)); + } + } + tables_->pending_files_.pop_back(); + } + + // Checkpoint the tables so that we can roll back if something goes wrong. + tables_->Checkpoint(); + + FileDescriptor* result = tables_->Allocate(); + file_ = result; + + if (!proto.has_name()) { + AddError("", proto, DescriptorPool::ErrorCollector::OTHER, + "Missing field: FileDescriptorProto.name."); + } + + result->name_ = tables_->AllocateString(proto.name()); + if (proto.has_package()) { + result->package_ = tables_->AllocateString(proto.package()); + } else { + // We cannot rely on proto.package() returning a valid string if + // proto.has_package() is false, because we might be running at static + // initialization time, in which case default values have not yet been + // initialized. + result->package_ = tables_->AllocateString(""); + } + result->pool_ = pool_; + + // Add to tables. + if (!tables_->AddFile(result)) { + AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, + "A file with this name is already in the pool."); + // Bail out early so that if this is actually the exact same file, we + // don't end up reporting that every single symbol is already defined. + tables_->Rollback(); + return NULL; + } + if (!result->package().empty()) { + AddPackage(result->package(), proto, result); + } + + // Make sure all dependencies are loaded. + set seen_dependencies; + result->dependency_count_ = proto.dependency_size(); + result->dependencies_ = + tables_->AllocateArray(proto.dependency_size()); + for (int i = 0; i < proto.dependency_size(); i++) { + if (!seen_dependencies.insert(proto.dependency(i)).second) { + AddError(proto.name(), proto, + DescriptorPool::ErrorCollector::OTHER, + "Import \"" + proto.dependency(i) + "\" was listed twice."); + } + + const FileDescriptor* dependency = tables_->FindFile(proto.dependency(i)); + if (dependency == NULL && pool_->underlay_ != NULL) { + dependency = pool_->underlay_->FindFileByName(proto.dependency(i)); + } + + if (dependency == NULL) { + string message; + if (pool_->fallback_database_ == NULL) { + message = "Import \"" + proto.dependency(i) + + "\" has not been loaded."; + } else { + message = "Import \"" + proto.dependency(i) + + "\" was not found or had errors."; + } + AddError(proto.name(), proto, + DescriptorPool::ErrorCollector::OTHER, + message); + } + + result->dependencies_[i] = dependency; + } + + // Convert children. + BUILD_ARRAY(proto, result, message_type, BuildMessage , NULL); + BUILD_ARRAY(proto, result, enum_type , BuildEnum , NULL); + BUILD_ARRAY(proto, result, service , BuildService , NULL); + BUILD_ARRAY(proto, result, extension , BuildExtension, NULL); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &FileOptions::default_instance(); + } else { + FileOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + // Cross-link. + CrossLinkFile(result, proto); + + if (had_errors_) { + tables_->Rollback(); + return NULL; + } else { + tables_->Checkpoint(); + return result; + } +} + +void DescriptorBuilder::BuildMessage(const DescriptorProto& proto, + const Descriptor* parent, + Descriptor* result) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->containing_type_ = parent; + + BUILD_ARRAY(proto, result, field , BuildField , result); + BUILD_ARRAY(proto, result, nested_type , BuildMessage , result); + BUILD_ARRAY(proto, result, enum_type , BuildEnum , result); + BUILD_ARRAY(proto, result, extension_range, BuildExtensionRange, result); + BUILD_ARRAY(proto, result, extension , BuildExtension , result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &MessageOptions::default_instance(); + } else { + MessageOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); + + // Check that no fields have numbers in extension ranges. + for (int i = 0; i < result->field_count(); i++) { + const FieldDescriptor* field = result->field(i); + for (int j = 0; j < result->extension_range_count(); j++) { + const Descriptor::ExtensionRange* range = result->extension_range(j); + if (range->start <= field->number() && field->number() < range->end) { + AddError(field->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Extension range $0 to $1 includes field \"$2\" ($3).", + range->start, range->end - 1, + field->name(), field->number())); + } + } + } + + // Check that extension ranges don't overlap. + for (int i = 0; i < result->extension_range_count(); i++) { + const Descriptor::ExtensionRange* range1 = result->extension_range(i); + for (int j = i + 1; j < result->extension_range_count(); j++) { + const Descriptor::ExtensionRange* range2 = result->extension_range(j); + if (range1->end > range2->start && range2->end > range1->start) { + AddError(result->full_name(), proto.extension_range(j), + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension range $0 to $1 overlaps with " + "already-defined range $2 to $3.", + range2->start, range2->end - 1, + range1->start, range1->end - 1)); + } + } + } +} + +void DescriptorBuilder::BuildFieldOrExtension(const FieldDescriptorProto& proto, + const Descriptor* parent, + FieldDescriptor* result, + bool is_extension) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->number_ = proto.number(); + result->type_ = static_cast(proto.type()); + result->label_ = static_cast(proto.label()); + result->is_extension_ = is_extension; + + // Some of these may be filled in when cross-linking. + result->containing_type_ = NULL; + result->extension_scope_ = NULL; + result->experimental_map_key_ = NULL; + result->message_type_ = NULL; + result->enum_type_ = NULL; + + result->has_default_value_ = proto.has_default_value(); + if (proto.has_type()) { + if (proto.has_default_value()) { + char* end_pos = NULL; + switch (result->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + result->default_value_int32_ = + strtol(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_INT64: + result->default_value_int64_ = + strto64(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_UINT32: + result->default_value_uint32_ = + strtoul(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_UINT64: + result->default_value_uint64_ = + strtou64(proto.default_value().c_str(), &end_pos, 0); + break; + case FieldDescriptor::CPPTYPE_FLOAT: + result->default_value_float_ = + NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + result->default_value_double_ = + NoLocaleStrtod(proto.default_value().c_str(), &end_pos); + break; + case FieldDescriptor::CPPTYPE_BOOL: + if (proto.default_value() == "true") { + result->default_value_bool_ = true; + } else if (proto.default_value() == "false") { + result->default_value_bool_ = false; + } else { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Boolean default must be true or false."); + } + break; + case FieldDescriptor::CPPTYPE_ENUM: + // This will be filled in when cross-linking. + result->default_value_enum_ = NULL; + break; + case FieldDescriptor::CPPTYPE_STRING: + if (result->type() == FieldDescriptor::TYPE_BYTES) { + result->default_value_string_ = tables_->AllocateString( + UnescapeCEscapeString(proto.default_value())); + } else { + result->default_value_string_ = + tables_->AllocateString(proto.default_value()); + } + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Messages can't have default values."); + result->has_default_value_ = false; + break; + } + + if (end_pos != NULL) { + // end_pos is only set non-NULL by the parsers for numeric types, above. + // This checks that the default was non-empty and had no extra junk + // after the end of the number. + if (proto.default_value().empty() || *end_pos != '\0') { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Couldn't parse default value."); + } + } + } else { + // No explicit default value + switch (result->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + result->default_value_int32_ = 0; + break; + case FieldDescriptor::CPPTYPE_INT64: + result->default_value_int64_ = 0; + break; + case FieldDescriptor::CPPTYPE_UINT32: + result->default_value_uint32_ = 0; + break; + case FieldDescriptor::CPPTYPE_UINT64: + result->default_value_uint64_ = 0; + break; + case FieldDescriptor::CPPTYPE_FLOAT: + result->default_value_float_ = 0.0f; + break; + case FieldDescriptor::CPPTYPE_DOUBLE: + result->default_value_double_ = 0.0; + break; + case FieldDescriptor::CPPTYPE_BOOL: + result->default_value_bool_ = false; + break; + case FieldDescriptor::CPPTYPE_ENUM: + // This will be filled in when cross-linking. + result->default_value_enum_ = NULL; + break; + case FieldDescriptor::CPPTYPE_STRING: + result->default_value_string_ = &kEmptyString; + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + break; + } + } + } + + if (result->number() <= 0) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + "Field numbers must be positive integers."); + } else if (result->number() > FieldDescriptor::kMaxNumber) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Field numbers cannot be greater than $0.", + FieldDescriptor::kMaxNumber)); + } else if (result->number() >= FieldDescriptor::kFirstReservedNumber && + result->number() <= FieldDescriptor::kLastReservedNumber) { + AddError(result->full_name(), proto, DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute( + "Field numbers $0 through $1 are reserved for the protocol " + "buffer library implementation.", + FieldDescriptor::kFirstReservedNumber, + FieldDescriptor::kLastReservedNumber)); + } + + if (is_extension) { + if (!proto.has_extendee()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "FieldDescriptorProto.extendee not set for extension field."); + } + + result->extension_scope_ = parent; + } else { + if (proto.has_extendee()) { + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "FieldDescriptorProto.extendee set for non-extension field."); + } + + result->containing_type_ = parent; + } + + // Copy options. + if (!proto.has_options()) { + result->options_ = &FieldOptions::default_instance(); + } else { + FieldOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildExtensionRange( + const DescriptorProto::ExtensionRange& proto, + const Descriptor* parent, + Descriptor::ExtensionRange* result) { + result->start = proto.start(); + result->end = proto.end(); + if (result->start <= 0) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Extension numbers must be positive integers."); + } + + if (result->end > FieldDescriptor::kMaxNumber + 1) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension numbers cannot be greater than $0.", + FieldDescriptor::kMaxNumber)); + } + + if (result->start >= result->end) { + AddError(parent->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + "Extension range end number must be greater than start number."); + } +} + +void DescriptorBuilder::BuildEnum(const EnumDescriptorProto& proto, + const Descriptor* parent, + EnumDescriptor* result) { + const string& scope = (parent == NULL) ? + file_->package() : parent->full_name(); + string* full_name = tables_->AllocateString(scope); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + result->containing_type_ = parent; + + if (proto.value_size() == 0) { + // We cannot allow enums with no values because this would mean there + // would be no valid default value for fields of this type. + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "Enums must contain at least one value."); + } + + BUILD_ARRAY(proto, result, value, BuildEnumValue, result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &EnumOptions::default_instance(); + } else { + EnumOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildEnumValue(const EnumValueDescriptorProto& proto, + const EnumDescriptor* parent, + EnumValueDescriptor* result) { + result->name_ = tables_->AllocateString(proto.name()); + result->number_ = proto.number(); + result->type_ = parent; + + // Note: full_name for enum values is a sibling to the parent's name, not a + // child of it. + string* full_name = tables_->AllocateString(*parent->full_name_); + full_name->resize(full_name->size() - parent->name_->size()); + full_name->append(*result->name_); + result->full_name_ = full_name; + + ValidateSymbolName(proto.name(), *full_name, proto); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &EnumValueOptions::default_instance(); + } else { + EnumValueOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + // Again, enum values are weird because we makes them appear as siblings + // of the enum type instead of children of it. So, we use + // parent->containing_type() as the value's parent. + bool added_to_outer_scope = + AddSymbol(result->full_name(), parent->containing_type(), result->name(), + proto, Symbol(result)); + + // However, we also want to be able to search for values within a single + // enum type, so we add it as a child of the enum type itself, too. + // Note: This could fail, but if it does, the error has already been + // reported by the above AddSymbol() call, so we ignore the return code. + bool added_to_inner_scope = + tables_->AddAliasUnderParent(parent, result->name(), Symbol(result)); + + if (added_to_inner_scope && !added_to_outer_scope) { + // This value did not conflict with any values defined in the same enum, + // but it did conflict with some other symbol defined in the enum type's + // scope. Let's print an additional error to explain this. + string outer_scope; + if (parent->containing_type() == NULL) { + outer_scope = file_->package(); + } else { + outer_scope = parent->containing_type()->full_name(); + } + + if (outer_scope.empty()) { + outer_scope = "the global scope"; + } else { + outer_scope = "\"" + outer_scope + "\""; + } + + AddError(result->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "Note that enum values use C++ scoping rules, meaning that " + "enum values are siblings of their type, not children of it. " + "Therefore, \"" + result->name() + "\" must be unique within " + + outer_scope + ", not just within \"" + parent->name() + "\"."); + } + + // An enum is allowed to define two numbers that refer to the same value. + // FindValueByNumber() should return the first such value, so we simply + // ignore AddEnumValueByNumber()'s return code. + tables_->AddEnumValueByNumber(result); +} + +void DescriptorBuilder::BuildService(const ServiceDescriptorProto& proto, + const void* dummy, + ServiceDescriptor* result) { + string* full_name = tables_->AllocateString(file_->package()); + if (!full_name->empty()) full_name->append(1, '.'); + full_name->append(proto.name()); + + ValidateSymbolName(proto.name(), *full_name, proto); + + result->name_ = tables_->AllocateString(proto.name()); + result->full_name_ = full_name; + result->file_ = file_; + + BUILD_ARRAY(proto, result, method, BuildMethod, result); + + // Copy options. + if (!proto.has_options()) { + result->options_ = &ServiceOptions::default_instance(); + } else { + ServiceOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), NULL, result->name(), + proto, Symbol(result)); +} + +void DescriptorBuilder::BuildMethod(const MethodDescriptorProto& proto, + const ServiceDescriptor* parent, + MethodDescriptor* result) { + result->name_ = tables_->AllocateString(proto.name()); + result->service_ = parent; + + string* full_name = tables_->AllocateString(parent->full_name()); + full_name->append(1, '.'); + full_name->append(*result->name_); + result->full_name_ = full_name; + + ValidateSymbolName(proto.name(), *full_name, proto); + + // These will be filled in when cross-linking. + result->input_type_ = NULL; + result->output_type_ = NULL; + + // Copy options. + if (!proto.has_options()) { + result->options_ = &MethodOptions::default_instance(); + } else { + MethodOptions* options = tables_->AllocateMessage(); + options->CopyFrom(proto.options()); + result->options_ = options; + } + + AddSymbol(result->full_name(), parent, result->name(), + proto, Symbol(result)); +} + +#undef BUILD_ARRAY + +// ------------------------------------------------------------------- + +void DescriptorBuilder::CrossLinkFile( + FileDescriptor* file, const FileDescriptorProto& proto) { + for (int i = 0; i < file->message_type_count(); i++) { + CrossLinkMessage(&file->message_types_[i], proto.message_type(i)); + } + + for (int i = 0; i < file->extension_count(); i++) { + CrossLinkField(&file->extensions_[i], proto.extension(i)); + } + + for (int i = 0; i < file->service_count(); i++) { + CrossLinkService(&file->services_[i], proto.service(i)); + } +} + +void DescriptorBuilder::CrossLinkMessage( + Descriptor* message, const DescriptorProto& proto) { + for (int i = 0; i < message->nested_type_count(); i++) { + CrossLinkMessage(&message->nested_types_[i], proto.nested_type(i)); + } + + for (int i = 0; i < message->field_count(); i++) { + CrossLinkField(&message->fields_[i], proto.field(i)); + } + + for (int i = 0; i < message->extension_count(); i++) { + CrossLinkField(&message->extensions_[i], proto.extension(i)); + } +} + +void DescriptorBuilder::CrossLinkField( + FieldDescriptor* field, const FieldDescriptorProto& proto) { + if (proto.has_extendee()) { + Symbol extendee = LookupSymbol(proto.extendee(), field->full_name()); + if (extendee.IsNull()) { + AddNotDefinedError(field->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + proto.extendee()); + return; + } else if (extendee.type != Symbol::MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::EXTENDEE, + "\"" + proto.extendee() + "\" is not a message type."); + return; + } + field->containing_type_ = extendee.descriptor; + + if (!field->containing_type()->IsExtensionNumber(field->number())) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("\"$0\" does not declare $1 as an " + "extension number.", + field->containing_type()->full_name(), + field->number())); + } + } + + if (proto.has_type_name()) { + Symbol type = LookupSymbol(proto.type_name(), field->full_name()); + if (type.IsNull()) { + AddNotDefinedError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + proto.type_name()); + return; + } + + if (!proto.has_type()) { + // Choose field type based on symbol. + if (type.type == Symbol::MESSAGE) { + field->type_ = FieldDescriptor::TYPE_MESSAGE; + } else if (type.type == Symbol::ENUM) { + field->type_ = FieldDescriptor::TYPE_ENUM; + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not a type."); + return; + } + } + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (type.type != Symbol::MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not a message type."); + return; + } + field->message_type_ = type.descriptor; + + if (field->has_default_value()) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Messages can't have default values."); + } + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + if (type.type != Symbol::ENUM) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "\"" + proto.type_name() + "\" is not an enum type."); + return; + } + field->enum_type_ = type.enum_descriptor; + + if (field->has_default_value()) { + // We can't just use field->enum_type()->FindValueByName() here + // because that locks the pool's mutex, which we have already locked + // at this point. + Symbol default_value = + LookupSymbol(proto.default_value(), field->enum_type()->full_name()); + + if (default_value.type == Symbol::ENUM_VALUE && + default_value.enum_value_descriptor->type() == field->enum_type()) { + field->default_value_enum_ = default_value.enum_value_descriptor; + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::DEFAULT_VALUE, + "Enum type \"" + field->enum_type()->full_name() + + "\" has no value named \"" + proto.default_value() + "\"."); + } + } else if (field->enum_type()->value_count() > 0) { + // All enums must have at least one value, or we would have reported + // an error elsewhere. We use the first defined value as the default + // if a default is not explicitly defined. + field->default_value_enum_ = field->enum_type()->value(0); + } + } else { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Field with primitive type has type_name."); + } + } else { + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE || + field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Field with message or enum type missing type_name."); + } + } + + if (proto.has_options() && proto.options().has_experimental_map_key()) { + CrossLinkMapKey(field, proto); + } + + // Add the field to the fields-by-number table. + // Note: We have to do this *after* cross-linking because extensions do not + // know their containing type until now. + if (!tables_->AddFieldByNumber(field)) { + const FieldDescriptor* conflicting_field = + tables_->FindFieldByNumber(field->containing_type(), field->number()); + if (field->is_extension()) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Extension number $0 has already been used " + "in \"$1\" by extension \"$2\".", + field->number(), + field->containing_type()->full_name(), + conflicting_field->full_name())); + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NUMBER, + strings::Substitute("Field number $0 has already been used in " + "\"$1\" by field \"$2\".", + field->number(), + field->containing_type()->full_name(), + conflicting_field->name())); + } + } + + // Note: Default instance may not yet be initialized here, so we have to + // avoid reading from it. + if (field->containing_type_ != NULL && + &field->containing_type()->options() != + &MessageOptions::default_instance() && + field->containing_type()->options().message_set_wire_format()) { + if (field->is_extension()) { + if (!field->is_optional() || + field->type() != FieldDescriptor::TYPE_MESSAGE) { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::TYPE, + "Extensions of MessageSets must be optional messages."); + } + } else { + AddError(field->full_name(), proto, + DescriptorPool::ErrorCollector::NAME, + "MessageSets cannot have fields, only extensions."); + } + } +} + +void DescriptorBuilder::CrossLinkService( + ServiceDescriptor* service, const ServiceDescriptorProto& proto) { + for (int i = 0; i < service->method_count(); i++) { + CrossLinkMethod(&service->methods_[i], proto.method(i)); + } +} + +void DescriptorBuilder::CrossLinkMethod( + MethodDescriptor* method, const MethodDescriptorProto& proto) { + Symbol input_type = LookupSymbol(proto.input_type(), method->full_name()); + if (input_type.IsNull()) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::INPUT_TYPE, + proto.input_type()); + } else if (input_type.type != Symbol::MESSAGE) { + AddError(method->full_name(), proto, + DescriptorPool::ErrorCollector::INPUT_TYPE, + "\"" + proto.input_type() + "\" is not a message type."); + } else { + method->input_type_ = input_type.descriptor; + } + + Symbol output_type = LookupSymbol(proto.output_type(), method->full_name()); + if (output_type.IsNull()) { + AddNotDefinedError(method->full_name(), proto, + DescriptorPool::ErrorCollector::OUTPUT_TYPE, + proto.output_type()); + } else if (output_type.type != Symbol::MESSAGE) { + AddError(method->full_name(), proto, + DescriptorPool::ErrorCollector::OUTPUT_TYPE, + "\"" + proto.output_type() + "\" is not a message type."); + } else { + method->output_type_ = output_type.descriptor; + } +} + +void DescriptorBuilder::CrossLinkMapKey( + FieldDescriptor* field, + const FieldDescriptorProto& proto) { + if (!field->is_repeated()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map type is only allowed for repeated fields."); + return; + } + + if (field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map type is only allowed for fields with a message type."); + return; + } + + const Descriptor* item_type = field->message_type(); + if (item_type == NULL) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Could not find field type."); + return; + } + + // Find the field in item_type named by "experimental_map_key" + const string& key_name = proto.options().experimental_map_key(); + const Symbol key_symbol = LookupSymbol( + key_name, + // We append ".key_name" to the containing type's name since + // LookupSymbol() searches for peers of the supplied name, not + // children of the supplied name. + item_type->full_name() + "." + key_name); + + if (key_symbol.IsNull() || key_symbol.field_descriptor->is_extension()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "Could not find field named \"" + key_name + "\" in type \"" + + item_type->full_name() + "\"."); + return; + } + const FieldDescriptor* key_field = key_symbol.field_descriptor; + + if (key_field->is_repeated()) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map_key must not name a repeated field."); + return; + } + + if (key_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + AddError(field->full_name(), proto, DescriptorPool::ErrorCollector::TYPE, + "map key must name a scalar or string field."); + return; + } + + field->experimental_map_key_ = key_field; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h new file mode 100644 index 00000000..5f3fa0a2 --- /dev/null +++ b/src/google/protobuf/descriptor.h @@ -0,0 +1,1185 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains classes which describe a type of protocol message. +// You can use a message's descriptor to learn at runtime what fields +// it contains and what the types of those fields are. The Message +// interface also allows you to dynamically access and modify individual +// fields by passing the FieldDescriptor of the field you are interested +// in. +// +// Most users will not care about descriptors, because they will write +// code specific to certain protocol types and will simply use the classes +// generated by the protocol compiler directly. Advanced users who want +// to operate on arbitrary types (not known at compile time) may want to +// read descriptors in order to learn about the contents of a message. +// A very small number of users will want to construct their own +// Descriptors, either because they are implementing Message manually or +// because they are writing something like the protocol compiler. +// +// For an example of how you might use descriptors, see the code example +// at the top of message.h. + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_H__ +#define GOOGLE_PROTOBUF_DESCRIPTOR_H__ + +#include +#include + + +namespace google { +namespace protobuf { + +// Defined in this file. +class Descriptor; +class FieldDescriptor; +class EnumDescriptor; +class EnumValueDescriptor; +class ServiceDescriptor; +class MethodDescriptor; +class FileDescriptor; +class DescriptorDatabase; +class DescriptorPool; + +// Defined in descriptor.proto +class DescriptorProto; +class FieldDescriptorProto; +class EnumDescriptorProto; +class EnumValueDescriptorProto; +class ServiceDescriptorProto; +class MethodDescriptorProto; +class FileDescriptorProto; +class MessageOptions; +class FieldOptions; +class EnumOptions; +class EnumValueOptions; +class ServiceOptions; +class MethodOptions; +class FileOptions; + +// Defined in message.h +class Message; + +// Defined in descriptor.cc +class DescriptorBuilder; + +// Describes a type of protocol message, or a particular group within a +// message. To obtain the Descriptor for a given message object, call +// Message::GetDescriptor(). Generated message classes also have a +// static method called descriptor() which returns the type's descriptor. +// Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT Descriptor { + public: + // The name of the message type, not including its scope. + const string& name() const; + + // The fully-qualified name of the message type, scope delimited by + // periods. For example, message type "Foo" which is declared in package + // "bar" has full name "bar.Foo". If a type "Baz" is nested within + // Foo, Baz's full_name is "bar.Foo.Baz". To get only the part that + // comes after the last '.', use name(). + const string& full_name() const; + + // Index of this descriptor within the file or containing type's message + // type array. + int index() const; + + // The .proto file in which this message type was defined. Never NULL. + const FileDescriptor* file() const; + + // If this Descriptor describes a nested type, this returns the type + // in which it is nested. Otherwise, returns NULL. + const Descriptor* containing_type() const; + + // Get options for this message type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the message definition. + // The exact set of known options is defined by MessageOptions in + // google/protobuf/descriptor.proto. + const MessageOptions& options() const; + + // Write the contents of this Descriptor into the given DescriptorProto. + // The target DescriptorProto must be clear before calling this; if it + // isn't, the result may be garbage. + void CopyTo(DescriptorProto* proto) const; + + // Write the contents of this decriptor in a human-readable form. Output + // will be suitable for re-parsing. + string DebugString() const; + + // Field stuff ----------------------------------------------------- + + // The number of fields in this message type. + int field_count() const; + // Gets a field by index, where 0 <= index < field_count(). + // These are returned in the order they were defined in the .proto file. + const FieldDescriptor* field(int index) const; + + // Looks up a field by declared tag number. Returns NULL if no such field + // exists. + const FieldDescriptor* FindFieldByNumber(int number) const; + // Looks up a field by name. Returns NULL if no such field exists. + const FieldDescriptor* FindFieldByName(const string& name) const; + + // Nested type stuff ----------------------------------------------- + + // The number of nested types in this message type. + int nested_type_count() const; + // Gets a nested type by index, where 0 <= index < nested_type_count(). + // These are returned in the order they were defined in the .proto file. + const Descriptor* nested_type(int index) const; + + // Looks up a nested type by name. Returns NULL if no such nested type + // exists. + const Descriptor* FindNestedTypeByName(const string& name) const; + + // Enum stuff ------------------------------------------------------ + + // The number of enum types in this message type. + int enum_type_count() const; + // Gets an enum type by index, where 0 <= index < enum_type_count(). + // These are returned in the order they were defined in the .proto file. + const EnumDescriptor* enum_type(int index) const; + + // Looks up an enum type by name. Returns NULL if no such enum type exists. + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + + // Looks up an enum value by name, among all enum types in this message. + // Returns NULL if no such value exists. + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + + // Extensions ------------------------------------------------------ + + // A range of field numbers which are designated for third-party + // extensions. + struct ExtensionRange { + int start; // inclusive + int end; // exclusive + }; + + // The number of extension ranges in this message type. + int extension_range_count() const; + // Gets an extension range by index, where 0 <= index < + // extension_range_count(). These are returned in the order they were defined + // in the .proto file. + const ExtensionRange* extension_range(int index) const; + + // Returns true if the number is in one of the extension ranges. + bool IsExtensionNumber(int number) const; + + // The number of extensions -- extending *other* messages -- that were + // defined nested within this message type's scope. + int extension_count() const; + // Get an extension by index, where 0 <= index < extension_count(). + // These are returned in the order they were defined in the .proto file. + const FieldDescriptor* extension(int index) const; + + // Looks up a named extension (which extends some *other* message type) + // defined within this message type's scope. + const FieldDescriptor* FindExtensionByName(const string& name) const; + + private: + // Internal version of DebugString; controls the level of indenting for + // correct depth + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + const Descriptor* containing_type_; + const MessageOptions* options_; + int field_count_; + FieldDescriptor* fields_; + int nested_type_count_; + Descriptor* nested_types_; + int enum_type_count_; + EnumDescriptor* enum_types_; + int extension_range_count_; + ExtensionRange* extension_ranges_; + int extension_count_; + FieldDescriptor* extensions_; + + // Must be constructed using DescriptorPool. + Descriptor() {} + friend class DescriptorBuilder; + friend class EnumDescriptor; + friend class FieldDescriptor; + friend class FileDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Descriptor); +}; + +// Describes a single field of a message. To get the descriptor for a given +// field, first get the Descriptor for the message in which it is defined, +// then call Descriptor::FindFieldByName(). To get a FieldDescriptor for +// an extension, do one of the following: +// - Get the Descriptor or FileDescriptor for its containing scope, then +// call Descriptor::FindExtensionByName() or +// FileDescriptor::FindExtensionByName(). +// - Given a DescriptorPool, call DescriptorPool::FindExtensionByNumber(). +// - Given a Reflection for a message object, call +// Reflection::FindKnownExtensionByName() or +// Reflection::FindKnownExtensionByNumber(). +// Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT FieldDescriptor { + public: + // Identifies a field type. 0 is reserved for errors. The order is weird + // for historical reasons. Types 12 and up are new in proto2. + enum Type { + TYPE_DOUBLE = 1, // double, exactly eight bytes on the wire. + TYPE_FLOAT = 2, // float, exactly four bytes on the wire. + TYPE_INT64 = 3, // int64, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. + TYPE_UINT64 = 4, // uint64, varint on the wire. + TYPE_INT32 = 5, // int32, varint on the wire. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. + TYPE_FIXED64 = 6, // uint64, exactly eight bytes on the wire. + TYPE_FIXED32 = 7, // uint32, exactly four bytes on the wire. + TYPE_BOOL = 8, // bool, varint on the wire. + TYPE_STRING = 9, // UTF-8 text. + TYPE_GROUP = 10, // Tag-delimited message. Deprecated. + TYPE_MESSAGE = 11, // Length-delimited message. + + TYPE_BYTES = 12, // Arbitrary byte array. + TYPE_UINT32 = 13, // uint32, varint on the wire + TYPE_ENUM = 14, // Enum, varint on the wire + TYPE_SFIXED32 = 15, // int32, exactly four bytes on the wire + TYPE_SFIXED64 = 16, // int64, exactly eight bytes on the wire + TYPE_SINT32 = 17, // int32, ZigZag-encoded varint on the wire + TYPE_SINT64 = 18, // int64, ZigZag-encoded varint on the wire + + MAX_TYPE = 18, // Constant useful for defining lookup tables + // indexed by Type. + }; + + // Specifies the C++ data type used to represent the field. There is a + // fixed mapping from Type to CppType where each Type maps to exactly one + // CppType. 0 is reserved for errors. + enum CppType { + CPPTYPE_INT32 = 1, // TYPE_INT32, TYPE_SINT32, TYPE_SFIXED32 + CPPTYPE_INT64 = 2, // TYPE_INT64, TYPE_SINT64, TYPE_SFIXED64 + CPPTYPE_UINT32 = 3, // TYPE_UINT32, TYPE_FIXED32 + CPPTYPE_UINT64 = 4, // TYPE_UINT64, TYPE_FIXED64 + CPPTYPE_DOUBLE = 5, // TYPE_DOUBLE + CPPTYPE_FLOAT = 6, // TYPE_FLOAT + CPPTYPE_BOOL = 7, // TYPE_BOOL + CPPTYPE_ENUM = 8, // TYPE_ENUM + CPPTYPE_STRING = 9, // TYPE_STRING, TYPE_BYTES + CPPTYPE_MESSAGE = 10, // TYPE_MESSAGE, TYPE_GROUP + + MAX_CPPTYPE = 10, // Constant useful for defining lookup tables + // indexed by CppType. + }; + + // Identifies whether the field is optional, required, or repeated. 0 is + // reserved for errors. + enum Label { + LABEL_OPTIONAL = 1, // optional + LABEL_REQUIRED = 2, // required + LABEL_REPEATED = 3, // repeated + + MAX_LABEL = 3, // Constant useful for defining lookup tables + // indexed by Label. + }; + + // Valid field numbers are positive integers up to kMaxNumber. + static const int kMaxNumber = (1 << 29) - 1; + + // First field number reserved for the protocol buffer library implementation. + // Users may not declare fields that use reserved numbers. + static const int kFirstReservedNumber = 19000; + // Last field number reserved for the protocol buffer library implementation. + // Users may not declare fields that use reserved numbers. + static const int kLastReservedNumber = 19999; + + const string& name() const; // Name of this field within the message. + const string& full_name() const; // Fully-qualified name of the field. + const FileDescriptor* file() const;// File in which this field was defined. + bool is_extension() const; // Is this an extension field? + int number() const; // Declared tag number. + + Type type() const; // Declared type of this field. + CppType cpp_type() const; // C++ type of this field. + Label label() const; // optional/required/repeated + + bool is_required() const; // shorthand for label() == LABEL_REQUIRED + bool is_optional() const; // shorthand for label() == LABEL_OPTIONAL + bool is_repeated() const; // shorthand for label() == LABEL_REPEATED + + // Index of this field within the message's field array, or the file or + // extension scope's extensions array. + int index() const; + + // Does this field have an explicitly-declared default value? + bool has_default_value() const; + + // Get the field default value if cpp_type() == CPPTYPE_INT32. If no + // explicit default was defined, the default is 0. + int32 default_value_int32() const; + // Get the field default value if cpp_type() == CPPTYPE_INT64. If no + // explicit default was defined, the default is 0. + int64 default_value_int64() const; + // Get the field default value if cpp_type() == CPPTYPE_UINT32. If no + // explicit default was defined, the default is 0. + uint32 default_value_uint32() const; + // Get the field default value if cpp_type() == CPPTYPE_UINT64. If no + // explicit default was defined, the default is 0. + uint64 default_value_uint64() const; + // Get the field default value if cpp_type() == CPPTYPE_FLOAT. If no + // explicit default was defined, the default is 0.0. + float default_value_float() const; + // Get the field default value if cpp_type() == CPPTYPE_DOUBLE. If no + // explicit default was defined, the default is 0.0. + double default_value_double() const; + // Get the field default value if cpp_type() == CPPTYPE_BOOL. If no + // explicit default was defined, the default is false. + bool default_value_bool() const; + // Get the field default value if cpp_type() == CPPTYPE_ENUM. If no + // explicit default was defined, the default is the first value defined + // in the enum type (all enum types are required to have at least one value). + // This never returns NULL. + const EnumValueDescriptor* default_value_enum() const; + // Get the field default value if cpp_type() == CPPTYPE_STRING. If no + // explicit default was defined, the default is the empty string. + const string& default_value_string() const; + + // The Descriptor for the message of which this is a field. For extensions, + // this is the extended type. Never NULL. + const Descriptor* containing_type() const; + + // An extension may be declared within the scope of another message. If this + // field is an extension (is_extension() is true), then extension_scope() + // returns that message, or NULL if the extension was declared at global + // scope. If this is not an extension, extension_scope() is undefined (may + // assert-fail). + const Descriptor* extension_scope() const; + + // If type is TYPE_MESSAGE or TYPE_GROUP, returns a descriptor for the + // message or the group type. Otherwise, undefined. + const Descriptor* message_type() const; + // If type is TYPE_ENUM, returns a descriptor for the enum. Otherwise, + // undefined. + const EnumDescriptor* enum_type() const; + + // EXPERIMENTAL; DO NOT USE. + // If this field is a map field, experimental_map_key() is the field + // that is the key for this map. + // experimental_map_key()->containing_type() is the same as message_type(). + const FieldDescriptor* experimental_map_key() const; + + // Get the FieldOptions for this field. This includes things listed in + // square brackets after the field definition. E.g., the field: + // optional string text = 1 [ctype=CORD]; + // has the "ctype" option set. FieldOptions is actually a protocol message, + // which makes it easier to extend. + const FieldOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(FieldDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + // formats the default value appropriately and returns it as a string. + // Must have a default value to call this. If quote_string_type is true, then + // types of CPPTYPE_STRING whill be surrounded by quotes and CEscaped. + string DefaultValueAsString(bool quote_string_type) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + int number_; + Type type_; + Label label_; + bool is_extension_; + const Descriptor* containing_type_; + const Descriptor* extension_scope_; + const Descriptor* message_type_; + const EnumDescriptor* enum_type_; + const FieldDescriptor* experimental_map_key_; + const FieldOptions* options_; + + bool has_default_value_; + union { + int32 default_value_int32_; + int64 default_value_int64_; + uint32 default_value_uint32_; + uint64 default_value_uint64_; + float default_value_float_; + double default_value_double_; + bool default_value_bool_; + + const EnumValueDescriptor* default_value_enum_; + const string* default_value_string_; + }; + + static const CppType kTypeToCppTypeMap[MAX_TYPE + 1]; + + static const char * const kTypeToName[MAX_TYPE + 1]; + + static const char * const kLabelToName[MAX_LABEL + 1]; + + // Must be constructed using DescriptorPool. + FieldDescriptor() {} + friend class DescriptorBuilder; + friend class FileDescriptor; + friend class Descriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FieldDescriptor); +}; + +// Describes an enum type defined in a .proto file. To get the EnumDescriptor +// for a generated enum type, call TypeName_descriptor(). Use DescriptorPool +// to construct your own descriptors. +class LIBPROTOBUF_EXPORT EnumDescriptor { + public: + // The name of this enum type in the containing scope. + const string& name() const; + + // The fully-qualified name of the enum type, scope delimited by periods. + const string& full_name() const; + + // Index of this enum within the file or containing message's enum array. + int index() const; + + // The .proto file in which this enum type was defined. Never NULL. + const FileDescriptor* file() const; + + // The number of values for this EnumDescriptor. Guaranteed to be greater + // than zero. + int value_count() const; + // Gets a value by index, where 0 <= index < value_count(). + // These are returned in the order they were defined in the .proto file. + const EnumValueDescriptor* value(int index) const; + + // Looks up a value by name. Returns NULL if no such value exists. + const EnumValueDescriptor* FindValueByName(const string& name) const; + // Looks up a value by number. Returns NULL if no such value exists. If + // multiple values have this number, the first one defined is returned. + const EnumValueDescriptor* FindValueByNumber(int number) const; + + // If this enum type is nested in a message type, this is that message type. + // Otherwise, NULL. + const Descriptor* containing_type() const; + + // Get options for this enum type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the enum definition. + // The exact set of known options is defined by EnumOptions in + // google/protobuf/descriptor.proto. + const EnumOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(EnumDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + int value_count_; + EnumValueDescriptor* values_; + const Descriptor* containing_type_; + const EnumOptions* options_; + + // Must be constructed using DescriptorPool. + EnumDescriptor() {} + friend class DescriptorBuilder; + friend class Descriptor; + friend class EnumValueDescriptor; + friend class FileDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumDescriptor); +}; + +// Describes an individual enum constant of a particular type. To get the +// EnumValueDescriptor for a given enum value, first get the EnumDescriptor +// for its type, then use EnumDescriptor::FindValueByName() or +// EnumDescriptor::FindValueByNumber(). Use DescriptorPool to construct +// your own descriptors. +class LIBPROTOBUF_EXPORT EnumValueDescriptor { + public: + const string& name() const; // Name of this enum constant. + int index() const; // Index within the enums's Descriptor. + int number() const; // Numeric value of this enum constant. + + // The full_name of an enum value is a sibling symbol of the enum type. + // e.g. the full name of FieldDescriptorProto::TYPE_INT32 is actually + // "google.protobuf.FieldDescriptorProto.TYPE_INT32", NOT + // "google.protobuf.FieldDescriptorProto.Type.TYPE_INT32". This is to conform + // with C++ scoping rules for enums. + const string& full_name() const; + + // The type of this value. Never NULL. + const EnumDescriptor* type() const; + + // Get options for this enum value. These are specified in the .proto + // file by adding text like "[foo = 1234]" after an enum value definition. + // The exact set of known options is defined by EnumValueOptions in + // google/protobuf/descriptor.proto. + const EnumValueOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(EnumValueDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + int number_; + const EnumDescriptor* type_; + const EnumValueOptions* options_; + + // Must be constructed using DescriptorPool. + EnumValueDescriptor() {} + friend class DescriptorBuilder; + friend class EnumDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumValueDescriptor); +}; + +// Describes an RPC service. To get the ServiceDescriptor for a service, +// call Service::GetDescriptor(). Generated service classes also have a +// static method called descriptor() which returns the type's +// ServiceDescriptor. Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT ServiceDescriptor { + public: + // The name of the service, not including its containing scope. + const string& name() const; + // The fully-qualified name of the service, scope delimited by periods. + const string& full_name() const; + // Index of this service within the file's services array. + int index() const; + + // The .proto file in which this service was defined. Never NULL. + const FileDescriptor* file() const; + + // Get options for this service type. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in the service definition. + // The exact set of known options is defined by ServiceOptions in + // google/protobuf/descriptor.proto. + const ServiceOptions& options() const; + + // The number of methods this service defines. + int method_count() const; + // Gets a MethodDescriptor by index, where 0 <= index < method_count(). + // These are returned in the order they were defined in the .proto file. + const MethodDescriptor* method(int index) const; + + // Look up a MethodDescriptor by name. + const MethodDescriptor* FindMethodByName(const string& name) const; + + // See Descriptor::CopyTo(). + void CopyTo(ServiceDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(string *contents) const; + + const string* name_; + const string* full_name_; + const FileDescriptor* file_; + const ServiceOptions* options_; + int method_count_; + MethodDescriptor* methods_; + + // Must be constructed using DescriptorPool. + ServiceDescriptor() {} + friend class DescriptorBuilder; + friend class FileDescriptor; + friend class MethodDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceDescriptor); +}; + +// Describes an individual service method. To obtain a MethodDescriptor given +// a service, first get its ServiceDescriptor, then call +// ServiceDescriptor::FindMethodByName(). Use DescriptorPool to construct your +// own descriptors. +class LIBPROTOBUF_EXPORT MethodDescriptor { + public: + // Name of this method, not including containing scope. + const string& name() const; + // The fully-qualified name of the method, scope delimited by periods. + const string& full_name() const; + // Index within the service's Descriptor. + int index() const; + + // Gets the service to which this method belongs. Never NULL. + const ServiceDescriptor* service() const; + + // Gets the type of protocol message which this method accepts as input. + const Descriptor* input_type() const; + // Gets the type of protocol message which this message produces as output. + const Descriptor* output_type() const; + + // Get options for this method. These are specified in the .proto + // file by placing lines like "option foo = 1234;" in curly-braces after + // a method declaration. The exact set of known options is defined by + // MethodOptions in google/protobuf/descriptor.proto. + const MethodOptions& options() const; + + // See Descriptor::CopyTo(). + void CopyTo(MethodDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + // See Descriptor::DebugString(). + void DebugString(int depth, string *contents) const; + + const string* name_; + const string* full_name_; + const ServiceDescriptor* service_; + const Descriptor* input_type_; + const Descriptor* output_type_; + const MethodOptions* options_; + + // Must be constructed using DescriptorPool. + MethodDescriptor() {} + friend class DescriptorBuilder; + friend class ServiceDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MethodDescriptor); +}; + +// Describes a whole .proto file. To get the FileDescriptor for a compiled-in +// file, get the descriptor for something defined in that file and call +// descriptor->file(). Use DescriptorPool to construct your own descriptors. +class LIBPROTOBUF_EXPORT FileDescriptor { + public: + // The filename, relative to the source tree. + // e.g. "google/protobuf/descriptor.proto" + const string& name() const; + + // The package, e.g. "google.protobuf.compiler". + const string& package() const; + + // The DescriptorPool in which this FileDescriptor and all its contents were + // allocated. Never NULL. + const DescriptorPool* pool() const; + + // The number of files imported by this one. + int dependency_count() const; + // Gets an imported file by index, where 0 <= index < dependency_count(). + // These are returned in the order they were defined in the .proto file. + const FileDescriptor* dependency(int index) const; + + // Number of top-level message types defined in this file. (This does not + // include nested types.) + int message_type_count() const; + // Gets a top-level message type, where 0 <= index < message_type_count(). + // These are returned in the order they were defined in the .proto file. + const Descriptor* message_type(int index) const; + + // Number of top-level enum types defined in this file. (This does not + // include nested types.) + int enum_type_count() const; + // Gets a top-level enum type, where 0 <= index < enum_type_count(). + // These are returned in the order they were defined in the .proto file. + const EnumDescriptor* enum_type(int index) const; + + // Number of services defined in this file. + int service_count() const; + // Gets a service, where 0 <= index < service_count(). + // These are returned in the order they were defined in the .proto file. + const ServiceDescriptor* service(int index) const; + + // Number of extensions defined at file scope. (This does not include + // extensions nested within message types.) + int extension_count() const; + // Gets an extension's descriptor, where 0 <= index < extension_count(). + // These are returned in the order they were defined in the .proto file. + const FieldDescriptor* extension(int index) const; + + // Get options for this file. These are specified in the .proto + // file by placing lines like "option foo = 1234;" at the top level, outside + // of any other definitions. The exact set of known options is defined by + // FileOptions in google/protobuf/descriptor.proto. + const FileOptions& options() const; + + // Find a top-level message type by name. Returns NULL if not found. + const Descriptor* FindMessageTypeByName(const string& name) const; + // Find a top-level enum type by name. Returns NULL if not found. + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + // Find an enum value defined in any top-level enum by name. Returns NULL if + // not found. + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + // Find a service definition by name. Returns NULL if not found. + const ServiceDescriptor* FindServiceByName(const string& name) const; + // Find a top-level extension definition by name. Returns NULL if not found. + const FieldDescriptor* FindExtensionByName(const string& name) const; + + // See Descriptor::CopyTo(). + void CopyTo(FileDescriptorProto* proto) const; + + // See Descriptor::DebugString(). + string DebugString() const; + + private: + const string* name_; + const string* package_; + const DescriptorPool* pool_; + int dependency_count_; + const FileDescriptor** dependencies_; + int message_type_count_; + Descriptor* message_types_; + int enum_type_count_; + EnumDescriptor* enum_types_; + int service_count_; + ServiceDescriptor* services_; + int extension_count_; + FieldDescriptor* extensions_; + const FileOptions* options_; + + FileDescriptor() {} + friend class DescriptorBuilder; + friend class Descriptor; + friend class FieldDescriptor; + friend class EnumDescriptor; + friend class ServiceDescriptor; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileDescriptor); +}; + +// =================================================================== + +// Used to construct descriptors. +// +// Normally you won't want to build your own descriptors. Message classes +// constructed by the protocol compiler will provide them for you. However, +// if you are implementing Message on your own, or if you are writing a +// program which can operate on totally arbitrary types and needs to load +// them from some sort of database, you might need to. +// +// Since Descriptors are composed of a whole lot of cross-linked bits of +// data that would be a pain to put together manually, the +// DescriptorPool class is provided to make the process easier. It can +// take a FileDescriptorProto (defined in descriptor.proto), validate it, +// and convert it to a set of nicely cross-linked Descriptors. +// +// DescriptorPool also helps with memory management. Descriptors are +// composed of many objects containing static data and pointers to each +// other. In all likelihood, when it comes time to delete this data, +// you'll want to delete it all at once. In fact, it is not uncommon to +// have a whole pool of descriptors all cross-linked with each other which +// you wish to delete all at once. This class represents such a pool, and +// handles the memory management for you. +// +// You can also search for descriptors within a DescriptorPool by name, and +// extensions by number. +class LIBPROTOBUF_EXPORT DescriptorPool { + public: + // Create a normal, empty DescriptorPool. + DescriptorPool(); + + // Constructs a DescriptorPool that, when it can't find something among the + // descriptors already in the pool, looks for it in the given + // DescriptorDatabase. + // Notes: + // - If a DescriptorPool is constructed this way, its BuildFile*() methods + // must not be called (they will assert-fail). The only way to populate + // the pool with descriptors is to call the Find*By*() methods. + // - The Find*By*() methods may block the calling thread if the + // DescriptorDatabase blocks. This in turn means that parsing messages + // may block if they need to look up extensions. + // - The Find*By*() methods will use mutexes for thread-safety, thus making + // them slower even when they don't have to fall back to the database. + // In fact, even the Find*By*() methods of descriptor objects owned by + // this pool will be slower, since they will have to obtain locks too. + // - An ErrorCollector may optionally be given to collect validation errors + // in files loaded from the database. If not given, errors will be printed + // to GOOGLE_LOG(ERROR). Remember that files are built on-demand, so this + // ErrorCollector may be called from any thread that calls one of the + // Find*By*() methods. + class ErrorCollector; + explicit DescriptorPool(DescriptorDatabase* fallback_database, + ErrorCollector* error_collector = NULL); + + ~DescriptorPool(); + + // Get a pointer to the generated pool. Generated protocol message classes + // which are compiled into the binary will allocate their descriptors in + // this pool. Do not add your own descriptors to this pool. + static const DescriptorPool* generated_pool(); + + // Find a FileDescriptor in the pool by file name. Returns NULL if not + // found. + const FileDescriptor* FindFileByName(const string& name) const; + + // Find the FileDescriptor in the pool which defines the given symbol. + // If any of the Find*ByName() methods below would succeed, then this is + // equivalent to calling that method and calling the result's file() method. + // Otherwise this returns NULL. + const FileDescriptor* FindFileContainingSymbol( + const string& symbol_name) const; + + // Looking up descriptors ------------------------------------------ + // These find descriptors by fully-qualified name. These will find both + // top-level descriptors and nested descriptors. They return NULL if not + // found. + + const Descriptor* FindMessageTypeByName(const string& name) const; + const FieldDescriptor* FindFieldByName(const string& name) const; + const FieldDescriptor* FindExtensionByName(const string& name) const; + const EnumDescriptor* FindEnumTypeByName(const string& name) const; + const EnumValueDescriptor* FindEnumValueByName(const string& name) const; + const ServiceDescriptor* FindServiceByName(const string& name) const; + const MethodDescriptor* FindMethodByName(const string& name) const; + + // Finds an extension of the given type by number. The extendee must be + // a member of this DescriptorPool or one of its underlays. + const FieldDescriptor* FindExtensionByNumber(const Descriptor* extendee, + int number) const; + + // Building descriptors -------------------------------------------- + + // When converting a FileDescriptorProto to a FileDescriptor, various + // errors might be detected in the input. The caller may handle these + // programmatically by implementing an ErrorCollector. + class LIBPROTOBUF_EXPORT ErrorCollector { + public: + inline ErrorCollector() {} + virtual ~ErrorCollector(); + + // These constants specify what exact part of the construct is broken. + // This is useful e.g. for mapping the error back to an exact location + // in a .proto file. + enum ErrorLocation { + NAME, // the symbol name, or the package name for files + NUMBER, // field or extension range number + TYPE, // field type + EXTENDEE, // field extendee + DEFAULT_VALUE, // field default value + INPUT_TYPE, // method input type + OUTPUT_TYPE, // method output type + OTHER // some other problem + }; + + // Reports an error in the FileDescriptorProto. + virtual void AddError( + const string& filename, // File name in which the error occurred. + const string& element_name, // Full name of the erroneous element. + const Message* descriptor, // Descriptor of the erroneous element. + ErrorLocation location, // One of the location constants, above. + const string& message // Human-readable error message. + ) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector); + }; + + // Convert the FileDescriptorProto to real descriptors and place them in + // this DescriptorPool. All dependencies of the file must already be in + // the pool. Returns the resulting FileDescriptor, or NULL if there were + // problems with the input (e.g. the message was invalid, or dependencies + // were missing). Details about the errors are written to GOOGLE_LOG(ERROR). + const FileDescriptor* BuildFile(const FileDescriptorProto& proto); + + // Same as BuildFile() except errors are sent to the given ErrorCollector. + const FileDescriptor* BuildFileCollectingErrors( + const FileDescriptorProto& proto, + ErrorCollector* error_collector); + + // Internal stuff -------------------------------------------------- + // These methods MUST NOT be called from outside the proto2 library. + // These methods may contain hidden pitfalls and may be removed in a + // future library version. + + // DEPRECATED: Use of underlays can lead to many subtle gotchas. Instead, + // try to formulate what you want to do in terms of DescriptorDatabases. + // This constructor will be removed soon. + // + // Create a DescriptorPool which is overlaid on top of some other pool. + // If you search for a descriptor in the overlay and it is not found, the + // underlay will be searched as a backup. If the underlay has its own + // underlay, that will be searched next, and so on. This also means that + // files built in the overlay will be cross-linked with the underlay's + // descriptors if necessary. The underlay remains property of the caller; + // it must remain valid for the lifetime of the newly-constructed pool. + // + // Example: Say you want to parse a .proto file at runtime in order to use + // its type with a DynamicMessage. Say this .proto file has dependencies, + // but you know that all the dependencies will be things that are already + // compiled into the binary. For ease of use, you'd like to load the types + // right out of generated_pool() rather than have to parse redundant copies + // of all these .protos and runtime. But, you don't want to add the parsed + // types directly into generated_pool(): this is not allowed, and would be + // bad design anyway. So, instead, you could use generated_pool() as an + // underlay for a new DescriptorPool in which you add only the new file. + explicit DescriptorPool(const DescriptorPool* underlay); + + // Called by generated classes at init time. Do NOT call this in your + // own code! + const FileDescriptor* InternalBuildGeneratedFile( + const void* data, int size); + + // For internal use only: Gets a non-const pointer to the generated pool. + // This is called at static-initialization time only, so thread-safety is + // not a concern. If both an underlay and a fallback database are present, + // the fallback database takes precedence. + static DescriptorPool* internal_generated_pool(); + + // For internal use only: Changes the behavior of BuildFile() such that it + // allows the file to make reference to message types declared in other files + // which it did not officially declare as dependencies. + void InternalDontEnforceDependencies(); + + // For internal use only. + void internal_set_underlay(const DescriptorPool* underlay) { + underlay_ = underlay; + } + + private: + friend class Descriptor; + friend class FieldDescriptor; + friend class EnumDescriptor; + friend class ServiceDescriptor; + friend class FileDescriptor; + friend class DescriptorBuilder; + + // Tries to find something in the fallback database and link in the + // corresponding proto file. Returns true if successful, in which case + // the caller should search for the thing again. These are declared + // const because they are called by (semantically) const methods. + bool TryFindFileInFallbackDatabase(const string& name) const; + bool TryFindSymbolInFallbackDatabase(const string& name) const; + bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type, + int field_number) const; + + // Like BuildFile() but called internally when the file has been loaded from + // fallback_database_. Declared const because it is called by (semantically) + // const methods. + const FileDescriptor* BuildFileFromDatabase( + const FileDescriptorProto& proto) const; + + // If fallback_database_ is NULL, this is NULL. Otherwise, this is a mutex + // which must be locked while accessing tables_. + Mutex* mutex_; + + // See constructor. + DescriptorDatabase* fallback_database_; + ErrorCollector* default_error_collector_; + const DescriptorPool* underlay_; + + // This class contains a lot of hash maps with complicated types that + // we'd like to keep out of the header. + class Tables; + scoped_ptr tables_; + + bool enforce_dependencies_; + + // See InternalBuildGeneratedFile(). + const void* last_internal_build_generated_file_call_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool); +}; + +// inline methods ==================================================== + +// These macros makes this repetitive code more readable. +#define PROTOBUF_DEFINE_ACCESSOR(CLASS, FIELD, TYPE) \ + inline TYPE CLASS::FIELD() const { return FIELD##_; } + +// Strings fields are stored as pointers but returned as const references. +#define PROTOBUF_DEFINE_STRING_ACCESSOR(CLASS, FIELD) \ + inline const string& CLASS::FIELD() const { return *FIELD##_; } + +// Arrays take an index parameter, obviously. +#define PROTOBUF_DEFINE_ARRAY_ACCESSOR(CLASS, FIELD, TYPE) \ + inline TYPE CLASS::FIELD(int index) const { return FIELD##s_ + index; } + +#define PROTOBUF_DEFINE_OPTIONS_ACCESSOR(CLASS, TYPE) \ + inline const TYPE& CLASS::options() const { return *options_; } + +PROTOBUF_DEFINE_STRING_ACCESSOR(Descriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(Descriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, containing_type, const Descriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, field_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, nested_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, enum_type_count, int) + +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, field, const FieldDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, nested_type, const Descriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, enum_type, const EnumDescriptor*) + +PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_range_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, extension_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension_range, + const Descriptor::ExtensionRange*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(Descriptor, extension, + const FieldDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(Descriptor, MessageOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, number, int) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, is_extension, bool) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, type, FieldDescriptor::Type) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, label, FieldDescriptor::Label) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, containing_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, extension_scope, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, message_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, enum_type, const EnumDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, experimental_map_key, + const FieldDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FieldDescriptor, FieldOptions); +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, has_default_value, bool) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int32 , int32 ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_int64 , int64 ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint32, uint32) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_uint64, uint64) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_float , float ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_double, double) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_bool , bool ) +PROTOBUF_DEFINE_ACCESSOR(FieldDescriptor, default_value_enum, + const EnumValueDescriptor*) +PROTOBUF_DEFINE_STRING_ACCESSOR(FieldDescriptor, default_value_string) + +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, containing_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(EnumDescriptor, value_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(EnumDescriptor, value, + const EnumValueDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumDescriptor, EnumOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumValueDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(EnumValueDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, number, int) +PROTOBUF_DEFINE_ACCESSOR(EnumValueDescriptor, type, const EnumDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(EnumValueDescriptor, EnumValueOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(ServiceDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(ServiceDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, file, const FileDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(ServiceDescriptor, method_count, int) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(ServiceDescriptor, method, + const MethodDescriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(ServiceDescriptor, ServiceOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(MethodDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(MethodDescriptor, full_name) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, service, const ServiceDescriptor*) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, input_type, const Descriptor*) +PROTOBUF_DEFINE_ACCESSOR(MethodDescriptor, output_type, const Descriptor*) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(MethodDescriptor, MethodOptions); + +PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, name) +PROTOBUF_DEFINE_STRING_ACCESSOR(FileDescriptor, package) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, pool, const DescriptorPool*) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, dependency_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, message_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, enum_type_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, service_count, int) +PROTOBUF_DEFINE_ACCESSOR(FileDescriptor, extension_count, int) +PROTOBUF_DEFINE_OPTIONS_ACCESSOR(FileDescriptor, FileOptions); + +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, message_type, const Descriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, enum_type, const EnumDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, service, + const ServiceDescriptor*) +PROTOBUF_DEFINE_ARRAY_ACCESSOR(FileDescriptor, extension, + const FieldDescriptor*) + +#undef PROTOBUF_DEFINE_ACCESSOR +#undef PROTOBUF_DEFINE_STRING_ACCESSOR +#undef PROTOBUF_DEFINE_ARRAY_ACCESSOR + +// A few accessors differ from the macros... + +inline bool FieldDescriptor::is_required() const { + return label() == LABEL_REQUIRED; +} + +inline bool FieldDescriptor::is_optional() const { + return label() == LABEL_OPTIONAL; +} + +inline bool FieldDescriptor::is_repeated() const { + return label() == LABEL_REPEATED; +} + +// To save space, index() is computed by looking at the descriptor's position +// in the parent's array of children. +inline int FieldDescriptor::index() const { + if (!is_extension_) { + return this - containing_type_->fields_; + } else if (extension_scope_ != NULL) { + return this - extension_scope_->extensions_; + } else { + return this - file_->extensions_; + } +} + +inline int Descriptor::index() const { + if (containing_type_ == NULL) { + return this - file_->message_types_; + } else { + return this - containing_type_->nested_types_; + } +} + +inline int EnumDescriptor::index() const { + if (containing_type_ == NULL) { + return this - file_->enum_types_; + } else { + return this - containing_type_->enum_types_; + } +} + +inline int EnumValueDescriptor::index() const { + return this - type_->values_; +} + +inline int ServiceDescriptor::index() const { + return this - file_->services_; +} + +inline int MethodDescriptor::index() const { + return this - service_->methods_; +} + +inline FieldDescriptor::CppType FieldDescriptor::cpp_type() const { + return kTypeToCppTypeMap[type_]; +} + +inline const FileDescriptor* FileDescriptor::dependency(int index) const { + return dependencies_[index]; +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DESCRIPTOR_H__ diff --git a/src/google/protobuf/descriptor.pb.cc b/src/google/protobuf/descriptor.pb.cc new file mode 100644 index 00000000..a37731d6 --- /dev/null +++ b/src/google/protobuf/descriptor.pb.cc @@ -0,0 +1,4114 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#include "google/protobuf/descriptor.pb.h" +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +const ::google::protobuf::Descriptor* FileDescriptorSet_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FileDescriptorSet_reflection_ = NULL; +const ::google::protobuf::Descriptor* FileDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FileDescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + DescriptorProto_ExtensionRange_reflection_ = NULL; +const ::google::protobuf::Descriptor* FieldDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FieldDescriptorProto_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + EnumDescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* EnumValueDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + EnumValueDescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* ServiceDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + ServiceDescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* MethodDescriptorProto_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + MethodDescriptorProto_reflection_ = NULL; +const ::google::protobuf::Descriptor* FileOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FileOptions_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor_ = NULL; +const ::google::protobuf::Descriptor* MessageOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + MessageOptions_reflection_ = NULL; +const ::google::protobuf::Descriptor* FieldOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + FieldOptions_reflection_ = NULL; +const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor_ = NULL; +const ::google::protobuf::Descriptor* EnumOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + EnumOptions_reflection_ = NULL; +const ::google::protobuf::Descriptor* EnumValueOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + EnumValueOptions_reflection_ = NULL; +const ::google::protobuf::Descriptor* ServiceOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + ServiceOptions_reflection_ = NULL; +const ::google::protobuf::Descriptor* MethodOptions_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + MethodOptions_reflection_ = NULL; + +} // namespace + + +void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + ::google::protobuf::DescriptorPool* pool = + ::google::protobuf::DescriptorPool::internal_generated_pool(); + + const ::google::protobuf::FileDescriptor* file = pool->InternalBuildGeneratedFile( + "\n google/protobuf/descriptor.proto\022\017goog" + "le.protobuf\"G\n\021FileDescriptorSet\0222\n\004file" + "\030\001 \003(\0132$.google.protobuf.FileDescriptorP" + "roto\"\334\002\n\023FileDescriptorProto\022\014\n\004name\030\001 \001" + "(\t\022\017\n\007package\030\002 \001(\t\022\022\n\ndependency\030\003 \003(\t\022" + "6\n\014message_type\030\004 \003(\0132 .google.protobuf." + "DescriptorProto\0227\n\tenum_type\030\005 \003(\0132$.goo" + "gle.protobuf.EnumDescriptorProto\0228\n\007serv" + "ice\030\006 \003(\0132\'.google.protobuf.ServiceDescr" + "iptorProto\0228\n\textension\030\007 \003(\0132%.google.p" + "rotobuf.FieldDescriptorProto\022-\n\007options\030" + "\010 \001(\0132\034.google.protobuf.FileOptions\"\251\003\n\017" + "DescriptorProto\022\014\n\004name\030\001 \001(\t\0224\n\005field\030\002" + " \003(\0132%.google.protobuf.FieldDescriptorPr" + "oto\0228\n\textension\030\006 \003(\0132%.google.protobuf" + ".FieldDescriptorProto\0225\n\013nested_type\030\003 \003" + "(\0132 .google.protobuf.DescriptorProto\0227\n\t" + "enum_type\030\004 \003(\0132$.google.protobuf.EnumDe" + "scriptorProto\022H\n\017extension_range\030\005 \003(\0132/" + ".google.protobuf.DescriptorProto.Extensi" + "onRange\0220\n\007options\030\007 \001(\0132\037.google.protob" + "uf.MessageOptions\032,\n\016ExtensionRange\022\r\n\005s" + "tart\030\001 \001(\005\022\013\n\003end\030\002 \001(\005\"\224\005\n\024FieldDescrip" + "torProto\022\014\n\004name\030\001 \001(\t\022\016\n\006number\030\003 \001(\005\022:" + "\n\005label\030\004 \001(\0162+.google.protobuf.FieldDes" + "criptorProto.Label\0228\n\004type\030\005 \001(\0162*.googl" + "e.protobuf.FieldDescriptorProto.Type\022\021\n\t" + "type_name\030\006 \001(\t\022\020\n\010extendee\030\002 \001(\t\022\025\n\rdef" + "ault_value\030\007 \001(\t\022.\n\007options\030\010 \001(\0132\035.goog" + "le.protobuf.FieldOptions\"\266\002\n\004Type\022\017\n\013TYP" + "E_DOUBLE\020\001\022\016\n\nTYPE_FLOAT\020\002\022\016\n\nTYPE_INT64" + "\020\003\022\017\n\013TYPE_UINT64\020\004\022\016\n\nTYPE_INT32\020\005\022\020\n\014T" + "YPE_FIXED64\020\006\022\020\n\014TYPE_FIXED32\020\007\022\r\n\tTYPE_" + "BOOL\020\010\022\017\n\013TYPE_STRING\020\t\022\016\n\nTYPE_GROUP\020\n\022" + "\020\n\014TYPE_MESSAGE\020\013\022\016\n\nTYPE_BYTES\020\014\022\017\n\013TYP" + "E_UINT32\020\r\022\r\n\tTYPE_ENUM\020\016\022\021\n\rTYPE_SFIXED" + "32\020\017\022\021\n\rTYPE_SFIXED64\020\020\022\017\n\013TYPE_SINT32\020\021" + "\022\017\n\013TYPE_SINT64\020\022\"C\n\005Label\022\022\n\016LABEL_OPTI" + "ONAL\020\001\022\022\n\016LABEL_REQUIRED\020\002\022\022\n\016LABEL_REPE" + "ATED\020\003\"\214\001\n\023EnumDescriptorProto\022\014\n\004name\030\001" + " \001(\t\0228\n\005value\030\002 \003(\0132).google.protobuf.En" + "umValueDescriptorProto\022-\n\007options\030\003 \001(\0132" + "\034.google.protobuf.EnumOptions\"l\n\030EnumVal" + "ueDescriptorProto\022\014\n\004name\030\001 \001(\t\022\016\n\006numbe" + "r\030\002 \001(\005\0222\n\007options\030\003 \001(\0132!.google.protob" + "uf.EnumValueOptions\"\220\001\n\026ServiceDescripto" + "rProto\022\014\n\004name\030\001 \001(\t\0226\n\006method\030\002 \003(\0132&.g" + "oogle.protobuf.MethodDescriptorProto\0220\n\007" + "options\030\003 \001(\0132\037.google.protobuf.ServiceO" + "ptions\"\177\n\025MethodDescriptorProto\022\014\n\004name\030" + "\001 \001(\t\022\022\n\ninput_type\030\002 \001(\t\022\023\n\013output_type" + "\030\003 \001(\t\022/\n\007options\030\004 \001(\0132\036.google.protobu" + "f.MethodOptions\"\333\001\n\013FileOptions\022\024\n\014java_" + "package\030\001 \001(\t\022\034\n\024java_outer_classname\030\010 " + "\001(\t\022\"\n\023java_multiple_files\030\n \001(\010:\005false\022" + "J\n\014optimize_for\030\t \001(\0162).google.protobuf." + "FileOptions.OptimizeMode:\tCODE_SIZE\"(\n\014O" + "ptimizeMode\022\t\n\005SPEED\020\001\022\r\n\tCODE_SIZE\020\002\"8\n" + "\016MessageOptions\022&\n\027message_set_wire_form" + "at\030\001 \001(\010:\005false\"\205\001\n\014FieldOptions\0222\n\005ctyp" + "e\030\001 \001(\0162#.google.protobuf.FieldOptions.C" + "Type\022\034\n\024experimental_map_key\030\t \001(\t\"#\n\005CT" + "ype\022\010\n\004CORD\020\001\022\020\n\014STRING_PIECE\020\002\"\r\n\013EnumO" + "ptions\"\022\n\020EnumValueOptions\"\020\n\016ServiceOpt" + "ions\"\017\n\rMethodOptionsB)\n\023com.google.prot" + "obufB\020DescriptorProtosH\001", 2624); + FileDescriptorSet_descriptor_ = file->message_type(0); + FileDescriptorSet_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + FileDescriptorSet_descriptor_, + &FileDescriptorSet::default_instance(), + FileDescriptorSet::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorSet, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorSet, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FileDescriptorSet_descriptor_, &FileDescriptorSet::default_instance()); + FileDescriptorProto_descriptor_ = file->message_type(1); + FileDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + FileDescriptorProto_descriptor_, + &FileDescriptorProto::default_instance(), + FileDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FileDescriptorProto_descriptor_, &FileDescriptorProto::default_instance()); + DescriptorProto_descriptor_ = file->message_type(2); + DescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + DescriptorProto_descriptor_, + &DescriptorProto::default_instance(), + DescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + DescriptorProto_ExtensionRange_descriptor_ = DescriptorProto_descriptor_->nested_type(0); + DescriptorProto_ExtensionRange_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + DescriptorProto_ExtensionRange_descriptor_, + &DescriptorProto_ExtensionRange::default_instance(), + DescriptorProto_ExtensionRange::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_ExtensionRange_descriptor_, &DescriptorProto_ExtensionRange::default_instance()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + DescriptorProto_descriptor_, &DescriptorProto::default_instance()); + FieldDescriptorProto_descriptor_ = file->message_type(3); + FieldDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + FieldDescriptorProto_descriptor_, + &FieldDescriptorProto::default_instance(), + FieldDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + FieldDescriptorProto_Type_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(0); + FieldDescriptorProto_Label_descriptor_ = FieldDescriptorProto_descriptor_->enum_type(1); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FieldDescriptorProto_descriptor_, &FieldDescriptorProto::default_instance()); + EnumDescriptorProto_descriptor_ = file->message_type(4); + EnumDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + EnumDescriptorProto_descriptor_, + &EnumDescriptorProto::default_instance(), + EnumDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumDescriptorProto_descriptor_, &EnumDescriptorProto::default_instance()); + EnumValueDescriptorProto_descriptor_ = file->message_type(5); + EnumValueDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + EnumValueDescriptorProto_descriptor_, + &EnumValueDescriptorProto::default_instance(), + EnumValueDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumValueDescriptorProto_descriptor_, &EnumValueDescriptorProto::default_instance()); + ServiceDescriptorProto_descriptor_ = file->message_type(6); + ServiceDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + ServiceDescriptorProto_descriptor_, + &ServiceDescriptorProto::default_instance(), + ServiceDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + ServiceDescriptorProto_descriptor_, &ServiceDescriptorProto::default_instance()); + MethodDescriptorProto_descriptor_ = file->message_type(7); + MethodDescriptorProto_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + MethodDescriptorProto_descriptor_, + &MethodDescriptorProto::default_instance(), + MethodDescriptorProto::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MethodDescriptorProto_descriptor_, &MethodDescriptorProto::default_instance()); + FileOptions_descriptor_ = file->message_type(8); + FileOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + FileOptions_descriptor_, + &FileOptions::default_instance(), + FileOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + FileOptions_OptimizeMode_descriptor_ = FileOptions_descriptor_->enum_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FileOptions_descriptor_, &FileOptions::default_instance()); + MessageOptions_descriptor_ = file->message_type(9); + MessageOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + MessageOptions_descriptor_, + &MessageOptions::default_instance(), + MessageOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MessageOptions_descriptor_, &MessageOptions::default_instance()); + FieldOptions_descriptor_ = file->message_type(10); + FieldOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + FieldOptions_descriptor_, + &FieldOptions::default_instance(), + FieldOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + FieldOptions_CType_descriptor_ = FieldOptions_descriptor_->enum_type(0); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + FieldOptions_descriptor_, &FieldOptions::default_instance()); + EnumOptions_descriptor_ = file->message_type(11); + EnumOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + EnumOptions_descriptor_, + &EnumOptions::default_instance(), + EnumOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumOptions_descriptor_, &EnumOptions::default_instance()); + EnumValueOptions_descriptor_ = file->message_type(12); + EnumValueOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + EnumValueOptions_descriptor_, + &EnumValueOptions::default_instance(), + EnumValueOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + EnumValueOptions_descriptor_, &EnumValueOptions::default_instance()); + ServiceOptions_descriptor_ = file->message_type(13); + ServiceOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + ServiceOptions_descriptor_, + &ServiceOptions::default_instance(), + ServiceOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + ServiceOptions_descriptor_, &ServiceOptions::default_instance()); + MethodOptions_descriptor_ = file->message_type(14); + MethodOptions_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + MethodOptions_descriptor_, + &MethodOptions::default_instance(), + MethodOptions::_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodOptions, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool()); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + MethodOptions_descriptor_, &MethodOptions::default_instance()); +} + +// Force BuildDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_google_2fprotobuf_2fdescriptor_2eproto { + StaticDescriptorInitializer_google_2fprotobuf_2fdescriptor_2eproto() { + protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + } +} static_descriptor_initializer_google_2fprotobuf_2fdescriptor_2eproto_; + + +// =================================================================== + +const FileDescriptorSet FileDescriptorSet::default_instance_; + + +const int FileDescriptorSet::_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorSet, file_), +}; + +FileDescriptorSet::FileDescriptorSet() + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +FileDescriptorSet::FileDescriptorSet(const FileDescriptorSet& from) + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FileDescriptorSet::~FileDescriptorSet() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* FileDescriptorSet::descriptor() { + if (FileDescriptorSet_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileDescriptorSet_descriptor_; +} + +FileDescriptorSet* FileDescriptorSet::New() const { + return new FileDescriptorSet; +} + +void FileDescriptorSet::Clear() { + file_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FileDescriptorSet::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // repeated .google.protobuf.FileDescriptorProto file = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_file: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_file())); + if (input->ExpectTag(10)) goto parse_file; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FileDescriptorSet::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // repeated .google.protobuf.FileDescriptorProto file = 1; + for (int i = 0; i < file_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(1, this->file(i), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FileDescriptorSet::ByteSize() const { + int total_size = 0; + + // repeated .google.protobuf.FileDescriptorProto file = 1; + total_size += 1 * file_size(); + for (int i = 0; i < file_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->file(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FileDescriptorSet::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FileDescriptorSet* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void FileDescriptorSet::MergeFrom(const FileDescriptorSet& from) { + GOOGLE_CHECK_NE(&from, this); + file_.MergeFrom(from.file_); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FileDescriptorSet::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileDescriptorSet::CopyFrom(const FileDescriptorSet& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileDescriptorSet::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FileDescriptorSet::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* FileDescriptorSet::GetReflection() const { + if (FileDescriptorSet_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileDescriptorSet_reflection_; +} + +// =================================================================== + +const FileDescriptorProto FileDescriptorProto::default_instance_; + +const ::std::string FileDescriptorProto::_default_name_; +const ::std::string FileDescriptorProto::_default_package_; + + + + + + +const int FileDescriptorProto::_offsets_[8] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, package_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, dependency_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, message_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, enum_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, service_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, extension_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileDescriptorProto, options_), +}; + +FileDescriptorProto::FileDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + package_(const_cast< ::std::string*>(&_default_package_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::FileOptions*>(&::google::protobuf::FileOptions::default_instance()); + } +} + +FileDescriptorProto::FileDescriptorProto(const FileDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + package_(const_cast< ::std::string*>(&_default_package_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FileDescriptorProto::~FileDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (package_ != &_default_package_) { + delete package_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* FileDescriptorProto::descriptor() { + if (FileDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileDescriptorProto_descriptor_; +} + +FileDescriptorProto* FileDescriptorProto::New() const { + return new FileDescriptorProto; +} + +void FileDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(1)) { + if (package_ != &_default_package_) { + package_->clear(); + } + } + if (_has_bit(7)) { + if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); + } + } + dependency_.Clear(); + message_type_.Clear(); + enum_type_.Clear(); + service_.Clear(); + extension_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FileDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_package; + break; + } + + // optional string package = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_package: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_package())); + if (input->ExpectTag(26)) goto parse_dependency; + break; + } + + // repeated string dependency = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_dependency: + DO_(::google::protobuf::internal::WireFormat::ReadString( + input, add_dependency())); + if (input->ExpectTag(26)) goto parse_dependency; + if (input->ExpectTag(34)) goto parse_message_type; + break; + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_message_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_message_type())); + if (input->ExpectTag(34)) goto parse_message_type; + if (input->ExpectTag(42)) goto parse_enum_type; + break; + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_enum_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_enum_type())); + if (input->ExpectTag(42)) goto parse_enum_type; + if (input->ExpectTag(50)) goto parse_service; + break; + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_service: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_service())); + if (input->ExpectTag(50)) goto parse_service; + if (input->ExpectTag(58)) goto parse_extension; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension())); + if (input->ExpectTag(58)) goto parse_extension; + if (input->ExpectTag(66)) goto parse_options; + break; + } + + // optional .google.protobuf.FileOptions options = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FileDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string package = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->package(), output)); + } + + // repeated string dependency = 3; + for (int i = 0; i < dependency_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->dependency(i), output)); + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + for (int i = 0; i < message_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->message_type(i), output)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + for (int i = 0; i < enum_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->enum_type(i), output)); + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + for (int i = 0; i < service_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->service(i), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + for (int i = 0; i < extension_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(7, this->extension(i), output)); + } + + // optional .google.protobuf.FileOptions options = 8; + if (_has_bit(7)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(8, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FileDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional string package = 2; + if (has_package()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->package()); + } + + // optional .google.protobuf.FileOptions options = 8; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated string dependency = 3; + total_size += 1 * dependency_size(); + for (int i = 0; i < dependency_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::StringSize( + this->dependency(i)); + } + + // repeated .google.protobuf.DescriptorProto message_type = 4; + total_size += 1 * message_type_size(); + for (int i = 0; i < message_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->message_type(i)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + total_size += 1 * enum_type_size(); + for (int i = 0; i < enum_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->enum_type(i)); + } + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + total_size += 1 * service_size(); + for (int i = 0; i < service_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->service(i)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + total_size += 1 * extension_size(); + for (int i = 0; i < extension_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FileDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FileDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void FileDescriptorProto::MergeFrom(const FileDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + dependency_.MergeFrom(from.dependency_); + message_type_.MergeFrom(from.message_type_); + enum_type_.MergeFrom(from.enum_type_); + service_.MergeFrom(from.service_); + extension_.MergeFrom(from.extension_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_package(from.package()); + } + if (from._has_bit(7)) { + mutable_options()->::google::protobuf::FileOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FileDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileDescriptorProto::CopyFrom(const FileDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FileDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* FileDescriptorProto::GetReflection() const { + if (FileDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileDescriptorProto_reflection_; +} + +// =================================================================== + +const DescriptorProto_ExtensionRange DescriptorProto_ExtensionRange::default_instance_; + + + +const int DescriptorProto_ExtensionRange::_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, start_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto_ExtensionRange, end_), +}; + +DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange() + : _cached_size_(0), + start_(0), + end_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +DescriptorProto_ExtensionRange::DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from) + : _cached_size_(0), + start_(0), + end_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +DescriptorProto_ExtensionRange::~DescriptorProto_ExtensionRange() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange::descriptor() { + if (DescriptorProto_ExtensionRange_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_ExtensionRange_descriptor_; +} + +DescriptorProto_ExtensionRange* DescriptorProto_ExtensionRange::New() const { + return new DescriptorProto_ExtensionRange; +} + +void DescriptorProto_ExtensionRange::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + start_ = 0; + end_ = 0; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool DescriptorProto_ExtensionRange::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional int32 start = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &start_)); + _set_bit(0); + if (input->ExpectTag(16)) goto parse_end; + break; + } + + // optional int32 end = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_end: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &end_)); + _set_bit(1); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool DescriptorProto_ExtensionRange::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional int32 start = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(1, this->start(), output)); + } + + // optional int32 end = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(2, this->end(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int DescriptorProto_ExtensionRange::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional int32 start = 1; + if (has_start()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->start()); + } + + // optional int32 end = 2; + if (has_end()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->end()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void DescriptorProto_ExtensionRange::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const DescriptorProto_ExtensionRange* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto_ExtensionRange::MergeFrom(const DescriptorProto_ExtensionRange& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_start(from.start()); + } + if (from._has_bit(1)) { + set_end(from.end()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void DescriptorProto_ExtensionRange::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto_ExtensionRange::CopyFrom(const DescriptorProto_ExtensionRange& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto_ExtensionRange::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* DescriptorProto_ExtensionRange::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* DescriptorProto_ExtensionRange::GetReflection() const { + if (DescriptorProto_ExtensionRange_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_ExtensionRange_reflection_; +} + +// ------------------------------------------------------------------- + +const DescriptorProto DescriptorProto::default_instance_; + +const ::std::string DescriptorProto::_default_name_; + + + + + + +const int DescriptorProto::_offsets_[7] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, field_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, nested_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, enum_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, extension_range_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(DescriptorProto, options_), +}; + +DescriptorProto::DescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::MessageOptions*>(&::google::protobuf::MessageOptions::default_instance()); + } +} + +DescriptorProto::DescriptorProto(const DescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +DescriptorProto::~DescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* DescriptorProto::descriptor() { + if (DescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_descriptor_; +} + +DescriptorProto* DescriptorProto::New() const { + return new DescriptorProto; +} + +void DescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(6)) { + if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); + } + } + field_.Clear(); + extension_.Clear(); + nested_type_.Clear(); + enum_type_.Clear(); + extension_range_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool DescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_field; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_field: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_field())); + if (input->ExpectTag(18)) goto parse_field; + if (input->ExpectTag(26)) goto parse_nested_type; + break; + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_nested_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_nested_type())); + if (input->ExpectTag(26)) goto parse_nested_type; + if (input->ExpectTag(34)) goto parse_enum_type; + break; + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_enum_type: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_enum_type())); + if (input->ExpectTag(34)) goto parse_enum_type; + if (input->ExpectTag(42)) goto parse_extension_range; + break; + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension_range: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension_range())); + if (input->ExpectTag(42)) goto parse_extension_range; + if (input->ExpectTag(50)) goto parse_extension; + break; + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extension: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_extension())); + if (input->ExpectTag(50)) goto parse_extension; + if (input->ExpectTag(58)) goto parse_options; + break; + } + + // optional .google.protobuf.MessageOptions options = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool DescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + for (int i = 0; i < field_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->field(i), output)); + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + for (int i = 0; i < nested_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->nested_type(i), output)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + for (int i = 0; i < enum_type_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->enum_type(i), output)); + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + for (int i = 0; i < extension_range_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(5, this->extension_range(i), output)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + for (int i = 0; i < extension_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(6, this->extension(i), output)); + } + + // optional .google.protobuf.MessageOptions options = 7; + if (_has_bit(6)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(7, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int DescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.MessageOptions options = 7; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.FieldDescriptorProto field = 2; + total_size += 1 * field_size(); + for (int i = 0; i < field_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->field(i)); + } + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + total_size += 1 * extension_size(); + for (int i = 0; i < extension_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension(i)); + } + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + total_size += 1 * nested_type_size(); + for (int i = 0; i < nested_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->nested_type(i)); + } + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + total_size += 1 * enum_type_size(); + for (int i = 0; i < enum_type_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->enum_type(i)); + } + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + total_size += 1 * extension_range_size(); + for (int i = 0; i < extension_range_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->extension_range(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void DescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const DescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void DescriptorProto::MergeFrom(const DescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + field_.MergeFrom(from.field_); + extension_.MergeFrom(from.extension_); + nested_type_.MergeFrom(from.nested_type_); + enum_type_.MergeFrom(from.enum_type_); + extension_range_.MergeFrom(from.extension_range_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(6)) { + mutable_options()->::google::protobuf::MessageOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void DescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void DescriptorProto::CopyFrom(const DescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool DescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* DescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* DescriptorProto::GetReflection() const { + if (DescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return DescriptorProto_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor() { + if (FieldDescriptorProto_Type_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_Type_descriptor_; +} +bool FieldDescriptorProto_Type_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + case 18: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_DOUBLE; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FLOAT; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_INT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_FIXED32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BOOL; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_STRING; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_GROUP; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_MESSAGE; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_BYTES; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_UINT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_ENUM; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SFIXED64; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT32; +const FieldDescriptorProto_Type FieldDescriptorProto::TYPE_SINT64; +const FieldDescriptorProto_Type FieldDescriptorProto::Type_MIN; +const FieldDescriptorProto_Type FieldDescriptorProto::Type_MAX; +#endif // _MSC_VER +const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor() { + if (FieldDescriptorProto_Label_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_Label_descriptor_; +} +bool FieldDescriptorProto_Label_IsValid(int value) { + switch(value) { + case 1: + case 2: + case 3: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_OPTIONAL; +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REQUIRED; +const FieldDescriptorProto_Label FieldDescriptorProto::LABEL_REPEATED; +const FieldDescriptorProto_Label FieldDescriptorProto::Label_MIN; +const FieldDescriptorProto_Label FieldDescriptorProto::Label_MAX; +#endif // _MSC_VER +const FieldDescriptorProto FieldDescriptorProto::default_instance_; + +const ::std::string FieldDescriptorProto::_default_name_; + + + +const ::std::string FieldDescriptorProto::_default_type_name_; +const ::std::string FieldDescriptorProto::_default_extendee_; +const ::std::string FieldDescriptorProto::_default_default_value_; + +const int FieldDescriptorProto::_offsets_[8] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, number_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, label_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, type_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, extendee_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, default_value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldDescriptorProto, options_), +}; + +FieldDescriptorProto::FieldDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + label_(1), + type_(1), + type_name_(const_cast< ::std::string*>(&_default_type_name_)), + extendee_(const_cast< ::std::string*>(&_default_extendee_)), + default_value_(const_cast< ::std::string*>(&_default_default_value_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::FieldOptions*>(&::google::protobuf::FieldOptions::default_instance()); + } +} + +FieldDescriptorProto::FieldDescriptorProto(const FieldDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + label_(1), + type_(1), + type_name_(const_cast< ::std::string*>(&_default_type_name_)), + extendee_(const_cast< ::std::string*>(&_default_extendee_)), + default_value_(const_cast< ::std::string*>(&_default_default_value_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FieldDescriptorProto::~FieldDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (type_name_ != &_default_type_name_) { + delete type_name_; + } + if (extendee_ != &_default_extendee_) { + delete extendee_; + } + if (default_value_ != &_default_default_value_) { + delete default_value_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* FieldDescriptorProto::descriptor() { + if (FieldDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_descriptor_; +} + +FieldDescriptorProto* FieldDescriptorProto::New() const { + return new FieldDescriptorProto; +} + +void FieldDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + number_ = 0; + label_ = 1; + type_ = 1; + if (_has_bit(4)) { + if (type_name_ != &_default_type_name_) { + type_name_->clear(); + } + } + if (_has_bit(5)) { + if (extendee_ != &_default_extendee_) { + extendee_->clear(); + } + } + if (_has_bit(6)) { + if (default_value_ != &_default_default_value_) { + default_value_->clear(); + } + } + if (_has_bit(7)) { + if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FieldDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_extendee; + break; + } + + // optional string extendee = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_extendee: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_extendee())); + if (input->ExpectTag(24)) goto parse_number; + break; + } + + // optional int32 number = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_number: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &number_)); + _set_bit(1); + if (input->ExpectTag(32)) goto parse_label; + break; + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_label: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldDescriptorProto_Label_IsValid(value)) { + set_label(static_cast< ::google::protobuf::FieldDescriptorProto_Label >(value)); + } else { + mutable_unknown_fields()->AddField(4)->add_varint(value); + } + if (input->ExpectTag(40)) goto parse_type; + break; + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_type: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldDescriptorProto_Type_IsValid(value)) { + set_type(static_cast< ::google::protobuf::FieldDescriptorProto_Type >(value)); + } else { + mutable_unknown_fields()->AddField(5)->add_varint(value); + } + if (input->ExpectTag(50)) goto parse_type_name; + break; + } + + // optional string type_name = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_type_name: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_type_name())); + if (input->ExpectTag(58)) goto parse_default_value; + break; + } + + // optional string default_value = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_default_value: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_default_value())); + if (input->ExpectTag(66)) goto parse_options; + break; + } + + // optional .google.protobuf.FieldOptions options = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FieldDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string extendee = 2; + if (_has_bit(5)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->extendee(), output)); + } + + // optional int32 number = 3; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(3, this->number(), output)); + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(4, this->label(), output)); + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(5, this->type(), output)); + } + + // optional string type_name = 6; + if (_has_bit(4)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(6, this->type_name(), output)); + } + + // optional string default_value = 7; + if (_has_bit(6)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(7, this->default_value(), output)); + } + + // optional .google.protobuf.FieldOptions options = 8; + if (_has_bit(7)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(8, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FieldDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional int32 number = 3; + if (has_number()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->number()); + } + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + if (has_label()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->label()); + } + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + if (has_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->type()); + } + + // optional string type_name = 6; + if (has_type_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->type_name()); + } + + // optional string extendee = 2; + if (has_extendee()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->extendee()); + } + + // optional string default_value = 7; + if (has_default_value()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->default_value()); + } + + // optional .google.protobuf.FieldOptions options = 8; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FieldDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FieldDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void FieldDescriptorProto::MergeFrom(const FieldDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_number(from.number()); + } + if (from._has_bit(2)) { + set_label(from.label()); + } + if (from._has_bit(3)) { + set_type(from.type()); + } + if (from._has_bit(4)) { + set_type_name(from.type_name()); + } + if (from._has_bit(5)) { + set_extendee(from.extendee()); + } + if (from._has_bit(6)) { + set_default_value(from.default_value()); + } + if (from._has_bit(7)) { + mutable_options()->::google::protobuf::FieldOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FieldDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FieldDescriptorProto::CopyFrom(const FieldDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FieldDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FieldDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* FieldDescriptorProto::GetReflection() const { + if (FieldDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldDescriptorProto_reflection_; +} + +// =================================================================== + +const EnumDescriptorProto EnumDescriptorProto::default_instance_; + +const ::std::string EnumDescriptorProto::_default_name_; + + +const int EnumDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumDescriptorProto, options_), +}; + +EnumDescriptorProto::EnumDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::EnumOptions*>(&::google::protobuf::EnumOptions::default_instance()); + } +} + +EnumDescriptorProto::EnumDescriptorProto(const EnumDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumDescriptorProto::~EnumDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* EnumDescriptorProto::descriptor() { + if (EnumDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumDescriptorProto_descriptor_; +} + +EnumDescriptorProto* EnumDescriptorProto::New() const { + return new EnumDescriptorProto; +} + +void EnumDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); + } + } + value_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_value; + break; + } + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_value: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_value())); + if (input->ExpectTag(18)) goto parse_value; + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.EnumOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool EnumDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + for (int i = 0; i < value_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->value(i), output)); + } + + // optional .google.protobuf.EnumOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.EnumOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + total_size += 1 * value_size(); + for (int i = 0; i < value_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->value(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const EnumDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void EnumDescriptorProto::MergeFrom(const EnumDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + value_.MergeFrom(from.value_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::EnumOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumDescriptorProto::CopyFrom(const EnumDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* EnumDescriptorProto::GetReflection() const { + if (EnumDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumDescriptorProto_reflection_; +} + +// =================================================================== + +const EnumValueDescriptorProto EnumValueDescriptorProto::default_instance_; + +const ::std::string EnumValueDescriptorProto::_default_name_; + + +const int EnumValueDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, number_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(EnumValueDescriptorProto, options_), +}; + +EnumValueDescriptorProto::EnumValueDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::EnumValueOptions*>(&::google::protobuf::EnumValueOptions::default_instance()); + } +} + +EnumValueDescriptorProto::EnumValueDescriptorProto(const EnumValueDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + number_(0), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumValueDescriptorProto::~EnumValueDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* EnumValueDescriptorProto::descriptor() { + if (EnumValueDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueDescriptorProto_descriptor_; +} + +EnumValueDescriptorProto* EnumValueDescriptorProto::New() const { + return new EnumValueDescriptorProto; +} + +void EnumValueDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + number_ = 0; + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumValueDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(16)) goto parse_number; + break; + } + + // optional int32 number = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_number: + DO_(::google::protobuf::internal::WireFormat::ReadInt32( + input, &number_)); + _set_bit(1); + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.EnumValueOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool EnumValueDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional int32 number = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteInt32(2, this->number(), output)); + } + + // optional .google.protobuf.EnumValueOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumValueDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional int32 number = 2; + if (has_number()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::Int32Size( + this->number()); + } + + // optional .google.protobuf.EnumValueOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumValueDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const EnumValueDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void EnumValueDescriptorProto::MergeFrom(const EnumValueDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_number(from.number()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::EnumValueOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumValueDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumValueDescriptorProto::CopyFrom(const EnumValueDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumValueDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumValueDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* EnumValueDescriptorProto::GetReflection() const { + if (EnumValueDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueDescriptorProto_reflection_; +} + +// =================================================================== + +const ServiceDescriptorProto ServiceDescriptorProto::default_instance_; + +const ::std::string ServiceDescriptorProto::_default_name_; + + +const int ServiceDescriptorProto::_offsets_[3] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, method_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(ServiceDescriptorProto, options_), +}; + +ServiceDescriptorProto::ServiceDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::ServiceOptions*>(&::google::protobuf::ServiceOptions::default_instance()); + } +} + +ServiceDescriptorProto::ServiceDescriptorProto(const ServiceDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +ServiceDescriptorProto::~ServiceDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* ServiceDescriptorProto::descriptor() { + if (ServiceDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceDescriptorProto_descriptor_; +} + +ServiceDescriptorProto* ServiceDescriptorProto::New() const { + return new ServiceDescriptorProto; +} + +void ServiceDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(2)) { + if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); + } + } + method_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool ServiceDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_method; + break; + } + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_method: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, add_method())); + if (input->ExpectTag(18)) goto parse_method; + if (input->ExpectTag(26)) goto parse_options; + break; + } + + // optional .google.protobuf.ServiceOptions options = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool ServiceDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + for (int i = 0; i < method_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(2, this->method(i), output)); + } + + // optional .google.protobuf.ServiceOptions options = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(3, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int ServiceDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional .google.protobuf.ServiceOptions options = 3; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + // repeated .google.protobuf.MethodDescriptorProto method = 2; + total_size += 1 * method_size(); + for (int i = 0; i < method_size(); i++) { + total_size += + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->method(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void ServiceDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const ServiceDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void ServiceDescriptorProto::MergeFrom(const ServiceDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + method_.MergeFrom(from.method_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(2)) { + mutable_options()->::google::protobuf::ServiceOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void ServiceDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ServiceDescriptorProto::CopyFrom(const ServiceDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ServiceDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* ServiceDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* ServiceDescriptorProto::GetReflection() const { + if (ServiceDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceDescriptorProto_reflection_; +} + +// =================================================================== + +const MethodDescriptorProto MethodDescriptorProto::default_instance_; + +const ::std::string MethodDescriptorProto::_default_name_; +const ::std::string MethodDescriptorProto::_default_input_type_; +const ::std::string MethodDescriptorProto::_default_output_type_; + +const int MethodDescriptorProto::_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, input_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, output_type_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MethodDescriptorProto, options_), +}; + +MethodDescriptorProto::MethodDescriptorProto() + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + input_type_(const_cast< ::std::string*>(&_default_input_type_)), + output_type_(const_cast< ::std::string*>(&_default_output_type_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + options_ = const_cast< ::google::protobuf::MethodOptions*>(&::google::protobuf::MethodOptions::default_instance()); + } +} + +MethodDescriptorProto::MethodDescriptorProto(const MethodDescriptorProto& from) + : _cached_size_(0), + name_(const_cast< ::std::string*>(&_default_name_)), + input_type_(const_cast< ::std::string*>(&_default_input_type_)), + output_type_(const_cast< ::std::string*>(&_default_output_type_)), + options_(NULL) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MethodDescriptorProto::~MethodDescriptorProto() { + if (name_ != &_default_name_) { + delete name_; + } + if (input_type_ != &_default_input_type_) { + delete input_type_; + } + if (output_type_ != &_default_output_type_) { + delete output_type_; + } + if (this != &default_instance_) { + delete options_; + } +} + +const ::google::protobuf::Descriptor* MethodDescriptorProto::descriptor() { + if (MethodDescriptorProto_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodDescriptorProto_descriptor_; +} + +MethodDescriptorProto* MethodDescriptorProto::New() const { + return new MethodDescriptorProto; +} + +void MethodDescriptorProto::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (name_ != &_default_name_) { + name_->clear(); + } + } + if (_has_bit(1)) { + if (input_type_ != &_default_input_type_) { + input_type_->clear(); + } + } + if (_has_bit(2)) { + if (output_type_ != &_default_output_type_) { + output_type_->clear(); + } + } + if (_has_bit(3)) { + if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MethodDescriptorProto::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string name = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_name())); + if (input->ExpectTag(18)) goto parse_input_type; + break; + } + + // optional string input_type = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_input_type: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_input_type())); + if (input->ExpectTag(26)) goto parse_output_type; + break; + } + + // optional string output_type = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_output_type: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_output_type())); + if (input->ExpectTag(34)) goto parse_options; + break; + } + + // optional .google.protobuf.MethodOptions options = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_options: + DO_(::google::protobuf::internal::WireFormat::ReadMessageNoVirtual( + input, mutable_options())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool MethodDescriptorProto::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string name = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->name(), output)); + } + + // optional string input_type = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->input_type(), output)); + } + + // optional string output_type = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->output_type(), output)); + } + + // optional .google.protobuf.MethodOptions options = 4; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteMessageNoVirtual(4, this->options(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MethodDescriptorProto::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string name = 1; + if (has_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->name()); + } + + // optional string input_type = 2; + if (has_input_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->input_type()); + } + + // optional string output_type = 3; + if (has_output_type()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->output_type()); + } + + // optional .google.protobuf.MethodOptions options = 4; + if (has_options()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::MessageSizeNoVirtual( + this->options()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MethodDescriptorProto::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const MethodDescriptorProto* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void MethodDescriptorProto::MergeFrom(const MethodDescriptorProto& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_name(from.name()); + } + if (from._has_bit(1)) { + set_input_type(from.input_type()); + } + if (from._has_bit(2)) { + set_output_type(from.output_type()); + } + if (from._has_bit(3)) { + mutable_options()->::google::protobuf::MethodOptions::MergeFrom(from.options()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MethodDescriptorProto::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MethodDescriptorProto::CopyFrom(const MethodDescriptorProto& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MethodDescriptorProto::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MethodDescriptorProto::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* MethodDescriptorProto::GetReflection() const { + if (MethodDescriptorProto_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodDescriptorProto_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor() { + if (FileOptions_OptimizeMode_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileOptions_OptimizeMode_descriptor_; +} +bool FileOptions_OptimizeMode_IsValid(int value) { + switch(value) { + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FileOptions_OptimizeMode FileOptions::SPEED; +const FileOptions_OptimizeMode FileOptions::CODE_SIZE; +const FileOptions_OptimizeMode FileOptions::OptimizeMode_MIN; +const FileOptions_OptimizeMode FileOptions::OptimizeMode_MAX; +#endif // _MSC_VER +const FileOptions FileOptions::default_instance_; + +const ::std::string FileOptions::_default_java_package_; +const ::std::string FileOptions::_default_java_outer_classname_; + + +const int FileOptions::_offsets_[4] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_package_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_outer_classname_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, java_multiple_files_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FileOptions, optimize_for_), +}; + +FileOptions::FileOptions() + : _cached_size_(0), + java_package_(const_cast< ::std::string*>(&_default_java_package_)), + java_outer_classname_(const_cast< ::std::string*>(&_default_java_outer_classname_)), + java_multiple_files_(false), + optimize_for_(2) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +FileOptions::FileOptions(const FileOptions& from) + : _cached_size_(0), + java_package_(const_cast< ::std::string*>(&_default_java_package_)), + java_outer_classname_(const_cast< ::std::string*>(&_default_java_outer_classname_)), + java_multiple_files_(false), + optimize_for_(2) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FileOptions::~FileOptions() { + if (java_package_ != &_default_java_package_) { + delete java_package_; + } + if (java_outer_classname_ != &_default_java_outer_classname_) { + delete java_outer_classname_; + } + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* FileOptions::descriptor() { + if (FileOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileOptions_descriptor_; +} + +FileOptions* FileOptions::New() const { + return new FileOptions; +} + +void FileOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (java_package_ != &_default_java_package_) { + java_package_->clear(); + } + } + if (_has_bit(1)) { + if (java_outer_classname_ != &_default_java_outer_classname_) { + java_outer_classname_->clear(); + } + } + java_multiple_files_ = false; + optimize_for_ = 2; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FileOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional string java_package = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_java_package())); + if (input->ExpectTag(66)) goto parse_java_outer_classname; + break; + } + + // optional string java_outer_classname = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_java_outer_classname: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_java_outer_classname())); + if (input->ExpectTag(72)) goto parse_optimize_for; + break; + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + case 9: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_optimize_for: + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FileOptions_OptimizeMode_IsValid(value)) { + set_optimize_for(static_cast< ::google::protobuf::FileOptions_OptimizeMode >(value)); + } else { + mutable_unknown_fields()->AddField(9)->add_varint(value); + } + if (input->ExpectTag(80)) goto parse_java_multiple_files; + break; + } + + // optional bool java_multiple_files = 10 [default = false]; + case 10: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_java_multiple_files: + DO_(::google::protobuf::internal::WireFormat::ReadBool( + input, &java_multiple_files_)); + _set_bit(2); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FileOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional string java_package = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->java_package(), output)); + } + + // optional string java_outer_classname = 8; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(8, this->java_outer_classname(), output)); + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(9, this->optimize_for(), output)); + } + + // optional bool java_multiple_files = 10 [default = false]; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteBool(10, this->java_multiple_files(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FileOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional string java_package = 1; + if (has_java_package()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->java_package()); + } + + // optional string java_outer_classname = 8; + if (has_java_outer_classname()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->java_outer_classname()); + } + + // optional bool java_multiple_files = 10 [default = false]; + if (has_java_multiple_files()) { + total_size += 1 + 1; + } + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + if (has_optimize_for()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->optimize_for()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FileOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FileOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void FileOptions::MergeFrom(const FileOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_java_package(from.java_package()); + } + if (from._has_bit(1)) { + set_java_outer_classname(from.java_outer_classname()); + } + if (from._has_bit(2)) { + set_java_multiple_files(from.java_multiple_files()); + } + if (from._has_bit(3)) { + set_optimize_for(from.optimize_for()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FileOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileOptions::CopyFrom(const FileOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FileOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* FileOptions::GetReflection() const { + if (FileOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FileOptions_reflection_; +} + +// =================================================================== + +const MessageOptions MessageOptions::default_instance_; + + +const int MessageOptions::_offsets_[1] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(MessageOptions, message_set_wire_format_), +}; + +MessageOptions::MessageOptions() + : _cached_size_(0), + message_set_wire_format_(false) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +MessageOptions::MessageOptions(const MessageOptions& from) + : _cached_size_(0), + message_set_wire_format_(false) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MessageOptions::~MessageOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* MessageOptions::descriptor() { + if (MessageOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MessageOptions_descriptor_; +} + +MessageOptions* MessageOptions::New() const { + return new MessageOptions; +} + +void MessageOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + message_set_wire_format_ = false; + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MessageOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional bool message_set_wire_format = 1 [default = false]; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadBool( + input, &message_set_wire_format_)); + _set_bit(0); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool MessageOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional bool message_set_wire_format = 1 [default = false]; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteBool(1, this->message_set_wire_format(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MessageOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional bool message_set_wire_format = 1 [default = false]; + if (has_message_set_wire_format()) { + total_size += 1 + 1; + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MessageOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const MessageOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void MessageOptions::MergeFrom(const MessageOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_message_set_wire_format(from.message_set_wire_format()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MessageOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MessageOptions::CopyFrom(const MessageOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MessageOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MessageOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* MessageOptions::GetReflection() const { + if (MessageOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MessageOptions_reflection_; +} + +// =================================================================== + +const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor() { + if (FieldOptions_CType_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldOptions_CType_descriptor_; +} +bool FieldOptions_CType_IsValid(int value) { + switch(value) { + case 1: + case 2: + return true; + default: + return false; + } +} + +#ifndef _MSC_VER +const FieldOptions_CType FieldOptions::CORD; +const FieldOptions_CType FieldOptions::STRING_PIECE; +const FieldOptions_CType FieldOptions::CType_MIN; +const FieldOptions_CType FieldOptions::CType_MAX; +#endif // _MSC_VER +const FieldOptions FieldOptions::default_instance_; + + +const ::std::string FieldOptions::_default_experimental_map_key_; +const int FieldOptions::_offsets_[2] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, ctype_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(FieldOptions, experimental_map_key_), +}; + +FieldOptions::FieldOptions() + : _cached_size_(0), + ctype_(1), + experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +FieldOptions::FieldOptions(const FieldOptions& from) + : _cached_size_(0), + ctype_(1), + experimental_map_key_(const_cast< ::std::string*>(&_default_experimental_map_key_)) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +FieldOptions::~FieldOptions() { + if (experimental_map_key_ != &_default_experimental_map_key_) { + delete experimental_map_key_; + } + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* FieldOptions::descriptor() { + if (FieldOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldOptions_descriptor_; +} + +FieldOptions* FieldOptions::New() const { + return new FieldOptions; +} + +void FieldOptions::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + ctype_ = 1; + if (_has_bit(1)) { + if (experimental_map_key_ != &_default_experimental_map_key_) { + experimental_map_key_->clear(); + } + } + } + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool FieldOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // optional .google.protobuf.FieldOptions.CType ctype = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + int value; + DO_(::google::protobuf::internal::WireFormat::ReadEnum(input, &value)); + if (::google::protobuf::FieldOptions_CType_IsValid(value)) { + set_ctype(static_cast< ::google::protobuf::FieldOptions_CType >(value)); + } else { + mutable_unknown_fields()->AddField(1)->add_varint(value); + } + if (input->ExpectTag(74)) goto parse_experimental_map_key; + break; + } + + // optional string experimental_map_key = 9; + case 9: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_experimental_map_key: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_experimental_map_key())); + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool FieldOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // optional .google.protobuf.FieldOptions.CType ctype = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteEnum(1, this->ctype(), output)); + } + + // optional string experimental_map_key = 9; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(9, this->experimental_map_key(), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int FieldOptions::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // optional .google.protobuf.FieldOptions.CType ctype = 1; + if (has_ctype()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::EnumSize(this->ctype()); + } + + // optional string experimental_map_key = 9; + if (has_experimental_map_key()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->experimental_map_key()); + } + + } + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void FieldOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const FieldOptions* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void FieldOptions::MergeFrom(const FieldOptions& from) { + GOOGLE_CHECK_NE(&from, this); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_ctype(from.ctype()); + } + if (from._has_bit(1)) { + set_experimental_map_key(from.experimental_map_key()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void FieldOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FieldOptions::CopyFrom(const FieldOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FieldOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* FieldOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* FieldOptions::GetReflection() const { + if (FieldOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return FieldOptions_reflection_; +} + +// =================================================================== + +const EnumOptions EnumOptions::default_instance_; + +const int EnumOptions::_offsets_[1] = { +}; + +EnumOptions::EnumOptions() + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +EnumOptions::EnumOptions(const EnumOptions& from) + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumOptions::~EnumOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* EnumOptions::descriptor() { + if (EnumOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumOptions_descriptor_; +} + +EnumOptions* EnumOptions::New() const { + return new EnumOptions; +} + +void EnumOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool EnumOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void EnumOptions::MergeFrom(const EnumOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumOptions::CopyFrom(const EnumOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* EnumOptions::GetReflection() const { + if (EnumOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumOptions_reflection_; +} + +// =================================================================== + +const EnumValueOptions EnumValueOptions::default_instance_; + +const int EnumValueOptions::_offsets_[1] = { +}; + +EnumValueOptions::EnumValueOptions() + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +EnumValueOptions::EnumValueOptions(const EnumValueOptions& from) + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +EnumValueOptions::~EnumValueOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* EnumValueOptions::descriptor() { + if (EnumValueOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueOptions_descriptor_; +} + +EnumValueOptions* EnumValueOptions::New() const { + return new EnumValueOptions; +} + +void EnumValueOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool EnumValueOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool EnumValueOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int EnumValueOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void EnumValueOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void EnumValueOptions::MergeFrom(const EnumValueOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void EnumValueOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void EnumValueOptions::CopyFrom(const EnumValueOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool EnumValueOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* EnumValueOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* EnumValueOptions::GetReflection() const { + if (EnumValueOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return EnumValueOptions_reflection_; +} + +// =================================================================== + +const ServiceOptions ServiceOptions::default_instance_; + +const int ServiceOptions::_offsets_[1] = { +}; + +ServiceOptions::ServiceOptions() + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +ServiceOptions::ServiceOptions(const ServiceOptions& from) + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +ServiceOptions::~ServiceOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* ServiceOptions::descriptor() { + if (ServiceOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceOptions_descriptor_; +} + +ServiceOptions* ServiceOptions::New() const { + return new ServiceOptions; +} + +void ServiceOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool ServiceOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool ServiceOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int ServiceOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void ServiceOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void ServiceOptions::MergeFrom(const ServiceOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void ServiceOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ServiceOptions::CopyFrom(const ServiceOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ServiceOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* ServiceOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* ServiceOptions::GetReflection() const { + if (ServiceOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return ServiceOptions_reflection_; +} + +// =================================================================== + +const MethodOptions MethodOptions::default_instance_; + +const int MethodOptions::_offsets_[1] = { +}; + +MethodOptions::MethodOptions() + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + if (this == &default_instance_) { + } +} + +MethodOptions::MethodOptions(const MethodOptions& from) + : _cached_size_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +MethodOptions::~MethodOptions() { + if (this != &default_instance_) { + } +} + +const ::google::protobuf::Descriptor* MethodOptions::descriptor() { + if (MethodOptions_descriptor_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodOptions_descriptor_; +} + +MethodOptions* MethodOptions::New() const { + return new MethodOptions; +} + +void MethodOptions::Clear() { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool MethodOptions::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + } + return true; +#undef DO_ +} + +bool MethodOptions::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int MethodOptions::ByteSize() const { + int total_size = 0; + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void MethodOptions::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); +} + +void MethodOptions::MergeFrom(const MethodOptions& from) { + GOOGLE_CHECK_NE(&from, this); + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void MethodOptions::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void MethodOptions::CopyFrom(const MethodOptions& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool MethodOptions::IsInitialized() const { + + return true; +} + +const ::google::protobuf::Descriptor* MethodOptions::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* MethodOptions::GetReflection() const { + if (MethodOptions_reflection_ == NULL) protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + return MethodOptions_reflection_; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h new file mode 100644 index 00000000..926e9bd0 --- /dev/null +++ b/src/google/protobuf/descriptor.pb.h @@ -0,0 +1,3062 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#ifndef PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED +#define PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2000001 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2000001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include + +namespace google { +namespace protobuf { + +// Internal implementation detail -- do not call this. +void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + +class FileDescriptorSet; +class FileDescriptorProto; +class DescriptorProto; +class DescriptorProto_ExtensionRange; +class FieldDescriptorProto; +class EnumDescriptorProto; +class EnumValueDescriptorProto; +class ServiceDescriptorProto; +class MethodDescriptorProto; +class FileOptions; +class MessageOptions; +class FieldOptions; +class EnumOptions; +class EnumValueOptions; +class ServiceOptions; +class MethodOptions; + +enum FieldDescriptorProto_Type { + FieldDescriptorProto_Type_TYPE_DOUBLE = 1, + FieldDescriptorProto_Type_TYPE_FLOAT = 2, + FieldDescriptorProto_Type_TYPE_INT64 = 3, + FieldDescriptorProto_Type_TYPE_UINT64 = 4, + FieldDescriptorProto_Type_TYPE_INT32 = 5, + FieldDescriptorProto_Type_TYPE_FIXED64 = 6, + FieldDescriptorProto_Type_TYPE_FIXED32 = 7, + FieldDescriptorProto_Type_TYPE_BOOL = 8, + FieldDescriptorProto_Type_TYPE_STRING = 9, + FieldDescriptorProto_Type_TYPE_GROUP = 10, + FieldDescriptorProto_Type_TYPE_MESSAGE = 11, + FieldDescriptorProto_Type_TYPE_BYTES = 12, + FieldDescriptorProto_Type_TYPE_UINT32 = 13, + FieldDescriptorProto_Type_TYPE_ENUM = 14, + FieldDescriptorProto_Type_TYPE_SFIXED32 = 15, + FieldDescriptorProto_Type_TYPE_SFIXED64 = 16, + FieldDescriptorProto_Type_TYPE_SINT32 = 17, + FieldDescriptorProto_Type_TYPE_SINT64 = 18, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Type_descriptor(); +LIBPROTOBUF_EXPORT bool FieldDescriptorProto_Type_IsValid(int value); +const FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MIN = FieldDescriptorProto_Type_TYPE_DOUBLE; +const FieldDescriptorProto_Type FieldDescriptorProto_Type_Type_MAX = FieldDescriptorProto_Type_TYPE_SINT64; + +enum FieldDescriptorProto_Label { + FieldDescriptorProto_Label_LABEL_OPTIONAL = 1, + FieldDescriptorProto_Label_LABEL_REQUIRED = 2, + FieldDescriptorProto_Label_LABEL_REPEATED = 3, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldDescriptorProto_Label_descriptor(); +LIBPROTOBUF_EXPORT bool FieldDescriptorProto_Label_IsValid(int value); +const FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MIN = FieldDescriptorProto_Label_LABEL_OPTIONAL; +const FieldDescriptorProto_Label FieldDescriptorProto_Label_Label_MAX = FieldDescriptorProto_Label_LABEL_REPEATED; + +enum FileOptions_OptimizeMode { + FileOptions_OptimizeMode_SPEED = 1, + FileOptions_OptimizeMode_CODE_SIZE = 2, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FileOptions_OptimizeMode_descriptor(); +LIBPROTOBUF_EXPORT bool FileOptions_OptimizeMode_IsValid(int value); +const FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MIN = FileOptions_OptimizeMode_SPEED; +const FileOptions_OptimizeMode FileOptions_OptimizeMode_OptimizeMode_MAX = FileOptions_OptimizeMode_CODE_SIZE; + +enum FieldOptions_CType { + FieldOptions_CType_CORD = 1, + FieldOptions_CType_STRING_PIECE = 2, +}; +LIBPROTOBUF_EXPORT const ::google::protobuf::EnumDescriptor* FieldOptions_CType_descriptor(); +LIBPROTOBUF_EXPORT bool FieldOptions_CType_IsValid(int value); +const FieldOptions_CType FieldOptions_CType_CType_MIN = FieldOptions_CType_CORD; +const FieldOptions_CType FieldOptions_CType_CType_MAX = FieldOptions_CType_STRING_PIECE; + +// =================================================================== + +class LIBPROTOBUF_EXPORT FileDescriptorSet : public ::google::protobuf::Message { + public: + FileDescriptorSet(); + virtual ~FileDescriptorSet(); + + FileDescriptorSet(const FileDescriptorSet& from); + + inline FileDescriptorSet& operator=(const FileDescriptorSet& from) { + CopyFrom(from); + return *this; + } + + inline static const FileDescriptorSet& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FileDescriptorSet* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FileDescriptorSet& from); + void MergeFrom(const FileDescriptorSet& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // repeated .google.protobuf.FileDescriptorProto file = 1; + inline int file_size() const; + inline void clear_file(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& file() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* mutable_file(); + inline const ::google::protobuf::FileDescriptorProto& file(int index) const; + inline ::google::protobuf::FileDescriptorProto* mutable_file(int index); + inline ::google::protobuf::FileDescriptorProto* add_file(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto > file_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const FileDescriptorSet default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FileDescriptorProto : public ::google::protobuf::Message { + public: + FileDescriptorProto(); + virtual ~FileDescriptorProto(); + + FileDescriptorProto(const FileDescriptorProto& from); + + inline FileDescriptorProto& operator=(const FileDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const FileDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FileDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FileDescriptorProto& from); + void MergeFrom(const FileDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional string package = 2; + inline bool has_package() const; + inline void clear_package(); + inline const ::std::string& package() const; + inline void set_package(const ::std::string& value); + inline void set_package(const char* value); + inline ::std::string* mutable_package(); + + // repeated string dependency = 3; + inline int dependency_size() const; + inline void clear_dependency(); + inline const ::google::protobuf::RepeatedPtrField< ::std::string>& dependency() const; + inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_dependency(); + inline const ::std::string& dependency(int index) const; + inline ::std::string* mutable_dependency(int index); + inline void set_dependency(int index, const ::std::string& value); + inline void set_dependency(int index, const char* value); + inline ::std::string* add_dependency(); + inline void add_dependency(const ::std::string& value); + inline void add_dependency(const char* value); + + // repeated .google.protobuf.DescriptorProto message_type = 4; + inline int message_type_size() const; + inline void clear_message_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& message_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_message_type(); + inline const ::google::protobuf::DescriptorProto& message_type(int index) const; + inline ::google::protobuf::DescriptorProto* mutable_message_type(int index); + inline ::google::protobuf::DescriptorProto* add_message_type(); + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 5; + inline int enum_type_size() const; + inline void clear_enum_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& enum_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + inline const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; + inline ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); + inline ::google::protobuf::EnumDescriptorProto* add_enum_type(); + + // repeated .google.protobuf.ServiceDescriptorProto service = 6; + inline int service_size() const; + inline void clear_service(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& service() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* mutable_service(); + inline const ::google::protobuf::ServiceDescriptorProto& service(int index) const; + inline ::google::protobuf::ServiceDescriptorProto* mutable_service(int index); + inline ::google::protobuf::ServiceDescriptorProto* add_service(); + + // repeated .google.protobuf.FieldDescriptorProto extension = 7; + inline int extension_size() const; + inline void clear_extension(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& extension() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + inline const ::google::protobuf::FieldDescriptorProto& extension(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); + inline ::google::protobuf::FieldDescriptorProto* add_extension(); + + // optional .google.protobuf.FileOptions options = 8; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::FileOptions& options() const; + inline ::google::protobuf::FileOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::std::string* package_; + static const ::std::string _default_package_; + ::google::protobuf::RepeatedPtrField< ::std::string> dependency_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > message_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto > service_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; + ::google::protobuf::FileOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const FileDescriptorProto default_instance_; + static const int _offsets_[8]; + + ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT DescriptorProto_ExtensionRange : public ::google::protobuf::Message { + public: + DescriptorProto_ExtensionRange(); + virtual ~DescriptorProto_ExtensionRange(); + + DescriptorProto_ExtensionRange(const DescriptorProto_ExtensionRange& from); + + inline DescriptorProto_ExtensionRange& operator=(const DescriptorProto_ExtensionRange& from) { + CopyFrom(from); + return *this; + } + + inline static const DescriptorProto_ExtensionRange& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + DescriptorProto_ExtensionRange* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto_ExtensionRange& from); + void MergeFrom(const DescriptorProto_ExtensionRange& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional int32 start = 1; + inline bool has_start() const; + inline void clear_start(); + inline ::google::protobuf::int32 start() const; + inline void set_start(::google::protobuf::int32 value); + + // optional int32 end = 2; + inline bool has_end() const; + inline void clear_end(); + inline ::google::protobuf::int32 end() const; + inline void set_end(::google::protobuf::int32 value); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::google::protobuf::int32 start_; + ::google::protobuf::int32 end_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const DescriptorProto_ExtensionRange default_instance_; + static const int _offsets_[2]; + + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT DescriptorProto : public ::google::protobuf::Message { + public: + DescriptorProto(); + virtual ~DescriptorProto(); + + DescriptorProto(const DescriptorProto& from); + + inline DescriptorProto& operator=(const DescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const DescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + DescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const DescriptorProto& from); + void MergeFrom(const DescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + typedef DescriptorProto_ExtensionRange ExtensionRange; + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.FieldDescriptorProto field = 2; + inline int field_size() const; + inline void clear_field(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& field() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_field(); + inline const ::google::protobuf::FieldDescriptorProto& field(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_field(int index); + inline ::google::protobuf::FieldDescriptorProto* add_field(); + + // repeated .google.protobuf.FieldDescriptorProto extension = 6; + inline int extension_size() const; + inline void clear_extension(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& extension() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* mutable_extension(); + inline const ::google::protobuf::FieldDescriptorProto& extension(int index) const; + inline ::google::protobuf::FieldDescriptorProto* mutable_extension(int index); + inline ::google::protobuf::FieldDescriptorProto* add_extension(); + + // repeated .google.protobuf.DescriptorProto nested_type = 3; + inline int nested_type_size() const; + inline void clear_nested_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& nested_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* mutable_nested_type(); + inline const ::google::protobuf::DescriptorProto& nested_type(int index) const; + inline ::google::protobuf::DescriptorProto* mutable_nested_type(int index); + inline ::google::protobuf::DescriptorProto* add_nested_type(); + + // repeated .google.protobuf.EnumDescriptorProto enum_type = 4; + inline int enum_type_size() const; + inline void clear_enum_type(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& enum_type() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* mutable_enum_type(); + inline const ::google::protobuf::EnumDescriptorProto& enum_type(int index) const; + inline ::google::protobuf::EnumDescriptorProto* mutable_enum_type(int index); + inline ::google::protobuf::EnumDescriptorProto* add_enum_type(); + + // repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; + inline int extension_range_size() const; + inline void clear_extension_range(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& extension_range() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* mutable_extension_range(); + inline const ::google::protobuf::DescriptorProto_ExtensionRange& extension_range(int index) const; + inline ::google::protobuf::DescriptorProto_ExtensionRange* mutable_extension_range(int index); + inline ::google::protobuf::DescriptorProto_ExtensionRange* add_extension_range(); + + // optional .google.protobuf.MessageOptions options = 7; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::MessageOptions& options() const; + inline ::google::protobuf::MessageOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > field_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto > extension_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto > nested_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto > enum_type_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange > extension_range_; + ::google::protobuf::MessageOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const DescriptorProto default_instance_; + static const int _offsets_[7]; + + ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FieldDescriptorProto : public ::google::protobuf::Message { + public: + FieldDescriptorProto(); + virtual ~FieldDescriptorProto(); + + FieldDescriptorProto(const FieldDescriptorProto& from); + + inline FieldDescriptorProto& operator=(const FieldDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const FieldDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FieldDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FieldDescriptorProto& from); + void MergeFrom(const FieldDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + typedef FieldDescriptorProto_Type Type; + static const Type TYPE_DOUBLE = FieldDescriptorProto_Type_TYPE_DOUBLE; + static const Type TYPE_FLOAT = FieldDescriptorProto_Type_TYPE_FLOAT; + static const Type TYPE_INT64 = FieldDescriptorProto_Type_TYPE_INT64; + static const Type TYPE_UINT64 = FieldDescriptorProto_Type_TYPE_UINT64; + static const Type TYPE_INT32 = FieldDescriptorProto_Type_TYPE_INT32; + static const Type TYPE_FIXED64 = FieldDescriptorProto_Type_TYPE_FIXED64; + static const Type TYPE_FIXED32 = FieldDescriptorProto_Type_TYPE_FIXED32; + static const Type TYPE_BOOL = FieldDescriptorProto_Type_TYPE_BOOL; + static const Type TYPE_STRING = FieldDescriptorProto_Type_TYPE_STRING; + static const Type TYPE_GROUP = FieldDescriptorProto_Type_TYPE_GROUP; + static const Type TYPE_MESSAGE = FieldDescriptorProto_Type_TYPE_MESSAGE; + static const Type TYPE_BYTES = FieldDescriptorProto_Type_TYPE_BYTES; + static const Type TYPE_UINT32 = FieldDescriptorProto_Type_TYPE_UINT32; + static const Type TYPE_ENUM = FieldDescriptorProto_Type_TYPE_ENUM; + static const Type TYPE_SFIXED32 = FieldDescriptorProto_Type_TYPE_SFIXED32; + static const Type TYPE_SFIXED64 = FieldDescriptorProto_Type_TYPE_SFIXED64; + static const Type TYPE_SINT32 = FieldDescriptorProto_Type_TYPE_SINT32; + static const Type TYPE_SINT64 = FieldDescriptorProto_Type_TYPE_SINT64; + static inline const ::google::protobuf::EnumDescriptor* + Type_descriptor() { + return FieldDescriptorProto_Type_descriptor(); + } + static inline bool Type_IsValid(int value) { + return FieldDescriptorProto_Type_IsValid(value); + } + static const Type Type_MIN = + FieldDescriptorProto_Type_Type_MIN; + static const Type Type_MAX = + FieldDescriptorProto_Type_Type_MAX; + + typedef FieldDescriptorProto_Label Label; + static const Label LABEL_OPTIONAL = FieldDescriptorProto_Label_LABEL_OPTIONAL; + static const Label LABEL_REQUIRED = FieldDescriptorProto_Label_LABEL_REQUIRED; + static const Label LABEL_REPEATED = FieldDescriptorProto_Label_LABEL_REPEATED; + static inline const ::google::protobuf::EnumDescriptor* + Label_descriptor() { + return FieldDescriptorProto_Label_descriptor(); + } + static inline bool Label_IsValid(int value) { + return FieldDescriptorProto_Label_IsValid(value); + } + static const Label Label_MIN = + FieldDescriptorProto_Label_Label_MIN; + static const Label Label_MAX = + FieldDescriptorProto_Label_Label_MAX; + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional int32 number = 3; + inline bool has_number() const; + inline void clear_number(); + inline ::google::protobuf::int32 number() const; + inline void set_number(::google::protobuf::int32 value); + + // optional .google.protobuf.FieldDescriptorProto.Label label = 4; + inline bool has_label() const; + inline void clear_label(); + inline ::google::protobuf::FieldDescriptorProto_Label label() const; + inline void set_label(::google::protobuf::FieldDescriptorProto_Label value); + + // optional .google.protobuf.FieldDescriptorProto.Type type = 5; + inline bool has_type() const; + inline void clear_type(); + inline ::google::protobuf::FieldDescriptorProto_Type type() const; + inline void set_type(::google::protobuf::FieldDescriptorProto_Type value); + + // optional string type_name = 6; + inline bool has_type_name() const; + inline void clear_type_name(); + inline const ::std::string& type_name() const; + inline void set_type_name(const ::std::string& value); + inline void set_type_name(const char* value); + inline ::std::string* mutable_type_name(); + + // optional string extendee = 2; + inline bool has_extendee() const; + inline void clear_extendee(); + inline const ::std::string& extendee() const; + inline void set_extendee(const ::std::string& value); + inline void set_extendee(const char* value); + inline ::std::string* mutable_extendee(); + + // optional string default_value = 7; + inline bool has_default_value() const; + inline void clear_default_value(); + inline const ::std::string& default_value() const; + inline void set_default_value(const ::std::string& value); + inline void set_default_value(const char* value); + inline ::std::string* mutable_default_value(); + + // optional .google.protobuf.FieldOptions options = 8; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::FieldOptions& options() const; + inline ::google::protobuf::FieldOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::int32 number_; + int label_; + int type_; + ::std::string* type_name_; + static const ::std::string _default_type_name_; + ::std::string* extendee_; + static const ::std::string _default_extendee_; + ::std::string* default_value_; + static const ::std::string _default_default_value_; + ::google::protobuf::FieldOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const FieldDescriptorProto default_instance_; + static const int _offsets_[8]; + + ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumDescriptorProto : public ::google::protobuf::Message { + public: + EnumDescriptorProto(); + virtual ~EnumDescriptorProto(); + + EnumDescriptorProto(const EnumDescriptorProto& from); + + inline EnumDescriptorProto& operator=(const EnumDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumDescriptorProto& from); + void MergeFrom(const EnumDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.EnumValueDescriptorProto value = 2; + inline int value_size() const; + inline void clear_value(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& value() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* mutable_value(); + inline const ::google::protobuf::EnumValueDescriptorProto& value(int index) const; + inline ::google::protobuf::EnumValueDescriptorProto* mutable_value(int index); + inline ::google::protobuf::EnumValueDescriptorProto* add_value(); + + // optional .google.protobuf.EnumOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::EnumOptions& options() const; + inline ::google::protobuf::EnumOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto > value_; + ::google::protobuf::EnumOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const EnumDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumValueDescriptorProto : public ::google::protobuf::Message { + public: + EnumValueDescriptorProto(); + virtual ~EnumValueDescriptorProto(); + + EnumValueDescriptorProto(const EnumValueDescriptorProto& from); + + inline EnumValueDescriptorProto& operator=(const EnumValueDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumValueDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumValueDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumValueDescriptorProto& from); + void MergeFrom(const EnumValueDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional int32 number = 2; + inline bool has_number() const; + inline void clear_number(); + inline ::google::protobuf::int32 number() const; + inline void set_number(::google::protobuf::int32 value); + + // optional .google.protobuf.EnumValueOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::EnumValueOptions& options() const; + inline ::google::protobuf::EnumValueOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::int32 number_; + ::google::protobuf::EnumValueOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const EnumValueDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT ServiceDescriptorProto : public ::google::protobuf::Message { + public: + ServiceDescriptorProto(); + virtual ~ServiceDescriptorProto(); + + ServiceDescriptorProto(const ServiceDescriptorProto& from); + + inline ServiceDescriptorProto& operator=(const ServiceDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const ServiceDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + ServiceDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const ServiceDescriptorProto& from); + void MergeFrom(const ServiceDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // repeated .google.protobuf.MethodDescriptorProto method = 2; + inline int method_size() const; + inline void clear_method(); + inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& method() const; + inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* mutable_method(); + inline const ::google::protobuf::MethodDescriptorProto& method(int index) const; + inline ::google::protobuf::MethodDescriptorProto* mutable_method(int index); + inline ::google::protobuf::MethodDescriptorProto* add_method(); + + // optional .google.protobuf.ServiceOptions options = 3; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::ServiceOptions& options() const; + inline ::google::protobuf::ServiceOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto > method_; + ::google::protobuf::ServiceOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const ServiceDescriptorProto default_instance_; + static const int _offsets_[3]; + + ::google::protobuf::uint32 _has_bits_[(3 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MethodDescriptorProto : public ::google::protobuf::Message { + public: + MethodDescriptorProto(); + virtual ~MethodDescriptorProto(); + + MethodDescriptorProto(const MethodDescriptorProto& from); + + inline MethodDescriptorProto& operator=(const MethodDescriptorProto& from) { + CopyFrom(from); + return *this; + } + + inline static const MethodDescriptorProto& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MethodDescriptorProto* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MethodDescriptorProto& from); + void MergeFrom(const MethodDescriptorProto& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional string name = 1; + inline bool has_name() const; + inline void clear_name(); + inline const ::std::string& name() const; + inline void set_name(const ::std::string& value); + inline void set_name(const char* value); + inline ::std::string* mutable_name(); + + // optional string input_type = 2; + inline bool has_input_type() const; + inline void clear_input_type(); + inline const ::std::string& input_type() const; + inline void set_input_type(const ::std::string& value); + inline void set_input_type(const char* value); + inline ::std::string* mutable_input_type(); + + // optional string output_type = 3; + inline bool has_output_type() const; + inline void clear_output_type(); + inline const ::std::string& output_type() const; + inline void set_output_type(const ::std::string& value); + inline void set_output_type(const char* value); + inline ::std::string* mutable_output_type(); + + // optional .google.protobuf.MethodOptions options = 4; + inline bool has_options() const; + inline void clear_options(); + inline const ::google::protobuf::MethodOptions& options() const; + inline ::google::protobuf::MethodOptions* mutable_options(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* name_; + static const ::std::string _default_name_; + ::std::string* input_type_; + static const ::std::string _default_input_type_; + ::std::string* output_type_; + static const ::std::string _default_output_type_; + ::google::protobuf::MethodOptions* options_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const MethodDescriptorProto default_instance_; + static const int _offsets_[4]; + + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FileOptions : public ::google::protobuf::Message { + public: + FileOptions(); + virtual ~FileOptions(); + + FileOptions(const FileOptions& from); + + inline FileOptions& operator=(const FileOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const FileOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FileOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FileOptions& from); + void MergeFrom(const FileOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + typedef FileOptions_OptimizeMode OptimizeMode; + static const OptimizeMode SPEED = FileOptions_OptimizeMode_SPEED; + static const OptimizeMode CODE_SIZE = FileOptions_OptimizeMode_CODE_SIZE; + static inline const ::google::protobuf::EnumDescriptor* + OptimizeMode_descriptor() { + return FileOptions_OptimizeMode_descriptor(); + } + static inline bool OptimizeMode_IsValid(int value) { + return FileOptions_OptimizeMode_IsValid(value); + } + static const OptimizeMode OptimizeMode_MIN = + FileOptions_OptimizeMode_OptimizeMode_MIN; + static const OptimizeMode OptimizeMode_MAX = + FileOptions_OptimizeMode_OptimizeMode_MAX; + + // accessors ------------------------------------------------------- + + // optional string java_package = 1; + inline bool has_java_package() const; + inline void clear_java_package(); + inline const ::std::string& java_package() const; + inline void set_java_package(const ::std::string& value); + inline void set_java_package(const char* value); + inline ::std::string* mutable_java_package(); + + // optional string java_outer_classname = 8; + inline bool has_java_outer_classname() const; + inline void clear_java_outer_classname(); + inline const ::std::string& java_outer_classname() const; + inline void set_java_outer_classname(const ::std::string& value); + inline void set_java_outer_classname(const char* value); + inline ::std::string* mutable_java_outer_classname(); + + // optional bool java_multiple_files = 10 [default = false]; + inline bool has_java_multiple_files() const; + inline void clear_java_multiple_files(); + inline bool java_multiple_files() const; + inline void set_java_multiple_files(bool value); + + // optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; + inline bool has_optimize_for() const; + inline void clear_optimize_for(); + inline ::google::protobuf::FileOptions_OptimizeMode optimize_for() const; + inline void set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* java_package_; + static const ::std::string _default_java_package_; + ::std::string* java_outer_classname_; + static const ::std::string _default_java_outer_classname_; + bool java_multiple_files_; + int optimize_for_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const FileOptions default_instance_; + static const int _offsets_[4]; + + ::google::protobuf::uint32 _has_bits_[(4 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MessageOptions : public ::google::protobuf::Message { + public: + MessageOptions(); + virtual ~MessageOptions(); + + MessageOptions(const MessageOptions& from); + + inline MessageOptions& operator=(const MessageOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const MessageOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MessageOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MessageOptions& from); + void MergeFrom(const MessageOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // optional bool message_set_wire_format = 1 [default = false]; + inline bool has_message_set_wire_format() const; + inline void clear_message_set_wire_format(); + inline bool message_set_wire_format() const; + inline void set_message_set_wire_format(bool value); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + bool message_set_wire_format_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const MessageOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT FieldOptions : public ::google::protobuf::Message { + public: + FieldOptions(); + virtual ~FieldOptions(); + + FieldOptions(const FieldOptions& from); + + inline FieldOptions& operator=(const FieldOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const FieldOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + FieldOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const FieldOptions& from); + void MergeFrom(const FieldOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + typedef FieldOptions_CType CType; + static const CType CORD = FieldOptions_CType_CORD; + static const CType STRING_PIECE = FieldOptions_CType_STRING_PIECE; + static inline const ::google::protobuf::EnumDescriptor* + CType_descriptor() { + return FieldOptions_CType_descriptor(); + } + static inline bool CType_IsValid(int value) { + return FieldOptions_CType_IsValid(value); + } + static const CType CType_MIN = + FieldOptions_CType_CType_MIN; + static const CType CType_MAX = + FieldOptions_CType_CType_MAX; + + // accessors ------------------------------------------------------- + + // optional .google.protobuf.FieldOptions.CType ctype = 1; + inline bool has_ctype() const; + inline void clear_ctype(); + inline ::google::protobuf::FieldOptions_CType ctype() const; + inline void set_ctype(::google::protobuf::FieldOptions_CType value); + + // optional string experimental_map_key = 9; + inline bool has_experimental_map_key() const; + inline void clear_experimental_map_key(); + inline const ::std::string& experimental_map_key() const; + inline void set_experimental_map_key(const ::std::string& value); + inline void set_experimental_map_key(const char* value); + inline ::std::string* mutable_experimental_map_key(); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + int ctype_; + ::std::string* experimental_map_key_; + static const ::std::string _default_experimental_map_key_; + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const FieldOptions default_instance_; + static const int _offsets_[2]; + + ::google::protobuf::uint32 _has_bits_[(2 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumOptions : public ::google::protobuf::Message { + public: + EnumOptions(); + virtual ~EnumOptions(); + + EnumOptions(const EnumOptions& from); + + inline EnumOptions& operator=(const EnumOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumOptions& from); + void MergeFrom(const EnumOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const EnumOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT EnumValueOptions : public ::google::protobuf::Message { + public: + EnumValueOptions(); + virtual ~EnumValueOptions(); + + EnumValueOptions(const EnumValueOptions& from); + + inline EnumValueOptions& operator=(const EnumValueOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const EnumValueOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + EnumValueOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const EnumValueOptions& from); + void MergeFrom(const EnumValueOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const EnumValueOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT ServiceOptions : public ::google::protobuf::Message { + public: + ServiceOptions(); + virtual ~ServiceOptions(); + + ServiceOptions(const ServiceOptions& from); + + inline ServiceOptions& operator=(const ServiceOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const ServiceOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + ServiceOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const ServiceOptions& from); + void MergeFrom(const ServiceOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const ServiceOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// ------------------------------------------------------------------- + +class LIBPROTOBUF_EXPORT MethodOptions : public ::google::protobuf::Message { + public: + MethodOptions(); + virtual ~MethodOptions(); + + MethodOptions(const MethodOptions& from); + + inline MethodOptions& operator=(const MethodOptions& from) { + CopyFrom(from); + return *this; + } + + inline static const MethodOptions& default_instance() { + return default_instance_; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + + // implements Message ---------------------------------------------- + + MethodOptions* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const MethodOptions& from); + void MergeFrom(const MethodOptions& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + friend void protobuf_BuildDesc_google_2fprotobuf_2fdescriptor_2eproto(); + static const MethodOptions default_instance_; + static const int _offsets_[1]; + + ::google::protobuf::uint32 _has_bits_[1]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } +}; +// =================================================================== + + +// =================================================================== + + +// =================================================================== + +// FileDescriptorSet + +// repeated .google.protobuf.FileDescriptorProto file = 1; +inline int FileDescriptorSet::file_size() const { + return file_.size(); +} +inline void FileDescriptorSet::clear_file() { + file_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >& +FileDescriptorSet::file() const { + return file_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FileDescriptorProto >* +FileDescriptorSet::mutable_file() { + return &file_; +} +inline const ::google::protobuf::FileDescriptorProto& FileDescriptorSet::file(int index) const { + return file_.Get(index); +} +inline ::google::protobuf::FileDescriptorProto* FileDescriptorSet::mutable_file(int index) { + return file_.Mutable(index); +} +inline ::google::protobuf::FileDescriptorProto* FileDescriptorSet::add_file() { + return file_.Add(); +} + +// ------------------------------------------------------------------- + +// FileDescriptorProto + +// optional string name = 1; +inline bool FileDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void FileDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FileDescriptorProto::name() const { + return *name_; +} +inline void FileDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void FileDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* FileDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional string package = 2; +inline bool FileDescriptorProto::has_package() const { + return _has_bit(1); +} +inline void FileDescriptorProto::clear_package() { + if (package_ != &_default_package_) { + package_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FileDescriptorProto::package() const { + return *package_; +} +inline void FileDescriptorProto::set_package(const ::std::string& value) { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + package_->assign(value); +} +inline void FileDescriptorProto::set_package(const char* value) { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + package_->assign(value); +} +inline ::std::string* FileDescriptorProto::mutable_package() { + _set_bit(1); + if (package_ == &_default_package_) { + package_ = new ::std::string; + } + return package_; +} + +// repeated string dependency = 3; +inline int FileDescriptorProto::dependency_size() const { + return dependency_.size(); +} +inline void FileDescriptorProto::clear_dependency() { + dependency_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +FileDescriptorProto::dependency() const { + return dependency_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +FileDescriptorProto::mutable_dependency() { + return &dependency_; +} +inline const ::std::string& FileDescriptorProto::dependency(int index) const { + return dependency_.Get(index); +} +inline ::std::string* FileDescriptorProto::mutable_dependency(int index) { + return dependency_.Mutable(index); +} +inline void FileDescriptorProto::set_dependency(int index, const ::std::string& value) { + dependency_.Mutable(index)->assign(value); +} +inline void FileDescriptorProto::set_dependency(int index, const char* value) { + dependency_.Mutable(index)->assign(value); +} +inline ::std::string* FileDescriptorProto::add_dependency() { + return dependency_.Add(); +} +inline void FileDescriptorProto::add_dependency(const ::std::string& value) { + dependency_.Add()->assign(value); +} +inline void FileDescriptorProto::add_dependency(const char* value) { + dependency_.Add()->assign(value); +} + +// repeated .google.protobuf.DescriptorProto message_type = 4; +inline int FileDescriptorProto::message_type_size() const { + return message_type_.size(); +} +inline void FileDescriptorProto::clear_message_type() { + message_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +FileDescriptorProto::message_type() const { + return message_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +FileDescriptorProto::mutable_message_type() { + return &message_type_; +} +inline const ::google::protobuf::DescriptorProto& FileDescriptorProto::message_type(int index) const { + return message_type_.Get(index); +} +inline ::google::protobuf::DescriptorProto* FileDescriptorProto::mutable_message_type(int index) { + return message_type_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto* FileDescriptorProto::add_message_type() { + return message_type_.Add(); +} + +// repeated .google.protobuf.EnumDescriptorProto enum_type = 5; +inline int FileDescriptorProto::enum_type_size() const { + return enum_type_.size(); +} +inline void FileDescriptorProto::clear_enum_type() { + enum_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +FileDescriptorProto::enum_type() const { + return enum_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +FileDescriptorProto::mutable_enum_type() { + return &enum_type_; +} +inline const ::google::protobuf::EnumDescriptorProto& FileDescriptorProto::enum_type(int index) const { + return enum_type_.Get(index); +} +inline ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::mutable_enum_type(int index) { + return enum_type_.Mutable(index); +} +inline ::google::protobuf::EnumDescriptorProto* FileDescriptorProto::add_enum_type() { + return enum_type_.Add(); +} + +// repeated .google.protobuf.ServiceDescriptorProto service = 6; +inline int FileDescriptorProto::service_size() const { + return service_.size(); +} +inline void FileDescriptorProto::clear_service() { + service_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >& +FileDescriptorProto::service() const { + return service_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::ServiceDescriptorProto >* +FileDescriptorProto::mutable_service() { + return &service_; +} +inline const ::google::protobuf::ServiceDescriptorProto& FileDescriptorProto::service(int index) const { + return service_.Get(index); +} +inline ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::mutable_service(int index) { + return service_.Mutable(index); +} +inline ::google::protobuf::ServiceDescriptorProto* FileDescriptorProto::add_service() { + return service_.Add(); +} + +// repeated .google.protobuf.FieldDescriptorProto extension = 7; +inline int FileDescriptorProto::extension_size() const { + return extension_.size(); +} +inline void FileDescriptorProto::clear_extension() { + extension_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +FileDescriptorProto::extension() const { + return extension_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +FileDescriptorProto::mutable_extension() { + return &extension_; +} +inline const ::google::protobuf::FieldDescriptorProto& FileDescriptorProto::extension(int index) const { + return extension_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::mutable_extension(int index) { + return extension_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* FileDescriptorProto::add_extension() { + return extension_.Add(); +} + +// optional .google.protobuf.FileOptions options = 8; +inline bool FileDescriptorProto::has_options() const { + return _has_bit(7); +} +inline void FileDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::FileOptions::Clear(); + _clear_bit(7); +} +inline const ::google::protobuf::FileOptions& FileDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::FileOptions* FileDescriptorProto::mutable_options() { + _set_bit(7); + if (options_ == NULL) options_ = new ::google::protobuf::FileOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// DescriptorProto_ExtensionRange + +// optional int32 start = 1; +inline bool DescriptorProto_ExtensionRange::has_start() const { + return _has_bit(0); +} +inline void DescriptorProto_ExtensionRange::clear_start() { + start_ = 0; + _clear_bit(0); +} +inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::start() const { + return start_; +} +inline void DescriptorProto_ExtensionRange::set_start(::google::protobuf::int32 value) { + _set_bit(0); + start_ = value; +} + +// optional int32 end = 2; +inline bool DescriptorProto_ExtensionRange::has_end() const { + return _has_bit(1); +} +inline void DescriptorProto_ExtensionRange::clear_end() { + end_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 DescriptorProto_ExtensionRange::end() const { + return end_; +} +inline void DescriptorProto_ExtensionRange::set_end(::google::protobuf::int32 value) { + _set_bit(1); + end_ = value; +} + +// ------------------------------------------------------------------- + +// DescriptorProto + +// optional string name = 1; +inline bool DescriptorProto::has_name() const { + return _has_bit(0); +} +inline void DescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& DescriptorProto::name() const { + return *name_; +} +inline void DescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void DescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* DescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.FieldDescriptorProto field = 2; +inline int DescriptorProto::field_size() const { + return field_.size(); +} +inline void DescriptorProto::clear_field() { + field_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::field() const { + return field_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +DescriptorProto::mutable_field() { + return &field_; +} +inline const ::google::protobuf::FieldDescriptorProto& DescriptorProto::field(int index) const { + return field_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_field(int index) { + return field_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_field() { + return field_.Add(); +} + +// repeated .google.protobuf.FieldDescriptorProto extension = 6; +inline int DescriptorProto::extension_size() const { + return extension_.size(); +} +inline void DescriptorProto::clear_extension() { + extension_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >& +DescriptorProto::extension() const { + return extension_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::FieldDescriptorProto >* +DescriptorProto::mutable_extension() { + return &extension_; +} +inline const ::google::protobuf::FieldDescriptorProto& DescriptorProto::extension(int index) const { + return extension_.Get(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::mutable_extension(int index) { + return extension_.Mutable(index); +} +inline ::google::protobuf::FieldDescriptorProto* DescriptorProto::add_extension() { + return extension_.Add(); +} + +// repeated .google.protobuf.DescriptorProto nested_type = 3; +inline int DescriptorProto::nested_type_size() const { + return nested_type_.size(); +} +inline void DescriptorProto::clear_nested_type() { + nested_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >& +DescriptorProto::nested_type() const { + return nested_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto >* +DescriptorProto::mutable_nested_type() { + return &nested_type_; +} +inline const ::google::protobuf::DescriptorProto& DescriptorProto::nested_type(int index) const { + return nested_type_.Get(index); +} +inline ::google::protobuf::DescriptorProto* DescriptorProto::mutable_nested_type(int index) { + return nested_type_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto* DescriptorProto::add_nested_type() { + return nested_type_.Add(); +} + +// repeated .google.protobuf.EnumDescriptorProto enum_type = 4; +inline int DescriptorProto::enum_type_size() const { + return enum_type_.size(); +} +inline void DescriptorProto::clear_enum_type() { + enum_type_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >& +DescriptorProto::enum_type() const { + return enum_type_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumDescriptorProto >* +DescriptorProto::mutable_enum_type() { + return &enum_type_; +} +inline const ::google::protobuf::EnumDescriptorProto& DescriptorProto::enum_type(int index) const { + return enum_type_.Get(index); +} +inline ::google::protobuf::EnumDescriptorProto* DescriptorProto::mutable_enum_type(int index) { + return enum_type_.Mutable(index); +} +inline ::google::protobuf::EnumDescriptorProto* DescriptorProto::add_enum_type() { + return enum_type_.Add(); +} + +// repeated .google.protobuf.DescriptorProto.ExtensionRange extension_range = 5; +inline int DescriptorProto::extension_range_size() const { + return extension_range_.size(); +} +inline void DescriptorProto::clear_extension_range() { + extension_range_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >& +DescriptorProto::extension_range() const { + return extension_range_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::DescriptorProto_ExtensionRange >* +DescriptorProto::mutable_extension_range() { + return &extension_range_; +} +inline const ::google::protobuf::DescriptorProto_ExtensionRange& DescriptorProto::extension_range(int index) const { + return extension_range_.Get(index); +} +inline ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::mutable_extension_range(int index) { + return extension_range_.Mutable(index); +} +inline ::google::protobuf::DescriptorProto_ExtensionRange* DescriptorProto::add_extension_range() { + return extension_range_.Add(); +} + +// optional .google.protobuf.MessageOptions options = 7; +inline bool DescriptorProto::has_options() const { + return _has_bit(6); +} +inline void DescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::MessageOptions::Clear(); + _clear_bit(6); +} +inline const ::google::protobuf::MessageOptions& DescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::MessageOptions* DescriptorProto::mutable_options() { + _set_bit(6); + if (options_ == NULL) options_ = new ::google::protobuf::MessageOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// FieldDescriptorProto + +// optional string name = 1; +inline bool FieldDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void FieldDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FieldDescriptorProto::name() const { + return *name_; +} +inline void FieldDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void FieldDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional int32 number = 3; +inline bool FieldDescriptorProto::has_number() const { + return _has_bit(1); +} +inline void FieldDescriptorProto::clear_number() { + number_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 FieldDescriptorProto::number() const { + return number_; +} +inline void FieldDescriptorProto::set_number(::google::protobuf::int32 value) { + _set_bit(1); + number_ = value; +} + +// optional .google.protobuf.FieldDescriptorProto.Label label = 4; +inline bool FieldDescriptorProto::has_label() const { + return _has_bit(2); +} +inline void FieldDescriptorProto::clear_label() { + label_ = 1; + _clear_bit(2); +} +inline ::google::protobuf::FieldDescriptorProto_Label FieldDescriptorProto::label() const { + return static_cast< ::google::protobuf::FieldDescriptorProto_Label >(label_); +} +inline void FieldDescriptorProto::set_label(::google::protobuf::FieldDescriptorProto_Label value) { + GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Label_IsValid(value)); + _set_bit(2); + label_ = value; +} + +// optional .google.protobuf.FieldDescriptorProto.Type type = 5; +inline bool FieldDescriptorProto::has_type() const { + return _has_bit(3); +} +inline void FieldDescriptorProto::clear_type() { + type_ = 1; + _clear_bit(3); +} +inline ::google::protobuf::FieldDescriptorProto_Type FieldDescriptorProto::type() const { + return static_cast< ::google::protobuf::FieldDescriptorProto_Type >(type_); +} +inline void FieldDescriptorProto::set_type(::google::protobuf::FieldDescriptorProto_Type value) { + GOOGLE_DCHECK(::google::protobuf::FieldDescriptorProto_Type_IsValid(value)); + _set_bit(3); + type_ = value; +} + +// optional string type_name = 6; +inline bool FieldDescriptorProto::has_type_name() const { + return _has_bit(4); +} +inline void FieldDescriptorProto::clear_type_name() { + if (type_name_ != &_default_type_name_) { + type_name_->clear(); + } + _clear_bit(4); +} +inline const ::std::string& FieldDescriptorProto::type_name() const { + return *type_name_; +} +inline void FieldDescriptorProto::set_type_name(const ::std::string& value) { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + type_name_->assign(value); +} +inline void FieldDescriptorProto::set_type_name(const char* value) { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + type_name_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_type_name() { + _set_bit(4); + if (type_name_ == &_default_type_name_) { + type_name_ = new ::std::string; + } + return type_name_; +} + +// optional string extendee = 2; +inline bool FieldDescriptorProto::has_extendee() const { + return _has_bit(5); +} +inline void FieldDescriptorProto::clear_extendee() { + if (extendee_ != &_default_extendee_) { + extendee_->clear(); + } + _clear_bit(5); +} +inline const ::std::string& FieldDescriptorProto::extendee() const { + return *extendee_; +} +inline void FieldDescriptorProto::set_extendee(const ::std::string& value) { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + extendee_->assign(value); +} +inline void FieldDescriptorProto::set_extendee(const char* value) { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + extendee_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_extendee() { + _set_bit(5); + if (extendee_ == &_default_extendee_) { + extendee_ = new ::std::string; + } + return extendee_; +} + +// optional string default_value = 7; +inline bool FieldDescriptorProto::has_default_value() const { + return _has_bit(6); +} +inline void FieldDescriptorProto::clear_default_value() { + if (default_value_ != &_default_default_value_) { + default_value_->clear(); + } + _clear_bit(6); +} +inline const ::std::string& FieldDescriptorProto::default_value() const { + return *default_value_; +} +inline void FieldDescriptorProto::set_default_value(const ::std::string& value) { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + default_value_->assign(value); +} +inline void FieldDescriptorProto::set_default_value(const char* value) { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + default_value_->assign(value); +} +inline ::std::string* FieldDescriptorProto::mutable_default_value() { + _set_bit(6); + if (default_value_ == &_default_default_value_) { + default_value_ = new ::std::string; + } + return default_value_; +} + +// optional .google.protobuf.FieldOptions options = 8; +inline bool FieldDescriptorProto::has_options() const { + return _has_bit(7); +} +inline void FieldDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::FieldOptions::Clear(); + _clear_bit(7); +} +inline const ::google::protobuf::FieldOptions& FieldDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::FieldOptions* FieldDescriptorProto::mutable_options() { + _set_bit(7); + if (options_ == NULL) options_ = new ::google::protobuf::FieldOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// EnumDescriptorProto + +// optional string name = 1; +inline bool EnumDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void EnumDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& EnumDescriptorProto::name() const { + return *name_; +} +inline void EnumDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void EnumDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* EnumDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.EnumValueDescriptorProto value = 2; +inline int EnumDescriptorProto::value_size() const { + return value_.size(); +} +inline void EnumDescriptorProto::clear_value() { + value_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >& +EnumDescriptorProto::value() const { + return value_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::EnumValueDescriptorProto >* +EnumDescriptorProto::mutable_value() { + return &value_; +} +inline const ::google::protobuf::EnumValueDescriptorProto& EnumDescriptorProto::value(int index) const { + return value_.Get(index); +} +inline ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::mutable_value(int index) { + return value_.Mutable(index); +} +inline ::google::protobuf::EnumValueDescriptorProto* EnumDescriptorProto::add_value() { + return value_.Add(); +} + +// optional .google.protobuf.EnumOptions options = 3; +inline bool EnumDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void EnumDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::EnumOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::EnumOptions& EnumDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::EnumOptions* EnumDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::EnumOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// EnumValueDescriptorProto + +// optional string name = 1; +inline bool EnumValueDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void EnumValueDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& EnumValueDescriptorProto::name() const { + return *name_; +} +inline void EnumValueDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void EnumValueDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* EnumValueDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional int32 number = 2; +inline bool EnumValueDescriptorProto::has_number() const { + return _has_bit(1); +} +inline void EnumValueDescriptorProto::clear_number() { + number_ = 0; + _clear_bit(1); +} +inline ::google::protobuf::int32 EnumValueDescriptorProto::number() const { + return number_; +} +inline void EnumValueDescriptorProto::set_number(::google::protobuf::int32 value) { + _set_bit(1); + number_ = value; +} + +// optional .google.protobuf.EnumValueOptions options = 3; +inline bool EnumValueDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void EnumValueDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::EnumValueOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::EnumValueOptions& EnumValueDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::EnumValueOptions* EnumValueDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::EnumValueOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// ServiceDescriptorProto + +// optional string name = 1; +inline bool ServiceDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void ServiceDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& ServiceDescriptorProto::name() const { + return *name_; +} +inline void ServiceDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void ServiceDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* ServiceDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// repeated .google.protobuf.MethodDescriptorProto method = 2; +inline int ServiceDescriptorProto::method_size() const { + return method_.size(); +} +inline void ServiceDescriptorProto::clear_method() { + method_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >& +ServiceDescriptorProto::method() const { + return method_; +} +inline ::google::protobuf::RepeatedPtrField< ::google::protobuf::MethodDescriptorProto >* +ServiceDescriptorProto::mutable_method() { + return &method_; +} +inline const ::google::protobuf::MethodDescriptorProto& ServiceDescriptorProto::method(int index) const { + return method_.Get(index); +} +inline ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::mutable_method(int index) { + return method_.Mutable(index); +} +inline ::google::protobuf::MethodDescriptorProto* ServiceDescriptorProto::add_method() { + return method_.Add(); +} + +// optional .google.protobuf.ServiceOptions options = 3; +inline bool ServiceDescriptorProto::has_options() const { + return _has_bit(2); +} +inline void ServiceDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::ServiceOptions::Clear(); + _clear_bit(2); +} +inline const ::google::protobuf::ServiceOptions& ServiceDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::ServiceOptions* ServiceDescriptorProto::mutable_options() { + _set_bit(2); + if (options_ == NULL) options_ = new ::google::protobuf::ServiceOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// MethodDescriptorProto + +// optional string name = 1; +inline bool MethodDescriptorProto::has_name() const { + return _has_bit(0); +} +inline void MethodDescriptorProto::clear_name() { + if (name_ != &_default_name_) { + name_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& MethodDescriptorProto::name() const { + return *name_; +} +inline void MethodDescriptorProto::set_name(const ::std::string& value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline void MethodDescriptorProto::set_name(const char* value) { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + name_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_name() { + _set_bit(0); + if (name_ == &_default_name_) { + name_ = new ::std::string; + } + return name_; +} + +// optional string input_type = 2; +inline bool MethodDescriptorProto::has_input_type() const { + return _has_bit(1); +} +inline void MethodDescriptorProto::clear_input_type() { + if (input_type_ != &_default_input_type_) { + input_type_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& MethodDescriptorProto::input_type() const { + return *input_type_; +} +inline void MethodDescriptorProto::set_input_type(const ::std::string& value) { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + input_type_->assign(value); +} +inline void MethodDescriptorProto::set_input_type(const char* value) { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + input_type_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_input_type() { + _set_bit(1); + if (input_type_ == &_default_input_type_) { + input_type_ = new ::std::string; + } + return input_type_; +} + +// optional string output_type = 3; +inline bool MethodDescriptorProto::has_output_type() const { + return _has_bit(2); +} +inline void MethodDescriptorProto::clear_output_type() { + if (output_type_ != &_default_output_type_) { + output_type_->clear(); + } + _clear_bit(2); +} +inline const ::std::string& MethodDescriptorProto::output_type() const { + return *output_type_; +} +inline void MethodDescriptorProto::set_output_type(const ::std::string& value) { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + output_type_->assign(value); +} +inline void MethodDescriptorProto::set_output_type(const char* value) { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + output_type_->assign(value); +} +inline ::std::string* MethodDescriptorProto::mutable_output_type() { + _set_bit(2); + if (output_type_ == &_default_output_type_) { + output_type_ = new ::std::string; + } + return output_type_; +} + +// optional .google.protobuf.MethodOptions options = 4; +inline bool MethodDescriptorProto::has_options() const { + return _has_bit(3); +} +inline void MethodDescriptorProto::clear_options() { + if (options_ != NULL) options_->::google::protobuf::MethodOptions::Clear(); + _clear_bit(3); +} +inline const ::google::protobuf::MethodOptions& MethodDescriptorProto::options() const { + return options_ != NULL ? *options_ : *default_instance_.options_; +} +inline ::google::protobuf::MethodOptions* MethodDescriptorProto::mutable_options() { + _set_bit(3); + if (options_ == NULL) options_ = new ::google::protobuf::MethodOptions; + return options_; +} + +// ------------------------------------------------------------------- + +// FileOptions + +// optional string java_package = 1; +inline bool FileOptions::has_java_package() const { + return _has_bit(0); +} +inline void FileOptions::clear_java_package() { + if (java_package_ != &_default_java_package_) { + java_package_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& FileOptions::java_package() const { + return *java_package_; +} +inline void FileOptions::set_java_package(const ::std::string& value) { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + java_package_->assign(value); +} +inline void FileOptions::set_java_package(const char* value) { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + java_package_->assign(value); +} +inline ::std::string* FileOptions::mutable_java_package() { + _set_bit(0); + if (java_package_ == &_default_java_package_) { + java_package_ = new ::std::string; + } + return java_package_; +} + +// optional string java_outer_classname = 8; +inline bool FileOptions::has_java_outer_classname() const { + return _has_bit(1); +} +inline void FileOptions::clear_java_outer_classname() { + if (java_outer_classname_ != &_default_java_outer_classname_) { + java_outer_classname_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FileOptions::java_outer_classname() const { + return *java_outer_classname_; +} +inline void FileOptions::set_java_outer_classname(const ::std::string& value) { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + java_outer_classname_->assign(value); +} +inline void FileOptions::set_java_outer_classname(const char* value) { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + java_outer_classname_->assign(value); +} +inline ::std::string* FileOptions::mutable_java_outer_classname() { + _set_bit(1); + if (java_outer_classname_ == &_default_java_outer_classname_) { + java_outer_classname_ = new ::std::string; + } + return java_outer_classname_; +} + +// optional bool java_multiple_files = 10 [default = false]; +inline bool FileOptions::has_java_multiple_files() const { + return _has_bit(2); +} +inline void FileOptions::clear_java_multiple_files() { + java_multiple_files_ = false; + _clear_bit(2); +} +inline bool FileOptions::java_multiple_files() const { + return java_multiple_files_; +} +inline void FileOptions::set_java_multiple_files(bool value) { + _set_bit(2); + java_multiple_files_ = value; +} + +// optional .google.protobuf.FileOptions.OptimizeMode optimize_for = 9 [default = CODE_SIZE]; +inline bool FileOptions::has_optimize_for() const { + return _has_bit(3); +} +inline void FileOptions::clear_optimize_for() { + optimize_for_ = 2; + _clear_bit(3); +} +inline ::google::protobuf::FileOptions_OptimizeMode FileOptions::optimize_for() const { + return static_cast< ::google::protobuf::FileOptions_OptimizeMode >(optimize_for_); +} +inline void FileOptions::set_optimize_for(::google::protobuf::FileOptions_OptimizeMode value) { + GOOGLE_DCHECK(::google::protobuf::FileOptions_OptimizeMode_IsValid(value)); + _set_bit(3); + optimize_for_ = value; +} + +// ------------------------------------------------------------------- + +// MessageOptions + +// optional bool message_set_wire_format = 1 [default = false]; +inline bool MessageOptions::has_message_set_wire_format() const { + return _has_bit(0); +} +inline void MessageOptions::clear_message_set_wire_format() { + message_set_wire_format_ = false; + _clear_bit(0); +} +inline bool MessageOptions::message_set_wire_format() const { + return message_set_wire_format_; +} +inline void MessageOptions::set_message_set_wire_format(bool value) { + _set_bit(0); + message_set_wire_format_ = value; +} + +// ------------------------------------------------------------------- + +// FieldOptions + +// optional .google.protobuf.FieldOptions.CType ctype = 1; +inline bool FieldOptions::has_ctype() const { + return _has_bit(0); +} +inline void FieldOptions::clear_ctype() { + ctype_ = 1; + _clear_bit(0); +} +inline ::google::protobuf::FieldOptions_CType FieldOptions::ctype() const { + return static_cast< ::google::protobuf::FieldOptions_CType >(ctype_); +} +inline void FieldOptions::set_ctype(::google::protobuf::FieldOptions_CType value) { + GOOGLE_DCHECK(::google::protobuf::FieldOptions_CType_IsValid(value)); + _set_bit(0); + ctype_ = value; +} + +// optional string experimental_map_key = 9; +inline bool FieldOptions::has_experimental_map_key() const { + return _has_bit(1); +} +inline void FieldOptions::clear_experimental_map_key() { + if (experimental_map_key_ != &_default_experimental_map_key_) { + experimental_map_key_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& FieldOptions::experimental_map_key() const { + return *experimental_map_key_; +} +inline void FieldOptions::set_experimental_map_key(const ::std::string& value) { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + experimental_map_key_->assign(value); +} +inline void FieldOptions::set_experimental_map_key(const char* value) { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + experimental_map_key_->assign(value); +} +inline ::std::string* FieldOptions::mutable_experimental_map_key() { + _set_bit(1); + if (experimental_map_key_ == &_default_experimental_map_key_) { + experimental_map_key_ = new ::std::string; + } + return experimental_map_key_; +} + +// ------------------------------------------------------------------- + +// EnumOptions + +// ------------------------------------------------------------------- + +// EnumValueOptions + +// ------------------------------------------------------------------- + +// ServiceOptions + +// ------------------------------------------------------------------- + +// MethodOptions + + +} // namespace protobuf +} // namespace google +#endif // PROTOBUF_google_2fprotobuf_2fdescriptor_2eproto__INCLUDED diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto new file mode 100644 index 00000000..42b4430d --- /dev/null +++ b/src/google/protobuf/descriptor.proto @@ -0,0 +1,301 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + + +package google.protobuf; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; + +option csharp_namespace = "Google.ProtocolBuffers"; +option csharp_outer_classname = "DescriptorProtos"; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + } + repeated ExtensionRange extension_range = 5; + + optional MessageOptions options = 7; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + TYPE_INT64 = 3; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT64 if negative + // values are likely. + TYPE_UINT64 = 4; + TYPE_INT32 = 5; // Not ZigZag encoded. Negative numbers + // take 10 bytes. Use TYPE_SINT32 if negative + // values are likely. + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + TYPE_GROUP = 10; // Tag-delimited aggregate. + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + // TODO(sanjay): Should we add LABEL_MAP? + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be either TYPE_ENUM or TYPE_MESSAGE. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + optional FieldOptions options = 8; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; +} + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. + +// TODO(kenton): Allow extensions to options. + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + } + optional OptimizeMode optimize_for = 9 [default=CODE_SIZE]; + + + // C# stuff... remove eventually? + optional string csharp_namespace = 1000; + optional string csharp_outer_classname = 1001; + optional bool csharp_multiple_files = 1002; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1; + enum CType { + CORD = 1; + + STRING_PIECE = 2; + } + + // EXPERIMENTAL. DO NOT USE. + // For "map" fields, the name of the field in the enclosed type that + // is the key for this map. For example, suppose we have: + // message Item { + // required string name = 1; + // required string value = 2; + // } + // message Config { + // repeated Item items = 1 [experimental_map_key="name"]; + // } + // In this situation, the map key for Item will be set to "name". + // TODO: Fully-implement this, then remove the "experimental_" prefix. + optional string experimental_map_key = 9; +} + +message EnumOptions { +} + +message EnumValueOptions { +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. +} diff --git a/src/google/protobuf/descriptor_database.cc b/src/google/protobuf/descriptor_database.cc new file mode 100644 index 00000000..944280c0 --- /dev/null +++ b/src/google/protobuf/descriptor_database.cc @@ -0,0 +1,291 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +DescriptorDatabase::~DescriptorDatabase() {} + +// =================================================================== + +SimpleDescriptorDatabase::SimpleDescriptorDatabase() {} +SimpleDescriptorDatabase::~SimpleDescriptorDatabase() { + STLDeleteElements(&files_to_delete_); +} + +void SimpleDescriptorDatabase::Add(const FileDescriptorProto& file) { + FileDescriptorProto* new_file = new FileDescriptorProto; + new_file->CopyFrom(file); + AddAndOwn(new_file); +} + +void SimpleDescriptorDatabase::AddAndOwn(const FileDescriptorProto* file) { + files_to_delete_.push_back(file); + InsertOrUpdate(&files_by_name_, file->name(), file); + + string path = file->package(); + if (!path.empty()) path += '.'; + + for (int i = 0; i < file->message_type_size(); i++) { + AddMessage(path, file->message_type(i), file); + } + for (int i = 0; i < file->enum_type_size(); i++) { + AddEnum(path, file->enum_type(i), file); + } + for (int i = 0; i < file->extension_size(); i++) { + AddField(path, file->extension(i), file); + } + for (int i = 0; i < file->service_size(); i++) { + AddService(path, file->service(i), file); + } +} + +void SimpleDescriptorDatabase::AddMessage( + const string& path, + const DescriptorProto& message_type, + const FileDescriptorProto* file) { + string full_name = path + message_type.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < message_type.nested_type_size(); i++) { + AddMessage(sub_path, message_type.nested_type(i), file); + } + for (int i = 0; i < message_type.enum_type_size(); i++) { + AddEnum(sub_path, message_type.enum_type(i), file); + } + for (int i = 0; i < message_type.field_size(); i++) { + AddField(sub_path, message_type.field(i), file); + } + for (int i = 0; i < message_type.extension_size(); i++) { + AddField(sub_path, message_type.extension(i), file); + } +} + +void SimpleDescriptorDatabase::AddField( + const string& path, + const FieldDescriptorProto& field, + const FileDescriptorProto* file) { + string full_name = path + field.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + if (field.has_extendee()) { + // This field is an extension. + if (!field.extendee().empty() && field.extendee()[0] == '.') { + // The extension is fully-qualified. We can use it as a lookup key in + // the files_by_symbol_ table. + InsertOrUpdate(&files_by_extension_, + make_pair(field.extendee().substr(1), field.number()), + file); + } else { + // Not fully-qualified. We can't really do anything here, unfortunately. + } + } +} + +void SimpleDescriptorDatabase::AddEnum( + const string& path, + const EnumDescriptorProto& enum_type, + const FileDescriptorProto* file) { + string full_name = path + enum_type.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < enum_type.value_size(); i++) { + InsertOrUpdate(&files_by_symbol_, + sub_path + enum_type.value(i).name(), + file); + } +} + +void SimpleDescriptorDatabase::AddService( + const string& path, + const ServiceDescriptorProto& service, + const FileDescriptorProto* file) { + string full_name = path + service.name(); + InsertOrUpdate(&files_by_symbol_, full_name, file); + + string sub_path = full_name + '.'; + for (int i = 0; i < service.method_size(); i++) { + InsertOrUpdate(&files_by_symbol_, + sub_path + service.method(i).name(), + file); + } +} + +bool SimpleDescriptorDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + const FileDescriptorProto* result = FindPtrOrNull(files_by_name_, filename); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +bool SimpleDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + const FileDescriptorProto* result = + FindPtrOrNull(files_by_symbol_, symbol_name); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +bool SimpleDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + const FileDescriptorProto* result = + FindPtrOrNull(files_by_extension_, + make_pair(containing_type, field_number)); + if (result == NULL) { + return false; + } else { + output->CopyFrom(*result); + return true; + } +} + +// =================================================================== + +DescriptorPoolDatabase::DescriptorPoolDatabase(const DescriptorPool& pool) + : pool_(pool) {} +DescriptorPoolDatabase::~DescriptorPoolDatabase() {} + +bool DescriptorPoolDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + const FileDescriptor* file = pool_.FindFileByName(filename); + if (file == NULL) return false; + output->Clear(); + file->CopyTo(output); + return true; +} + +bool DescriptorPoolDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + const FileDescriptor* file = pool_.FindFileContainingSymbol(symbol_name); + if (file == NULL) return false; + output->Clear(); + file->CopyTo(output); + return true; +} + +bool DescriptorPoolDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + const Descriptor* extendee = pool_.FindMessageTypeByName(containing_type); + if (extendee == NULL) return false; + + const FieldDescriptor* extension = + pool_.FindExtensionByNumber(extendee, field_number); + if (extension == NULL) return false; + + output->Clear(); + extension->file()->CopyTo(output); + return true; +} + +// =================================================================== + +MergedDescriptorDatabase::MergedDescriptorDatabase( + DescriptorDatabase* source1, + DescriptorDatabase* source2) { + sources_.push_back(source1); + sources_.push_back(source2); +} +MergedDescriptorDatabase::MergedDescriptorDatabase( + const vector& sources) + : sources_(sources) {} +MergedDescriptorDatabase::~MergedDescriptorDatabase() {} + +bool MergedDescriptorDatabase::FindFileByName( + const string& filename, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileByName(filename, output)) { + return true; + } + } + return false; +} + +bool MergedDescriptorDatabase::FindFileContainingSymbol( + const string& symbol_name, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileContainingSymbol(symbol_name, output)) { + // The symbol was found in source i. However, if one of the previous + // sources defines a file with the same name (which presumably doesn't + // contain the symbol, since it wasn't found in that source), then we + // must hide it from the caller. + FileDescriptorProto temp; + for (int j = 0; j < i; j++) { + if (sources_[j]->FindFileByName(output->name(), &temp)) { + // Found conflicting file in a previous source. + return false; + } + } + return true; + } + } + return false; +} + +bool MergedDescriptorDatabase::FindFileContainingExtension( + const string& containing_type, + int field_number, + FileDescriptorProto* output) { + for (int i = 0; i < sources_.size(); i++) { + if (sources_[i]->FindFileContainingExtension( + containing_type, field_number, output)) { + // The symbol was found in source i. However, if one of the previous + // sources defines a file with the same name (which presumably doesn't + // contain the symbol, since it wasn't found in that source), then we + // must hide it from the caller. + FileDescriptorProto temp; + for (int j = 0; j < i; j++) { + if (sources_[j]->FindFileByName(output->name(), &temp)) { + // Found conflicting file in a previous source. + return false; + } + } + return true; + } + } + return false; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor_database.h b/src/google/protobuf/descriptor_database.h new file mode 100644 index 00000000..d629da4c --- /dev/null +++ b/src/google/protobuf/descriptor_database.h @@ -0,0 +1,183 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Interface for manipulating databases of descriptors. + +#ifndef GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ +#define GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +// Abstract interface for a database of descriptors. +// +// This is useful if you want to create a DescriptorPool which loads +// descriptors on-demand from some sort of large database. If the database +// is large, it may be inefficient to enumerate every .proto file inside it +// calling DescriptorPool::BuildFile() for each one. Instead, a DescriptorPool +// can be created which wraps a DescriptorDatabase and only builds particular +// descriptors when they are needed. +class LIBPROTOBUF_EXPORT DescriptorDatabase { + public: + inline DescriptorDatabase() {} + virtual ~DescriptorDatabase(); + + // Find a file by file name. Fills in in *output and returns true if found. + // Otherwise, returns false, leaving the contents of *output undefined. + virtual bool FindFileByName(const string& filename, + FileDescriptorProto* output) = 0; + + // Find the file that declares the given fully-qualified symbol name. + // If found, fills in *output and returns true, otherwise returns false + // and leaves *output undefined. + virtual bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) = 0; + + // Find the file which defines an extension extending the given message type + // with the given field number. If found, fills in *output and returns true, + // otherwise returns false and leaves *output undefined. containing_type + // must be a fully-qualified type name. + virtual bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase); +}; + +// A DescriptorDatabase into which you can insert files manually. +// +// FindFileContainingSymbol() is fully-implemented. When you add a file, its +// symbols will be indexed for this purpose. +// +// FindFileContainingExtension() is mostly-implemented. It works if and only +// if the original FieldDescriptorProto defining the extension has a +// fully-qualified type name in its "extendee" field (i.e. starts with a '.'). +// If the extendee is a relative name, SimpleDescriptorDatabase will not +// attempt to resolve the type, so it will not know what type the extension is +// extending. Therefore, calling FindFileContainingExtension() with the +// extension's containing type will never actually find that extension. Note +// that this is an unlikely problem, as all FileDescriptorProtos created by the +// protocol compiler (as well as ones created by calling +// FileDescriptor::CopyTo()) will always use fully-qualified names for all +// types. You only need to worry if you are constructing FileDescriptorProtos +// yourself, or are calling compiler::Parser directly. +class LIBPROTOBUF_EXPORT SimpleDescriptorDatabase : public DescriptorDatabase { + public: + SimpleDescriptorDatabase(); + ~SimpleDescriptorDatabase(); + + // Adds the FileDescriptorProto to the database, making a copy. The object + // can be deleted after Add() returns. + void Add(const FileDescriptorProto& file); + + // Adds the FileDescriptorProto to the database and takes ownership of it. + void AddAndOwn(const FileDescriptorProto* file); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + // Helpers to recursively add particular descriptors and all their contents + // to the by-symbol and by-extension tables. + void AddMessage(const string& path, + const DescriptorProto& message_type, + const FileDescriptorProto* file); + void AddField(const string& path, + const FieldDescriptorProto& field, + const FileDescriptorProto* file); + void AddEnum(const string& path, + const EnumDescriptorProto& enum_type, + const FileDescriptorProto* file); + void AddService(const string& path, + const ServiceDescriptorProto& service, + const FileDescriptorProto* file); + + vector files_to_delete_; + map files_by_name_; + map files_by_symbol_; + map, const FileDescriptorProto*> files_by_extension_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(SimpleDescriptorDatabase); +}; + +// A DescriptorDatabase that fetches files from a given pool. +class LIBPROTOBUF_EXPORT DescriptorPoolDatabase : public DescriptorDatabase { + public: + DescriptorPoolDatabase(const DescriptorPool& pool); + ~DescriptorPoolDatabase(); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + const DescriptorPool& pool_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPoolDatabase); +}; + +// A DescriptorDatabase that wraps two or more others. It first searches the +// first database and, if that fails, tries the second, and so on. +class LIBPROTOBUF_EXPORT MergedDescriptorDatabase : public DescriptorDatabase { + public: + // Merge just two databases. The sources remain property of the caller. + MergedDescriptorDatabase(DescriptorDatabase* source1, + DescriptorDatabase* source2); + // Merge more than two databases. The sources remain property of the caller. + // The vector may be deleted after the constructor returns but the + // DescriptorDatabases need to stick around. + MergedDescriptorDatabase(const vector& sources); + ~MergedDescriptorDatabase(); + + // implements DescriptorDatabase ----------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output); + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output); + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output); + + private: + vector sources_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MergedDescriptorDatabase); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DESCRIPTOR_DATABASE_H__ diff --git a/src/google/protobuf/descriptor_database_unittest.cc b/src/google/protobuf/descriptor_database_unittest.cc new file mode 100644 index 00000000..cbbf5927 --- /dev/null +++ b/src/google/protobuf/descriptor_database_unittest.cc @@ -0,0 +1,601 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file makes extensive use of RFC 3092. :) + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace { + +static bool AddToPool(DescriptorPool* pool, const char* file_text) { + FileDescriptorProto file_proto; + if (!TextFormat::ParseFromString(file_text, &file_proto)) return false; + if (pool->BuildFile(file_proto) == NULL) return false; + return true; +} + +static void AddToDatabase(SimpleDescriptorDatabase* database, + const char* file_text) { + FileDescriptorProto file_proto; + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + database->Add(file_proto); +} + +static void ExpectContainsType(const FileDescriptorProto& proto, + const string& type_name) { + for (int i = 0; i < proto.message_type_size(); i++) { + if (proto.message_type(i).name() == type_name) return; + } + ADD_FAILURE() << "\"" << proto.name() + << "\" did not contain expected type \"" + << type_name << "\"."; +} + +// =================================================================== + +TEST(SimpleDescriptorDatabaseTest, FindFileByName) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" }"); + AddToDatabase(&database, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" }"); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // Fails to find undefined files. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileByName("baz.proto", &file)); + } +} + +TEST(SimpleDescriptorDatabaseTest, FindFileContainingSymbol) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " field { name:\"qux\" }" + " nested_type { name: \"Grault\" } " + " enum_type { name: \"Garply\" } " + "} " + "enum_type { " + " name: \"Waldo\" " + " value { name:\"FRED\" } " + "} " + "extension { name: \"plugh\" } " + "service { " + " name: \"Xyzzy\" " + " method { name: \"Thud\" } " + "}" + ); + AddToDatabase(&database, + "name: \"bar.proto\" " + "package: \"corge\" " + "message_type { name: \"Bar\" }"); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find fields. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.qux", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find nested types. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.Grault", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find nested enums. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.Garply", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find enum types. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Waldo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find enum values. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Waldo.FRED", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find extensions. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("plugh", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find services. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Xyzzy", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find methods. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Xyzzy.Thud", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find things in packages. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("corge.Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Fails to find undefined symbols. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Baz", &file)); + } + + { + // Names must be fully-qualified. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Bar", &file)); + } +} + +TEST(SimpleDescriptorDatabaseTest, FindFileContainingExtension) { + SimpleDescriptorDatabase database; + AddToDatabase(&database, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " extension_range { start: 1 end: 1000 } " + " extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 " + " extendee: \".Foo\" }" + "}"); + AddToDatabase(&database, + "name: \"bar.proto\" " + "package: \"corge\" " + "dependency: \"foo.proto\" " + "message_type { " + " name: \"Bar\" " + " extension_range { start: 1 end: 1000 } " + "} " + "extension { name:\"grault\" extendee: \".Foo\" number:32 } " + "extension { name:\"garply\" extendee: \".corge.Bar\" number:70 } " + "extension { name:\"waldo\" extendee: \"Bar\" number:56 } "); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 5, &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 32, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can find extensions for qualified type names. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("corge.Bar", 70, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can't find extensions whose extendee was not fully-qualified in the + // FileDescriptorProto. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 56, &file)); + EXPECT_FALSE(database.FindFileContainingExtension("corge.Bar", 56, &file)); + } + + { + // Can't find non-existent extension numbers. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Foo", 12, &file)); + } + + { + // Can't find extensions for non-existent types. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("NoSuchType", 5, &file)); + } + + { + // Can't find extensions for unqualified type names. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 70, &file)); + } +} + +// =================================================================== + +TEST(DescriptorPoolDatabaseTest, FindFileByName) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" }")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" }")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // Fails to find undefined files. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileByName("baz.proto", &file)); + } +} + +TEST(DescriptorPoolDatabaseTest, FindFileContainingSymbol) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " field { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "}")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "package: \"corge\" " + "message_type { name: \"Bar\" }")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find fields. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("Foo.qux", &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + // Can find things in packages. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingSymbol("corge.Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Fails to find undefined symbols. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Baz", &file)); + } + + { + // Names must be fully-qualified. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingSymbol("Bar", &file)); + } +} + +TEST(DescriptorPoolDatabaseTest, FindFileContainingExtension) { + DescriptorPool pool; + ASSERT_TRUE(AddToPool(&pool, + "name: \"foo.proto\" " + "message_type { " + " name: \"Foo\" " + " extension_range { start: 1 end: 1000 } " + " extension { name:\"qux\" label:LABEL_OPTIONAL type:TYPE_INT32 number:5 " + " extendee: \"Foo\" }" + "}")); + ASSERT_TRUE(AddToPool(&pool, + "name: \"bar.proto\" " + "package: \"corge\" " + "dependency: \"foo.proto\" " + "message_type { " + " name: \"Bar\" " + " extension_range { start: 1 end: 1000 } " + "} " + "extension { name:\"grault\" label:LABEL_OPTIONAL type:TYPE_BOOL number:32 " + " extendee: \"Foo\" } " + "extension { name:\"garply\" label:LABEL_OPTIONAL type:TYPE_BOOL number:70 " + " extendee: \"Bar\" } ")); + + DescriptorPoolDatabase database(pool); + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 5, &file)); + EXPECT_EQ("foo.proto", file.name()); + } + + { + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("Foo", 32, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can find extensions for qualified type names.. + FileDescriptorProto file; + EXPECT_TRUE(database.FindFileContainingExtension("corge.Bar", 70, &file)); + EXPECT_EQ("bar.proto", file.name()); + } + + { + // Can't find non-existent extension numbers. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Foo", 12, &file)); + } + + { + // Can't find extensions for non-existent types. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("NoSuchType", 5, &file)); + } + + { + // Can't find extensions for unqualified type names. + FileDescriptorProto file; + EXPECT_FALSE(database.FindFileContainingExtension("Bar", 70, &file)); + } +} + +// =================================================================== + +class MergedDescriptorDatabaseTest : public testing::Test { + protected: + MergedDescriptorDatabaseTest() + : forward_merged_(&database1_, &database2_), + reverse_merged_(&database2_, &database1_) {} + + virtual void SetUp() { + AddToDatabase(&database1_, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } " + "extension { name:\"foo_ext\" extendee: \".Foo\" number:3 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + AddToDatabase(&database2_, + "name: \"bar.proto\" " + "message_type { name:\"Bar\" extension_range { start: 1 end: 100 } } " + "extension { name:\"bar_ext\" extendee: \".Bar\" number:5 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + + // baz.proto exists in both pools, with different definitions. + AddToDatabase(&database1_, + "name: \"baz.proto\" " + "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } " + "message_type { name:\"FromPool1\" } " + "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } " + "extension { name:\"database1_only_ext\" extendee: \".Baz\" number:13 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + AddToDatabase(&database2_, + "name: \"baz.proto\" " + "message_type { name:\"Baz\" extension_range { start: 1 end: 100 } } " + "message_type { name:\"FromPool2\" } " + "extension { name:\"baz_ext\" extendee: \".Baz\" number:12 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + } + + SimpleDescriptorDatabase database1_; + SimpleDescriptorDatabase database2_; + + MergedDescriptorDatabase forward_merged_; + MergedDescriptorDatabase reverse_merged_; +}; + +TEST_F(MergedDescriptorDatabaseTest, FindFileByName) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("foo.proto", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("bar.proto", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileByName("baz.proto", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(reverse_merged_.FindFileByName("baz.proto", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // Can't find non-existent file. + FileDescriptorProto file; + EXPECT_FALSE(forward_merged_.FindFileByName("no_such.proto", &file)); + } +} + +TEST_F(MergedDescriptorDatabaseTest, FindFileContainingSymbol) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Foo", &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Bar", &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("Baz", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE(reverse_merged_.FindFileContainingSymbol("Baz", &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // FromPool1 only shows up in forward_merged_ because it is masked by + // database2_'s baz.proto in reverse_merged_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingSymbol("FromPool1", &file)); + EXPECT_FALSE(reverse_merged_.FindFileContainingSymbol("FromPool1", &file)); + } + + { + // Can't find non-existent symbol. + FileDescriptorProto file; + EXPECT_FALSE( + forward_merged_.FindFileContainingSymbol("NoSuchType", &file)); + } +} + +TEST_F(MergedDescriptorDatabaseTest, FindFileContainingExtension) { + { + // Can find file that is only in database1_. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Foo", 3, &file)); + EXPECT_EQ("foo.proto", file.name()); + ExpectContainsType(file, "Foo"); + } + + { + // Can find file that is only in database2_. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Bar", 5, &file)); + EXPECT_EQ("bar.proto", file.name()); + ExpectContainsType(file, "Bar"); + } + + { + // In forward_merged_, database1_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE( + forward_merged_.FindFileContainingExtension("Baz", 12, &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool1"); + } + + { + // In reverse_merged_, database2_'s baz.proto takes precedence. + FileDescriptorProto file; + EXPECT_TRUE( + reverse_merged_.FindFileContainingExtension("Baz", 12, &file)); + EXPECT_EQ("baz.proto", file.name()); + ExpectContainsType(file, "FromPool2"); + } + + { + // Baz's extension 13 only shows up in forward_merged_ because it is + // masked by database2_'s baz.proto in reverse_merged_. + FileDescriptorProto file; + EXPECT_TRUE(forward_merged_.FindFileContainingExtension("Baz", 13, &file)); + EXPECT_FALSE(reverse_merged_.FindFileContainingExtension("Baz", 13, &file)); + } + + { + // Can't find non-existent extension. + FileDescriptorProto file; + EXPECT_FALSE( + forward_merged_.FindFileContainingExtension("Foo", 6, &file)); + } +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc new file mode 100644 index 00000000..25d6040d --- /dev/null +++ b/src/google/protobuf/descriptor_unittest.cc @@ -0,0 +1,2661 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file makes extensive use of RFC 3092. :) + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +// Some helpers to make assembling descriptors faster. +DescriptorProto* AddMessage(FileDescriptorProto* file, const string& name) { + DescriptorProto* result = file->add_message_type(); + result->set_name(name); + return result; +} + +DescriptorProto* AddNestedMessage(DescriptorProto* parent, const string& name) { + DescriptorProto* result = parent->add_nested_type(); + result->set_name(name); + return result; +} + +EnumDescriptorProto* AddEnum(FileDescriptorProto* file, const string& name) { + EnumDescriptorProto* result = file->add_enum_type(); + result->set_name(name); + return result; +} + +EnumDescriptorProto* AddNestedEnum(DescriptorProto* parent, + const string& name) { + EnumDescriptorProto* result = parent->add_enum_type(); + result->set_name(name); + return result; +} + +ServiceDescriptorProto* AddService(FileDescriptorProto* file, + const string& name) { + ServiceDescriptorProto* result = file->add_service(); + result->set_name(name); + return result; +} + +FieldDescriptorProto* AddField(DescriptorProto* parent, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = parent->add_field(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + return result; +} + +FieldDescriptorProto* AddExtension(FileDescriptorProto* file, + const string& extendee, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = file->add_extension(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + result->set_extendee(extendee); + return result; +} + +FieldDescriptorProto* AddNestedExtension(DescriptorProto* parent, + const string& extendee, + const string& name, int number, + FieldDescriptorProto::Label label, + FieldDescriptorProto::Type type) { + FieldDescriptorProto* result = parent->add_extension(); + result->set_name(name); + result->set_number(number); + result->set_label(label); + result->set_type(type); + result->set_extendee(extendee); + return result; +} + +DescriptorProto::ExtensionRange* AddExtensionRange(DescriptorProto* parent, + int start, int end) { + DescriptorProto::ExtensionRange* result = parent->add_extension_range(); + result->set_start(start); + result->set_end(end); + return result; +} + +EnumValueDescriptorProto* AddEnumValue(EnumDescriptorProto* enum_proto, + const string& name, int number) { + EnumValueDescriptorProto* result = enum_proto->add_value(); + result->set_name(name); + result->set_number(number); + return result; +} + +MethodDescriptorProto* AddMethod(ServiceDescriptorProto* service, + const string& name, + const string& input_type, + const string& output_type) { + MethodDescriptorProto* result = service->add_method(); + result->set_name(name); + result->set_input_type(input_type); + result->set_output_type(output_type); + return result; +} + +// Empty enums technically aren't allowed. We need to insert a dummy value +// into them. +void AddEmptyEnum(FileDescriptorProto* file, const string& name) { + AddEnumValue(AddEnum(file, name), name + "_DUMMY", 1); +} + +// =================================================================== + +// Test simple files. +class FileDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message FooMessage { extensions 1; } + // enum FooEnum {FOO_ENUM_VALUE = 1;} + // service FooService {} + // extend FooMessage { optional int32 foo_extension = 1; } + // + // // in "bar.proto" + // package bar_package; + // message BarMessage { extensions 1; } + // enum BarEnum {BAR_ENUM_VALUE = 1;} + // service BarService {} + // extend BarMessage { optional int32 bar_extension = 1; } + // + // Also, we have an empty file "baz.proto". This file's purpose is to + // make sure that even though it has the same package as foo.proto, + // searching it for members of foo.proto won't work. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + AddExtensionRange(AddMessage(&foo_file, "FooMessage"), 1, 2); + AddEnumValue(AddEnum(&foo_file, "FooEnum"), "FOO_ENUM_VALUE", 1); + AddService(&foo_file, "FooService"); + AddExtension(&foo_file, "FooMessage", "foo_extension", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("bar_package"); + bar_file.add_dependency("foo.proto"); + AddExtensionRange(AddMessage(&bar_file, "BarMessage"), 1, 2); + AddEnumValue(AddEnum(&bar_file, "BarEnum"), "BAR_ENUM_VALUE", 1); + AddService(&bar_file, "BarService"); + AddExtension(&bar_file, "bar_package.BarMessage", "bar_extension", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FileDescriptorProto baz_file; + baz_file.set_name("baz.proto"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + baz_file_ = pool_.BuildFile(baz_file); + ASSERT_TRUE(baz_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + foo_message_ = foo_file_->message_type(0); + ASSERT_EQ(1, foo_file_->enum_type_count()); + foo_enum_ = foo_file_->enum_type(0); + ASSERT_EQ(1, foo_enum_->value_count()); + foo_enum_value_ = foo_enum_->value(0); + ASSERT_EQ(1, foo_file_->service_count()); + foo_service_ = foo_file_->service(0); + ASSERT_EQ(1, foo_file_->extension_count()); + foo_extension_ = foo_file_->extension(0); + + ASSERT_EQ(1, bar_file_->message_type_count()); + bar_message_ = bar_file_->message_type(0); + ASSERT_EQ(1, bar_file_->enum_type_count()); + bar_enum_ = bar_file_->enum_type(0); + ASSERT_EQ(1, bar_enum_->value_count()); + bar_enum_value_ = bar_enum_->value(0); + ASSERT_EQ(1, bar_file_->service_count()); + bar_service_ = bar_file_->service(0); + ASSERT_EQ(1, bar_file_->extension_count()); + bar_extension_ = bar_file_->extension(0); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + const FileDescriptor* baz_file_; + + const Descriptor* foo_message_; + const EnumDescriptor* foo_enum_; + const EnumValueDescriptor* foo_enum_value_; + const ServiceDescriptor* foo_service_; + const FieldDescriptor* foo_extension_; + + const Descriptor* bar_message_; + const EnumDescriptor* bar_enum_; + const EnumValueDescriptor* bar_enum_value_; + const ServiceDescriptor* bar_service_; + const FieldDescriptor* bar_extension_; +}; + +TEST_F(FileDescriptorTest, Name) { + EXPECT_EQ("foo.proto", foo_file_->name()); + EXPECT_EQ("bar.proto", bar_file_->name()); + EXPECT_EQ("baz.proto", baz_file_->name()); +} + +TEST_F(FileDescriptorTest, Package) { + EXPECT_EQ("", foo_file_->package()); + EXPECT_EQ("bar_package", bar_file_->package()); +} + +TEST_F(FileDescriptorTest, Dependencies) { + EXPECT_EQ(0, foo_file_->dependency_count()); + EXPECT_EQ(1, bar_file_->dependency_count()); + EXPECT_EQ(foo_file_, bar_file_->dependency(0)); +} + +TEST_F(FileDescriptorTest, FindMessageTypeByName) { + EXPECT_EQ(foo_message_, foo_file_->FindMessageTypeByName("FooMessage")); + EXPECT_EQ(bar_message_, bar_file_->FindMessageTypeByName("BarMessage")); + + EXPECT_TRUE(foo_file_->FindMessageTypeByName("BarMessage") == NULL); + EXPECT_TRUE(bar_file_->FindMessageTypeByName("FooMessage") == NULL); + EXPECT_TRUE(baz_file_->FindMessageTypeByName("FooMessage") == NULL); + + EXPECT_TRUE(foo_file_->FindMessageTypeByName("NoSuchMessage") == NULL); + EXPECT_TRUE(foo_file_->FindMessageTypeByName("FooEnum") == NULL); +} + +TEST_F(FileDescriptorTest, FindEnumTypeByName) { + EXPECT_EQ(foo_enum_, foo_file_->FindEnumTypeByName("FooEnum")); + EXPECT_EQ(bar_enum_, bar_file_->FindEnumTypeByName("BarEnum")); + + EXPECT_TRUE(foo_file_->FindEnumTypeByName("BarEnum") == NULL); + EXPECT_TRUE(bar_file_->FindEnumTypeByName("FooEnum") == NULL); + EXPECT_TRUE(baz_file_->FindEnumTypeByName("FooEnum") == NULL); + + EXPECT_TRUE(foo_file_->FindEnumTypeByName("NoSuchEnum") == NULL); + EXPECT_TRUE(foo_file_->FindEnumTypeByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindEnumValueByName) { + EXPECT_EQ(foo_enum_value_, foo_file_->FindEnumValueByName("FOO_ENUM_VALUE")); + EXPECT_EQ(bar_enum_value_, bar_file_->FindEnumValueByName("BAR_ENUM_VALUE")); + + EXPECT_TRUE(foo_file_->FindEnumValueByName("BAR_ENUM_VALUE") == NULL); + EXPECT_TRUE(bar_file_->FindEnumValueByName("FOO_ENUM_VALUE") == NULL); + EXPECT_TRUE(baz_file_->FindEnumValueByName("FOO_ENUM_VALUE") == NULL); + + EXPECT_TRUE(foo_file_->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(foo_file_->FindEnumValueByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindServiceByName) { + EXPECT_EQ(foo_service_, foo_file_->FindServiceByName("FooService")); + EXPECT_EQ(bar_service_, bar_file_->FindServiceByName("BarService")); + + EXPECT_TRUE(foo_file_->FindServiceByName("BarService") == NULL); + EXPECT_TRUE(bar_file_->FindServiceByName("FooService") == NULL); + EXPECT_TRUE(baz_file_->FindServiceByName("FooService") == NULL); + + EXPECT_TRUE(foo_file_->FindServiceByName("NoSuchService") == NULL); + EXPECT_TRUE(foo_file_->FindServiceByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindExtensionByName) { + EXPECT_EQ(foo_extension_, foo_file_->FindExtensionByName("foo_extension")); + EXPECT_EQ(bar_extension_, bar_file_->FindExtensionByName("bar_extension")); + + EXPECT_TRUE(foo_file_->FindExtensionByName("bar_extension") == NULL); + EXPECT_TRUE(bar_file_->FindExtensionByName("foo_extension") == NULL); + EXPECT_TRUE(baz_file_->FindExtensionByName("foo_extension") == NULL); + + EXPECT_TRUE(foo_file_->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo_file_->FindExtensionByName("FooMessage") == NULL); +} + +TEST_F(FileDescriptorTest, FindExtensionByNumber) { + EXPECT_EQ(foo_extension_, pool_.FindExtensionByNumber(foo_message_, 1)); + EXPECT_EQ(bar_extension_, pool_.FindExtensionByNumber(bar_message_, 1)); + + EXPECT_TRUE(pool_.FindExtensionByNumber(foo_message_, 2) == NULL); +} + +// =================================================================== + +// Test simple flat messages and fields. +class DescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message TestForeign {} + // enum TestEnum {} + // + // message TestMessage { + // required string foo = 1; + // optional TestEnum bar = 6; + // repeated TestForeign baz = 500000000; + // optional group qux = 15 {} + // } + // + // // in "bar.proto" + // package corge.grault; + // message TestMessage2 { + // required string foo = 1; + // required string bar = 2; + // required string quux = 6; + // } + // + // We cheat and use TestForeign as the type for qux rather than create + // an actual nested type. + // + // Since all primitive types (including string) use the same building + // code, there's no need to test each one individually. + // + // TestMessage2 is primarily here to test FindFieldByName and friends. + // All messages created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + AddMessage(&foo_file, "TestForeign"); + AddEmptyEnum(&foo_file, "TestEnum"); + + DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); + AddField(message, "foo", 1, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message, "bar", 6, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_ENUM) + ->set_type_name("TestEnum"); + AddField(message, "baz", 500000000, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_MESSAGE) + ->set_type_name("TestForeign"); + AddField(message, "qux", 15, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_GROUP) + ->set_type_name("TestForeign"); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); + AddField(message2, "foo", 1, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message2, "bar", 2, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + AddField(message2, "quux", 6, + FieldDescriptorProto::LABEL_REQUIRED, + FieldDescriptorProto::TYPE_STRING); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + enum_ = foo_file_->enum_type(0); + + ASSERT_EQ(2, foo_file_->message_type_count()); + foreign_ = foo_file_->message_type(0); + message_ = foo_file_->message_type(1); + + ASSERT_EQ(4, message_->field_count()); + foo_ = message_->field(0); + bar_ = message_->field(1); + baz_ = message_->field(2); + qux_ = message_->field(3); + + ASSERT_EQ(1, bar_file_->message_type_count()); + message2_ = bar_file_->message_type(0); + + ASSERT_EQ(3, message2_->field_count()); + foo2_ = message2_->field(0); + bar2_ = message2_->field(1); + quux2_ = message2_->field(2); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* message_; + const Descriptor* message2_; + const Descriptor* foreign_; + const EnumDescriptor* enum_; + + const FieldDescriptor* foo_; + const FieldDescriptor* bar_; + const FieldDescriptor* baz_; + const FieldDescriptor* qux_; + + const FieldDescriptor* foo2_; + const FieldDescriptor* bar2_; + const FieldDescriptor* quux2_; +}; + +TEST_F(DescriptorTest, Name) { + EXPECT_EQ("TestMessage", message_->name()); + EXPECT_EQ("TestMessage", message_->full_name()); + EXPECT_EQ(foo_file_, message_->file()); + + EXPECT_EQ("TestMessage2", message2_->name()); + EXPECT_EQ("corge.grault.TestMessage2", message2_->full_name()); + EXPECT_EQ(bar_file_, message2_->file()); +} + +TEST_F(DescriptorTest, ContainingType) { + EXPECT_TRUE(message_->containing_type() == NULL); + EXPECT_TRUE(message2_->containing_type() == NULL); +} + +TEST_F(DescriptorTest, FieldsByIndex) { + ASSERT_EQ(4, message_->field_count()); + EXPECT_EQ(foo_, message_->field(0)); + EXPECT_EQ(bar_, message_->field(1)); + EXPECT_EQ(baz_, message_->field(2)); + EXPECT_EQ(qux_, message_->field(3)); +} + +TEST_F(DescriptorTest, FindFieldByName) { + // All messages in the same DescriptorPool share a single lookup table for + // fields. So, in addition to testing that FindFieldByName finds the fields + // of the message, we need to test that it does *not* find the fields of + // *other* messages. + + EXPECT_EQ(foo_, message_->FindFieldByName("foo")); + EXPECT_EQ(bar_, message_->FindFieldByName("bar")); + EXPECT_EQ(baz_, message_->FindFieldByName("baz")); + EXPECT_EQ(qux_, message_->FindFieldByName("qux")); + EXPECT_TRUE(message_->FindFieldByName("no_such_field") == NULL); + EXPECT_TRUE(message_->FindFieldByName("quux") == NULL); + + EXPECT_EQ(foo2_ , message2_->FindFieldByName("foo" )); + EXPECT_EQ(bar2_ , message2_->FindFieldByName("bar" )); + EXPECT_EQ(quux2_, message2_->FindFieldByName("quux")); + EXPECT_TRUE(message2_->FindFieldByName("baz") == NULL); + EXPECT_TRUE(message2_->FindFieldByName("qux") == NULL); +} + +TEST_F(DescriptorTest, FindFieldByNumber) { + EXPECT_EQ(foo_, message_->FindFieldByNumber(1)); + EXPECT_EQ(bar_, message_->FindFieldByNumber(6)); + EXPECT_EQ(baz_, message_->FindFieldByNumber(500000000)); + EXPECT_EQ(qux_, message_->FindFieldByNumber(15)); + EXPECT_TRUE(message_->FindFieldByNumber(837592) == NULL); + EXPECT_TRUE(message_->FindFieldByNumber(2) == NULL); + + EXPECT_EQ(foo2_ , message2_->FindFieldByNumber(1)); + EXPECT_EQ(bar2_ , message2_->FindFieldByNumber(2)); + EXPECT_EQ(quux2_, message2_->FindFieldByNumber(6)); + EXPECT_TRUE(message2_->FindFieldByNumber(15) == NULL); + EXPECT_TRUE(message2_->FindFieldByNumber(500000000) == NULL); +} + +TEST_F(DescriptorTest, FieldName) { + EXPECT_EQ("foo", foo_->name()); + EXPECT_EQ("bar", bar_->name()); + EXPECT_EQ("baz", baz_->name()); + EXPECT_EQ("qux", qux_->name()); +} + +TEST_F(DescriptorTest, FieldFullName) { + EXPECT_EQ("TestMessage.foo", foo_->full_name()); + EXPECT_EQ("TestMessage.bar", bar_->full_name()); + EXPECT_EQ("TestMessage.baz", baz_->full_name()); + EXPECT_EQ("TestMessage.qux", qux_->full_name()); + + EXPECT_EQ("corge.grault.TestMessage2.foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.bar", bar2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.quux", quux2_->full_name()); +} + +TEST_F(DescriptorTest, FieldFile) { + EXPECT_EQ(foo_file_, foo_->file()); + EXPECT_EQ(foo_file_, bar_->file()); + EXPECT_EQ(foo_file_, baz_->file()); + EXPECT_EQ(foo_file_, qux_->file()); + + EXPECT_EQ(bar_file_, foo2_->file()); + EXPECT_EQ(bar_file_, bar2_->file()); + EXPECT_EQ(bar_file_, quux2_->file()); +} + +TEST_F(DescriptorTest, FieldIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); + EXPECT_EQ(2, baz_->index()); + EXPECT_EQ(3, qux_->index()); +} + +TEST_F(DescriptorTest, FieldNumber) { + EXPECT_EQ( 1, foo_->number()); + EXPECT_EQ( 6, bar_->number()); + EXPECT_EQ(500000000, baz_->number()); + EXPECT_EQ( 15, qux_->number()); +} + +TEST_F(DescriptorTest, FieldType) { + EXPECT_EQ(FieldDescriptor::TYPE_STRING , foo_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_ENUM , bar_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, baz_->type()); + EXPECT_EQ(FieldDescriptor::TYPE_GROUP , qux_->type()); +} + +TEST_F(DescriptorTest, FieldLabel) { + EXPECT_EQ(FieldDescriptor::LABEL_REQUIRED, foo_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, baz_->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, qux_->label()); + + EXPECT_TRUE (foo_->is_required()); + EXPECT_FALSE(foo_->is_optional()); + EXPECT_FALSE(foo_->is_repeated()); + + EXPECT_FALSE(bar_->is_required()); + EXPECT_TRUE (bar_->is_optional()); + EXPECT_FALSE(bar_->is_repeated()); + + EXPECT_FALSE(baz_->is_required()); + EXPECT_FALSE(baz_->is_optional()); + EXPECT_TRUE (baz_->is_repeated()); +} + +TEST_F(DescriptorTest, FieldHasDefault) { + EXPECT_FALSE(foo_->has_default_value()); + EXPECT_FALSE(bar_->has_default_value()); + EXPECT_FALSE(baz_->has_default_value()); + EXPECT_FALSE(qux_->has_default_value()); +} + +TEST_F(DescriptorTest, FieldContainingType) { + EXPECT_EQ(message_, foo_->containing_type()); + EXPECT_EQ(message_, bar_->containing_type()); + EXPECT_EQ(message_, baz_->containing_type()); + EXPECT_EQ(message_, qux_->containing_type()); + + EXPECT_EQ(message2_, foo2_ ->containing_type()); + EXPECT_EQ(message2_, bar2_ ->containing_type()); + EXPECT_EQ(message2_, quux2_->containing_type()); +} + +TEST_F(DescriptorTest, FieldMessageType) { + EXPECT_TRUE(foo_->message_type() == NULL); + EXPECT_TRUE(bar_->message_type() == NULL); + + EXPECT_EQ(foreign_, baz_->message_type()); + EXPECT_EQ(foreign_, qux_->message_type()); +} + +TEST_F(DescriptorTest, FieldEnumType) { + EXPECT_TRUE(foo_->enum_type() == NULL); + EXPECT_TRUE(baz_->enum_type() == NULL); + EXPECT_TRUE(qux_->enum_type() == NULL); + + EXPECT_EQ(enum_, bar_->enum_type()); +} + +// =================================================================== + +// Test enum descriptors. +class EnumDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // enum TestEnum { + // FOO = 1; + // BAR = 2; + // } + // + // // in "bar.proto" + // package corge.grault; + // enum TestEnum2 { + // FOO = 1; + // BAZ = 3; + // } + // + // TestEnum2 is primarily here to test FindValueByName and friends. + // All enums created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + + // TestEnum + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + EnumDescriptorProto* enum_proto = AddEnum(&foo_file, "TestEnum"); + AddEnumValue(enum_proto, "FOO", 1); + AddEnumValue(enum_proto, "BAR", 2); + + // TestEnum2 + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + EnumDescriptorProto* enum2_proto = AddEnum(&bar_file, "TestEnum2"); + AddEnumValue(enum2_proto, "FOO", 1); + AddEnumValue(enum2_proto, "BAZ", 3); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + enum_ = foo_file_->enum_type(0); + + ASSERT_EQ(2, enum_->value_count()); + foo_ = enum_->value(0); + bar_ = enum_->value(1); + + ASSERT_EQ(1, bar_file_->enum_type_count()); + enum2_ = bar_file_->enum_type(0); + + ASSERT_EQ(2, enum2_->value_count()); + foo2_ = enum2_->value(0); + baz2_ = enum2_->value(1); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const EnumDescriptor* enum_; + const EnumDescriptor* enum2_; + + const EnumValueDescriptor* foo_; + const EnumValueDescriptor* bar_; + + const EnumValueDescriptor* foo2_; + const EnumValueDescriptor* baz2_; +}; + +TEST_F(EnumDescriptorTest, Name) { + EXPECT_EQ("TestEnum", enum_->name()); + EXPECT_EQ("TestEnum", enum_->full_name()); + EXPECT_EQ(foo_file_, enum_->file()); + + EXPECT_EQ("TestEnum2", enum2_->name()); + EXPECT_EQ("corge.grault.TestEnum2", enum2_->full_name()); + EXPECT_EQ(bar_file_, enum2_->file()); +} + +TEST_F(EnumDescriptorTest, ContainingType) { + EXPECT_TRUE(enum_->containing_type() == NULL); + EXPECT_TRUE(enum2_->containing_type() == NULL); +} + +TEST_F(EnumDescriptorTest, ValuesByIndex) { + ASSERT_EQ(2, enum_->value_count()); + EXPECT_EQ(foo_, enum_->value(0)); + EXPECT_EQ(bar_, enum_->value(1)); +} + +TEST_F(EnumDescriptorTest, FindValueByName) { + EXPECT_EQ(foo_ , enum_ ->FindValueByName("FOO")); + EXPECT_EQ(bar_ , enum_ ->FindValueByName("BAR")); + EXPECT_EQ(foo2_, enum2_->FindValueByName("FOO")); + EXPECT_EQ(baz2_, enum2_->FindValueByName("BAZ")); + + EXPECT_TRUE(enum_ ->FindValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(enum_ ->FindValueByName("BAZ" ) == NULL); + EXPECT_TRUE(enum2_->FindValueByName("BAR" ) == NULL); +} + +TEST_F(EnumDescriptorTest, FindValueByNumber) { + EXPECT_EQ(foo_ , enum_ ->FindValueByNumber(1)); + EXPECT_EQ(bar_ , enum_ ->FindValueByNumber(2)); + EXPECT_EQ(foo2_, enum2_->FindValueByNumber(1)); + EXPECT_EQ(baz2_, enum2_->FindValueByNumber(3)); + + EXPECT_TRUE(enum_ ->FindValueByNumber(416) == NULL); + EXPECT_TRUE(enum_ ->FindValueByNumber(3) == NULL); + EXPECT_TRUE(enum2_->FindValueByNumber(2) == NULL); +} + +TEST_F(EnumDescriptorTest, ValueName) { + EXPECT_EQ("FOO", foo_->name()); + EXPECT_EQ("BAR", bar_->name()); +} + +TEST_F(EnumDescriptorTest, ValueFullName) { + EXPECT_EQ("FOO", foo_->full_name()); + EXPECT_EQ("BAR", bar_->full_name()); + EXPECT_EQ("corge.grault.FOO", foo2_->full_name()); + EXPECT_EQ("corge.grault.BAZ", baz2_->full_name()); +} + +TEST_F(EnumDescriptorTest, ValueIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); +} + +TEST_F(EnumDescriptorTest, ValueNumber) { + EXPECT_EQ(1, foo_->number()); + EXPECT_EQ(2, bar_->number()); +} + +TEST_F(EnumDescriptorTest, ValueType) { + EXPECT_EQ(enum_ , foo_ ->type()); + EXPECT_EQ(enum_ , bar_ ->type()); + EXPECT_EQ(enum2_, foo2_->type()); + EXPECT_EQ(enum2_, baz2_->type()); +} + +// =================================================================== + +// Test service descriptors. +class ServiceDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following messages and service: + // // in "foo.proto" + // message FooRequest {} + // message FooResponse {} + // message BarRequest {} + // message BarResponse {} + // message BazRequest {} + // message BazResponse {} + // + // service TestService { + // rpc Foo(FooRequest) returns (FooResponse); + // rpc Bar(BarRequest) returns (BarResponse); + // } + // + // // in "bar.proto" + // package corge.grault + // service TestService2 { + // rpc Foo(FooRequest) returns (FooResponse); + // rpc Baz(BazRequest) returns (BazResponse); + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + AddMessage(&foo_file, "FooRequest"); + AddMessage(&foo_file, "FooResponse"); + AddMessage(&foo_file, "BarRequest"); + AddMessage(&foo_file, "BarResponse"); + AddMessage(&foo_file, "BazRequest"); + AddMessage(&foo_file, "BazResponse"); + + ServiceDescriptorProto* service = AddService(&foo_file, "TestService"); + AddMethod(service, "Foo", "FooRequest", "FooResponse"); + AddMethod(service, "Bar", "BarRequest", "BarResponse"); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + bar_file.add_dependency("foo.proto"); + + ServiceDescriptorProto* service2 = AddService(&bar_file, "TestService2"); + AddMethod(service2, "Foo", "FooRequest", "FooResponse"); + AddMethod(service2, "Baz", "BazRequest", "BazResponse"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(6, foo_file_->message_type_count()); + foo_request_ = foo_file_->message_type(0); + foo_response_ = foo_file_->message_type(1); + bar_request_ = foo_file_->message_type(2); + bar_response_ = foo_file_->message_type(3); + baz_request_ = foo_file_->message_type(4); + baz_response_ = foo_file_->message_type(5); + + ASSERT_EQ(1, foo_file_->service_count()); + service_ = foo_file_->service(0); + + ASSERT_EQ(2, service_->method_count()); + foo_ = service_->method(0); + bar_ = service_->method(1); + + ASSERT_EQ(1, bar_file_->service_count()); + service2_ = bar_file_->service(0); + + ASSERT_EQ(2, service2_->method_count()); + foo2_ = service2_->method(0); + baz2_ = service2_->method(1); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* foo_request_; + const Descriptor* foo_response_; + const Descriptor* bar_request_; + const Descriptor* bar_response_; + const Descriptor* baz_request_; + const Descriptor* baz_response_; + + const ServiceDescriptor* service_; + const ServiceDescriptor* service2_; + + const MethodDescriptor* foo_; + const MethodDescriptor* bar_; + + const MethodDescriptor* foo2_; + const MethodDescriptor* baz2_; +}; + +TEST_F(ServiceDescriptorTest, Name) { + EXPECT_EQ("TestService", service_->name()); + EXPECT_EQ("TestService", service_->full_name()); + EXPECT_EQ(foo_file_, service_->file()); + + EXPECT_EQ("TestService2", service2_->name()); + EXPECT_EQ("corge.grault.TestService2", service2_->full_name()); + EXPECT_EQ(bar_file_, service2_->file()); +} + +TEST_F(ServiceDescriptorTest, MethodsByIndex) { + ASSERT_EQ(2, service_->method_count()); + EXPECT_EQ(foo_, service_->method(0)); + EXPECT_EQ(bar_, service_->method(1)); +} + +TEST_F(ServiceDescriptorTest, FindMethodByName) { + EXPECT_EQ(foo_ , service_ ->FindMethodByName("Foo")); + EXPECT_EQ(bar_ , service_ ->FindMethodByName("Bar")); + EXPECT_EQ(foo2_, service2_->FindMethodByName("Foo")); + EXPECT_EQ(baz2_, service2_->FindMethodByName("Baz")); + + EXPECT_TRUE(service_ ->FindMethodByName("NoSuchMethod") == NULL); + EXPECT_TRUE(service_ ->FindMethodByName("Baz" ) == NULL); + EXPECT_TRUE(service2_->FindMethodByName("Bar" ) == NULL); +} + +TEST_F(ServiceDescriptorTest, MethodName) { + EXPECT_EQ("Foo", foo_->name()); + EXPECT_EQ("Bar", bar_->name()); +} + +TEST_F(ServiceDescriptorTest, MethodFullName) { + EXPECT_EQ("TestService.Foo", foo_->full_name()); + EXPECT_EQ("TestService.Bar", bar_->full_name()); + EXPECT_EQ("corge.grault.TestService2.Foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestService2.Baz", baz2_->full_name()); +} + +TEST_F(ServiceDescriptorTest, MethodIndex) { + EXPECT_EQ(0, foo_->index()); + EXPECT_EQ(1, bar_->index()); +} + +TEST_F(ServiceDescriptorTest, MethodParent) { + EXPECT_EQ(service_, foo_->service()); + EXPECT_EQ(service_, bar_->service()); +} + +TEST_F(ServiceDescriptorTest, MethodInputType) { + EXPECT_EQ(foo_request_, foo_->input_type()); + EXPECT_EQ(bar_request_, bar_->input_type()); +} + +TEST_F(ServiceDescriptorTest, MethodOutputType) { + EXPECT_EQ(foo_response_, foo_->output_type()); + EXPECT_EQ(bar_response_, bar_->output_type()); +} + +// =================================================================== + +// Test nested types. +class NestedDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // // in "foo.proto" + // message TestMessage { + // message Foo {} + // message Bar {} + // enum Baz { A = 1; } + // enum Qux { B = 1; } + // } + // + // // in "bar.proto" + // package corge.grault; + // message TestMessage2 { + // message Foo {} + // message Baz {} + // enum Qux { A = 1; } + // enum Quux { C = 1; } + // } + // + // TestMessage2 is primarily here to test FindNestedTypeByName and friends. + // All messages created from the same DescriptorPool share the same lookup + // table, so we need to insure that they don't interfere. + // + // We add enum values to the enums in order to test searching for enum + // values across a message's scope. + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + DescriptorProto* message = AddMessage(&foo_file, "TestMessage"); + AddNestedMessage(message, "Foo"); + AddNestedMessage(message, "Bar"); + EnumDescriptorProto* baz = AddNestedEnum(message, "Baz"); + AddEnumValue(baz, "A", 1); + EnumDescriptorProto* qux = AddNestedEnum(message, "Qux"); + AddEnumValue(qux, "B", 1); + + FileDescriptorProto bar_file; + bar_file.set_name("bar.proto"); + bar_file.set_package("corge.grault"); + + DescriptorProto* message2 = AddMessage(&bar_file, "TestMessage2"); + AddNestedMessage(message2, "Foo"); + AddNestedMessage(message2, "Baz"); + EnumDescriptorProto* qux2 = AddNestedEnum(message2, "Qux"); + AddEnumValue(qux2, "A", 1); + EnumDescriptorProto* quux2 = AddNestedEnum(message2, "Quux"); + AddEnumValue(quux2, "C", 1); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + bar_file_ = pool_.BuildFile(bar_file); + ASSERT_TRUE(bar_file_ != NULL); + + ASSERT_EQ(1, foo_file_->message_type_count()); + message_ = foo_file_->message_type(0); + + ASSERT_EQ(2, message_->nested_type_count()); + foo_ = message_->nested_type(0); + bar_ = message_->nested_type(1); + + ASSERT_EQ(2, message_->enum_type_count()); + baz_ = message_->enum_type(0); + qux_ = message_->enum_type(1); + + ASSERT_EQ(1, baz_->value_count()); + a_ = baz_->value(0); + ASSERT_EQ(1, qux_->value_count()); + b_ = qux_->value(0); + + ASSERT_EQ(1, bar_file_->message_type_count()); + message2_ = bar_file_->message_type(0); + + ASSERT_EQ(2, message2_->nested_type_count()); + foo2_ = message2_->nested_type(0); + baz2_ = message2_->nested_type(1); + + ASSERT_EQ(2, message2_->enum_type_count()); + qux2_ = message2_->enum_type(0); + quux2_ = message2_->enum_type(1); + + ASSERT_EQ(1, qux2_->value_count()); + a2_ = qux2_->value(0); + ASSERT_EQ(1, quux2_->value_count()); + c2_ = quux2_->value(0); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + const FileDescriptor* bar_file_; + + const Descriptor* message_; + const Descriptor* message2_; + + const Descriptor* foo_; + const Descriptor* bar_; + const EnumDescriptor* baz_; + const EnumDescriptor* qux_; + const EnumValueDescriptor* a_; + const EnumValueDescriptor* b_; + + const Descriptor* foo2_; + const Descriptor* baz2_; + const EnumDescriptor* qux2_; + const EnumDescriptor* quux2_; + const EnumValueDescriptor* a2_; + const EnumValueDescriptor* c2_; +}; + +TEST_F(NestedDescriptorTest, MessageName) { + EXPECT_EQ("Foo", foo_ ->name()); + EXPECT_EQ("Bar", bar_ ->name()); + EXPECT_EQ("Foo", foo2_->name()); + EXPECT_EQ("Baz", baz2_->name()); + + EXPECT_EQ("TestMessage.Foo", foo_->full_name()); + EXPECT_EQ("TestMessage.Bar", bar_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Foo", foo2_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Baz", baz2_->full_name()); +} + +TEST_F(NestedDescriptorTest, MessageContainingType) { + EXPECT_EQ(message_ , foo_ ->containing_type()); + EXPECT_EQ(message_ , bar_ ->containing_type()); + EXPECT_EQ(message2_, foo2_->containing_type()); + EXPECT_EQ(message2_, baz2_->containing_type()); +} + +TEST_F(NestedDescriptorTest, NestedMessagesByIndex) { + ASSERT_EQ(2, message_->nested_type_count()); + EXPECT_EQ(foo_, message_->nested_type(0)); + EXPECT_EQ(bar_, message_->nested_type(1)); +} + +TEST_F(NestedDescriptorTest, FindFieldByNameDoesntFindNestedTypes) { + EXPECT_TRUE(message_->FindFieldByName("Foo") == NULL); + EXPECT_TRUE(message_->FindFieldByName("Qux") == NULL); + EXPECT_TRUE(message_->FindExtensionByName("Foo") == NULL); + EXPECT_TRUE(message_->FindExtensionByName("Qux") == NULL); +} + +TEST_F(NestedDescriptorTest, FindNestedTypeByName) { + EXPECT_EQ(foo_ , message_ ->FindNestedTypeByName("Foo")); + EXPECT_EQ(bar_ , message_ ->FindNestedTypeByName("Bar")); + EXPECT_EQ(foo2_, message2_->FindNestedTypeByName("Foo")); + EXPECT_EQ(baz2_, message2_->FindNestedTypeByName("Baz")); + + EXPECT_TRUE(message_ ->FindNestedTypeByName("NoSuchType") == NULL); + EXPECT_TRUE(message_ ->FindNestedTypeByName("Baz" ) == NULL); + EXPECT_TRUE(message2_->FindNestedTypeByName("Bar" ) == NULL); + + EXPECT_TRUE(message_->FindNestedTypeByName("Qux") == NULL); +} + +TEST_F(NestedDescriptorTest, EnumName) { + EXPECT_EQ("Baz" , baz_ ->name()); + EXPECT_EQ("Qux" , qux_ ->name()); + EXPECT_EQ("Qux" , qux2_->name()); + EXPECT_EQ("Quux", quux2_->name()); + + EXPECT_EQ("TestMessage.Baz", baz_->full_name()); + EXPECT_EQ("TestMessage.Qux", qux_->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Qux" , qux2_ ->full_name()); + EXPECT_EQ("corge.grault.TestMessage2.Quux", quux2_->full_name()); +} + +TEST_F(NestedDescriptorTest, EnumContainingType) { + EXPECT_EQ(message_ , baz_ ->containing_type()); + EXPECT_EQ(message_ , qux_ ->containing_type()); + EXPECT_EQ(message2_, qux2_ ->containing_type()); + EXPECT_EQ(message2_, quux2_->containing_type()); +} + +TEST_F(NestedDescriptorTest, NestedEnumsByIndex) { + ASSERT_EQ(2, message_->nested_type_count()); + EXPECT_EQ(foo_, message_->nested_type(0)); + EXPECT_EQ(bar_, message_->nested_type(1)); +} + +TEST_F(NestedDescriptorTest, FindEnumTypeByName) { + EXPECT_EQ(baz_ , message_ ->FindEnumTypeByName("Baz" )); + EXPECT_EQ(qux_ , message_ ->FindEnumTypeByName("Qux" )); + EXPECT_EQ(qux2_ , message2_->FindEnumTypeByName("Qux" )); + EXPECT_EQ(quux2_, message2_->FindEnumTypeByName("Quux")); + + EXPECT_TRUE(message_ ->FindEnumTypeByName("NoSuchType") == NULL); + EXPECT_TRUE(message_ ->FindEnumTypeByName("Quux" ) == NULL); + EXPECT_TRUE(message2_->FindEnumTypeByName("Baz" ) == NULL); + + EXPECT_TRUE(message_->FindEnumTypeByName("Foo") == NULL); +} + +TEST_F(NestedDescriptorTest, FindEnumValueByName) { + EXPECT_EQ(a_ , message_ ->FindEnumValueByName("A")); + EXPECT_EQ(b_ , message_ ->FindEnumValueByName("B")); + EXPECT_EQ(a2_, message2_->FindEnumValueByName("A")); + EXPECT_EQ(c2_, message2_->FindEnumValueByName("C")); + + EXPECT_TRUE(message_ ->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(message_ ->FindEnumValueByName("C" ) == NULL); + EXPECT_TRUE(message2_->FindEnumValueByName("B" ) == NULL); + + EXPECT_TRUE(message_->FindEnumValueByName("Foo") == NULL); +} + +// =================================================================== + +// Test extensions. +class ExtensionDescriptorTest : public testing::Test { + protected: + virtual void SetUp() { + // Build descriptors for the following definitions: + // + // enum Baz {} + // message Qux {} + // + // message Foo { + // extensions 10 to 19; + // extensions 30 to 39; + // } + // extends Foo with optional int32 foo_int32 = 10; + // extends Foo with repeated TestEnum foo_enum = 19; + // message Bar { + // extends Foo with optional Qux foo_message = 30; + // // (using Qux as the group type) + // extends Foo with repeated group foo_group = 39; + // } + + FileDescriptorProto foo_file; + foo_file.set_name("foo.proto"); + + AddEmptyEnum(&foo_file, "Baz"); + AddMessage(&foo_file, "Qux"); + + DescriptorProto* foo = AddMessage(&foo_file, "Foo"); + AddExtensionRange(foo, 10, 20); + AddExtensionRange(foo, 30, 40); + + AddExtension(&foo_file, "Foo", "foo_int32", 10, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + AddExtension(&foo_file, "Foo", "foo_enum", 19, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_ENUM) + ->set_type_name("Baz"); + + DescriptorProto* bar = AddMessage(&foo_file, "Bar"); + AddNestedExtension(bar, "Foo", "foo_message", 30, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_MESSAGE) + ->set_type_name("Qux"); + AddNestedExtension(bar, "Foo", "foo_group", 39, + FieldDescriptorProto::LABEL_REPEATED, + FieldDescriptorProto::TYPE_GROUP) + ->set_type_name("Qux"); + + // Build the descriptors and get the pointers. + foo_file_ = pool_.BuildFile(foo_file); + ASSERT_TRUE(foo_file_ != NULL); + + ASSERT_EQ(1, foo_file_->enum_type_count()); + baz_ = foo_file_->enum_type(0); + + ASSERT_EQ(3, foo_file_->message_type_count()); + qux_ = foo_file_->message_type(0); + foo_ = foo_file_->message_type(1); + bar_ = foo_file_->message_type(2); + } + + DescriptorPool pool_; + + const FileDescriptor* foo_file_; + + const Descriptor* foo_; + const Descriptor* bar_; + const EnumDescriptor* baz_; + const Descriptor* qux_; +}; + +TEST_F(ExtensionDescriptorTest, ExtensionRanges) { + EXPECT_EQ(0, bar_->extension_range_count()); + ASSERT_EQ(2, foo_->extension_range_count()); + + EXPECT_EQ(10, foo_->extension_range(0)->start); + EXPECT_EQ(30, foo_->extension_range(1)->start); + + EXPECT_EQ(20, foo_->extension_range(0)->end); + EXPECT_EQ(40, foo_->extension_range(1)->end); +}; + +TEST_F(ExtensionDescriptorTest, Extensions) { + EXPECT_EQ(0, foo_->extension_count()); + ASSERT_EQ(2, foo_file_->extension_count()); + ASSERT_EQ(2, bar_->extension_count()); + + EXPECT_TRUE(foo_file_->extension(0)->is_extension()); + EXPECT_TRUE(foo_file_->extension(1)->is_extension()); + EXPECT_TRUE(bar_->extension(0)->is_extension()); + EXPECT_TRUE(bar_->extension(1)->is_extension()); + + EXPECT_EQ("foo_int32" , foo_file_->extension(0)->name()); + EXPECT_EQ("foo_enum" , foo_file_->extension(1)->name()); + EXPECT_EQ("foo_message", bar_->extension(0)->name()); + EXPECT_EQ("foo_group" , bar_->extension(1)->name()); + + EXPECT_EQ(10, foo_file_->extension(0)->number()); + EXPECT_EQ(19, foo_file_->extension(1)->number()); + EXPECT_EQ(30, bar_->extension(0)->number()); + EXPECT_EQ(39, bar_->extension(1)->number()); + + EXPECT_EQ(FieldDescriptor::TYPE_INT32 , foo_file_->extension(0)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_ENUM , foo_file_->extension(1)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_MESSAGE, bar_->extension(0)->type()); + EXPECT_EQ(FieldDescriptor::TYPE_GROUP , bar_->extension(1)->type()); + + EXPECT_EQ(baz_, foo_file_->extension(1)->enum_type()); + EXPECT_EQ(qux_, bar_->extension(0)->message_type()); + EXPECT_EQ(qux_, bar_->extension(1)->message_type()); + + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, foo_file_->extension(0)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, foo_file_->extension(1)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_OPTIONAL, bar_->extension(0)->label()); + EXPECT_EQ(FieldDescriptor::LABEL_REPEATED, bar_->extension(1)->label()); + + EXPECT_EQ(foo_, foo_file_->extension(0)->containing_type()); + EXPECT_EQ(foo_, foo_file_->extension(1)->containing_type()); + EXPECT_EQ(foo_, bar_->extension(0)->containing_type()); + EXPECT_EQ(foo_, bar_->extension(1)->containing_type()); + + EXPECT_TRUE(foo_file_->extension(0)->extension_scope() == NULL); + EXPECT_TRUE(foo_file_->extension(1)->extension_scope() == NULL); + EXPECT_EQ(bar_, bar_->extension(0)->extension_scope()); + EXPECT_EQ(bar_, bar_->extension(1)->extension_scope()); +}; + +TEST_F(ExtensionDescriptorTest, IsExtensionNumber) { + EXPECT_FALSE(foo_->IsExtensionNumber( 9)); + EXPECT_TRUE (foo_->IsExtensionNumber(10)); + EXPECT_TRUE (foo_->IsExtensionNumber(19)); + EXPECT_FALSE(foo_->IsExtensionNumber(20)); + EXPECT_FALSE(foo_->IsExtensionNumber(29)); + EXPECT_TRUE (foo_->IsExtensionNumber(30)); + EXPECT_TRUE (foo_->IsExtensionNumber(39)); + EXPECT_FALSE(foo_->IsExtensionNumber(40)); +} + +TEST_F(ExtensionDescriptorTest, FindExtensionByName) { + // Note that FileDescriptor::FindExtensionByName() is tested by + // FileDescriptorTest. + ASSERT_EQ(2, bar_->extension_count()); + + EXPECT_EQ(bar_->extension(0), bar_->FindExtensionByName("foo_message")); + EXPECT_EQ(bar_->extension(1), bar_->FindExtensionByName("foo_group" )); + + EXPECT_TRUE(bar_->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo_->FindExtensionByName("foo_int32") == NULL); + EXPECT_TRUE(foo_->FindExtensionByName("foo_message") == NULL); +} + +// =================================================================== + +class MiscTest : public testing::Test { + protected: + // Function which makes a field of the given type just to find out what its + // cpp_type is. + FieldDescriptor::CppType GetCppTypeForFieldType(FieldDescriptor::Type type) { + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + AddEmptyEnum(&file_proto, "DummyEnum"); + + DescriptorProto* message = AddMessage(&file_proto, "TestMessage"); + FieldDescriptorProto* field = + AddField(message, "foo", 1, FieldDescriptorProto::LABEL_OPTIONAL, + static_cast(type)); + + if (type == FieldDescriptor::TYPE_MESSAGE || + type == FieldDescriptor::TYPE_GROUP) { + field->set_type_name("TestMessage"); + } else if (type == FieldDescriptor::TYPE_ENUM) { + field->set_type_name("DummyEnum"); + } + + // Build the descriptors and get the pointers. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + + if (file != NULL && + file->message_type_count() == 1 && + file->message_type(0)->field_count() == 1) { + return file->message_type(0)->field(0)->cpp_type(); + } else { + return static_cast(0); + } + } +}; + +TEST_F(MiscTest, CppTypes) { + // Test that CPP types are assigned correctly. + + typedef FieldDescriptor FD; // avoid ugly line wrapping + + EXPECT_EQ(FD::CPPTYPE_DOUBLE , GetCppTypeForFieldType(FD::TYPE_DOUBLE )); + EXPECT_EQ(FD::CPPTYPE_FLOAT , GetCppTypeForFieldType(FD::TYPE_FLOAT )); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_INT64 )); + EXPECT_EQ(FD::CPPTYPE_UINT64 , GetCppTypeForFieldType(FD::TYPE_UINT64 )); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_INT32 )); + EXPECT_EQ(FD::CPPTYPE_UINT64 , GetCppTypeForFieldType(FD::TYPE_FIXED64 )); + EXPECT_EQ(FD::CPPTYPE_UINT32 , GetCppTypeForFieldType(FD::TYPE_FIXED32 )); + EXPECT_EQ(FD::CPPTYPE_BOOL , GetCppTypeForFieldType(FD::TYPE_BOOL )); + EXPECT_EQ(FD::CPPTYPE_STRING , GetCppTypeForFieldType(FD::TYPE_STRING )); + EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_GROUP )); + EXPECT_EQ(FD::CPPTYPE_MESSAGE, GetCppTypeForFieldType(FD::TYPE_MESSAGE )); + EXPECT_EQ(FD::CPPTYPE_STRING , GetCppTypeForFieldType(FD::TYPE_BYTES )); + EXPECT_EQ(FD::CPPTYPE_UINT32 , GetCppTypeForFieldType(FD::TYPE_UINT32 )); + EXPECT_EQ(FD::CPPTYPE_ENUM , GetCppTypeForFieldType(FD::TYPE_ENUM )); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_SFIXED32)); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_SFIXED64)); + EXPECT_EQ(FD::CPPTYPE_INT32 , GetCppTypeForFieldType(FD::TYPE_SINT32 )); + EXPECT_EQ(FD::CPPTYPE_INT64 , GetCppTypeForFieldType(FD::TYPE_SINT64 )); +} + +TEST_F(MiscTest, DefaultValues) { + // Test that setting default values works. + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + + EnumDescriptorProto* enum_type_proto = AddEnum(&file_proto, "DummyEnum"); + AddEnumValue(enum_type_proto, "A", 1); + AddEnumValue(enum_type_proto, "B", 2); + + DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage"); + + typedef FieldDescriptorProto FD; // avoid ugly line wrapping + const FD::Label label = FD::LABEL_OPTIONAL; + + // Create fields of every CPP type with default values. + AddField(message_proto, "int32" , 1, label, FD::TYPE_INT32 ) + ->set_default_value("-1"); + AddField(message_proto, "int64" , 2, label, FD::TYPE_INT64 ) + ->set_default_value("-1000000000000"); + AddField(message_proto, "uint32", 3, label, FD::TYPE_UINT32) + ->set_default_value("42"); + AddField(message_proto, "uint64", 4, label, FD::TYPE_UINT64) + ->set_default_value("2000000000000"); + AddField(message_proto, "float" , 5, label, FD::TYPE_FLOAT ) + ->set_default_value("4.5"); + AddField(message_proto, "double", 6, label, FD::TYPE_DOUBLE) + ->set_default_value("10e100"); + AddField(message_proto, "bool" , 7, label, FD::TYPE_BOOL ) + ->set_default_value("true"); + AddField(message_proto, "string", 8, label, FD::TYPE_STRING) + ->set_default_value("hello"); + AddField(message_proto, "data" , 9, label, FD::TYPE_BYTES ) + ->set_default_value("\\001\\002\\003"); + + FieldDescriptorProto* enum_field = + AddField(message_proto, "enum", 10, label, FD::TYPE_ENUM); + enum_field->set_type_name("DummyEnum"); + enum_field->set_default_value("B"); + + // Strings are allowed to have empty defaults. (At one point, due to + // a bug, empty defaults for strings were rejected. Oops.) + AddField(message_proto, "empty_string", 11, label, FD::TYPE_STRING) + ->set_default_value(""); + + // Add a second set of fields with implicit defalut values. + AddField(message_proto, "implicit_int32" , 21, label, FD::TYPE_INT32 ); + AddField(message_proto, "implicit_int64" , 22, label, FD::TYPE_INT64 ); + AddField(message_proto, "implicit_uint32", 23, label, FD::TYPE_UINT32); + AddField(message_proto, "implicit_uint64", 24, label, FD::TYPE_UINT64); + AddField(message_proto, "implicit_float" , 25, label, FD::TYPE_FLOAT ); + AddField(message_proto, "implicit_double", 26, label, FD::TYPE_DOUBLE); + AddField(message_proto, "implicit_bool" , 27, label, FD::TYPE_BOOL ); + AddField(message_proto, "implicit_string", 28, label, FD::TYPE_STRING); + AddField(message_proto, "implicit_data" , 29, label, FD::TYPE_BYTES ); + AddField(message_proto, "implicit_enum" , 30, label, FD::TYPE_ENUM) + ->set_type_name("DummyEnum"); + + // Build it. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->enum_type_count()); + const EnumDescriptor* enum_type = file->enum_type(0); + ASSERT_EQ(2, enum_type->value_count()); + const EnumValueDescriptor* enum_value_a = enum_type->value(0); + const EnumValueDescriptor* enum_value_b = enum_type->value(1); + + ASSERT_EQ(1, file->message_type_count()); + const Descriptor* message = file->message_type(0); + + ASSERT_EQ(21, message->field_count()); + + // Check the default values. + ASSERT_TRUE(message->field(0)->has_default_value()); + ASSERT_TRUE(message->field(1)->has_default_value()); + ASSERT_TRUE(message->field(2)->has_default_value()); + ASSERT_TRUE(message->field(3)->has_default_value()); + ASSERT_TRUE(message->field(4)->has_default_value()); + ASSERT_TRUE(message->field(5)->has_default_value()); + ASSERT_TRUE(message->field(6)->has_default_value()); + ASSERT_TRUE(message->field(7)->has_default_value()); + ASSERT_TRUE(message->field(8)->has_default_value()); + ASSERT_TRUE(message->field(9)->has_default_value()); + ASSERT_TRUE(message->field(10)->has_default_value()); + + EXPECT_EQ(-1 , message->field(0)->default_value_int32 ()); + EXPECT_EQ(-GOOGLE_ULONGLONG(1000000000000), + message->field(1)->default_value_int64 ()); + EXPECT_EQ(42 , message->field(2)->default_value_uint32()); + EXPECT_EQ(GOOGLE_ULONGLONG(2000000000000), + message->field(3)->default_value_uint64()); + EXPECT_EQ(4.5 , message->field(4)->default_value_float ()); + EXPECT_EQ(10e100 , message->field(5)->default_value_double()); + EXPECT_EQ(true , message->field(6)->default_value_bool ()); + EXPECT_EQ("hello" , message->field(7)->default_value_string()); + EXPECT_EQ("\001\002\003" , message->field(8)->default_value_string()); + EXPECT_EQ(enum_value_b , message->field(9)->default_value_enum ()); + EXPECT_EQ("" , message->field(10)->default_value_string()); + + ASSERT_FALSE(message->field(11)->has_default_value()); + ASSERT_FALSE(message->field(12)->has_default_value()); + ASSERT_FALSE(message->field(13)->has_default_value()); + ASSERT_FALSE(message->field(14)->has_default_value()); + ASSERT_FALSE(message->field(15)->has_default_value()); + ASSERT_FALSE(message->field(16)->has_default_value()); + ASSERT_FALSE(message->field(17)->has_default_value()); + ASSERT_FALSE(message->field(18)->has_default_value()); + ASSERT_FALSE(message->field(19)->has_default_value()); + ASSERT_FALSE(message->field(20)->has_default_value()); + + EXPECT_EQ(0 , message->field(11)->default_value_int32 ()); + EXPECT_EQ(0 , message->field(12)->default_value_int64 ()); + EXPECT_EQ(0 , message->field(13)->default_value_uint32()); + EXPECT_EQ(0 , message->field(14)->default_value_uint64()); + EXPECT_EQ(0.0f , message->field(15)->default_value_float ()); + EXPECT_EQ(0.0 , message->field(16)->default_value_double()); + EXPECT_EQ(false, message->field(17)->default_value_bool ()); + EXPECT_EQ("" , message->field(18)->default_value_string()); + EXPECT_EQ("" , message->field(19)->default_value_string()); + EXPECT_EQ(enum_value_a, message->field(20)->default_value_enum()); +} + +TEST_F(MiscTest, FieldOptions) { + // Try setting field options. + + FileDescriptorProto file_proto; + file_proto.set_name("foo.proto"); + + DescriptorProto* message_proto = AddMessage(&file_proto, "TestMessage"); + AddField(message_proto, "foo", 1, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + FieldDescriptorProto* bar_proto = + AddField(message_proto, "bar", 2, + FieldDescriptorProto::LABEL_OPTIONAL, + FieldDescriptorProto::TYPE_INT32); + + FieldOptions* options = bar_proto->mutable_options(); + options->set_ctype(FieldOptions::CORD); + + // Build the descriptors and get the pointers. + DescriptorPool pool; + const FileDescriptor* file = pool.BuildFile(file_proto); + ASSERT_TRUE(file != NULL); + + ASSERT_EQ(1, file->message_type_count()); + const Descriptor* message = file->message_type(0); + + ASSERT_EQ(2, message->field_count()); + const FieldDescriptor* foo = message->field(0); + const FieldDescriptor* bar = message->field(1); + + // "foo" had no options set, so it should return the default options. + EXPECT_EQ(&FieldOptions::default_instance(), &foo->options()); + + // "bar" had options set. + EXPECT_NE(&FieldOptions::default_instance(), options); + EXPECT_TRUE(bar->options().has_ctype()); + EXPECT_EQ(FieldOptions::CORD, bar->options().ctype()); +} + +// =================================================================== + +// The tests below trigger every unique call to AddError() in descriptor.cc, +// in the order in which they appear in that file. I'm using TextFormat here +// to specify the input descriptors because building them using code would +// be too bulky. + +class MockErrorCollector : public DescriptorPool::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(const string& filename, + const string& element_name, const Message* descriptor, + ErrorLocation location, const string& message) { + const char* location_name = NULL; + switch (location) { + case NAME : location_name = "NAME" ; break; + case NUMBER : location_name = "NUMBER" ; break; + case TYPE : location_name = "TYPE" ; break; + case EXTENDEE : location_name = "EXTENDEE" ; break; + case DEFAULT_VALUE: location_name = "DEFAULT_VALUE"; break; + case INPUT_TYPE : location_name = "INPUT_TYPE" ; break; + case OUTPUT_TYPE : location_name = "OUTPUT_TYPE" ; break; + case OTHER : location_name = "OTHER" ; break; + } + + strings::SubstituteAndAppend( + &text_, "$0: $1: $2: $3\n", + filename, element_name, location_name, message); + } +}; + +class ValidationErrorTest : public testing::Test { + protected: + // Parse file_text as a FileDescriptorProto in text format and add it + // to the DescriptorPool. Expect no errors. + void BuildFile(const string& file_text) { + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + ASSERT_TRUE(pool_.BuildFile(file_proto) != NULL); + } + + // Parse file_text as a FileDescriptorProto in text format and add it + // to the DescriptorPool. Expect errors to be produced which match the + // given error text. + void BuildFileWithErrors(const string& file_text, + const string& expected_errors) { + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + + MockErrorCollector error_collector; + EXPECT_TRUE( + pool_.BuildFileCollectingErrors(file_proto, &error_collector) == NULL); + EXPECT_EQ(expected_errors, error_collector.text_); + } + + DescriptorPool pool_; +}; + +TEST_F(ValidationErrorTest, AlreadyDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }" + "message_type { name: \"Foo\" }", + + "foo.proto: Foo: NAME: \"Foo\" is already defined.\n"); +} + +TEST_F(ValidationErrorTest, AlreadyDefinedInPackage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "package: \"foo.bar\" " + "message_type { name: \"Foo\" }" + "message_type { name: \"Foo\" }", + + "foo.proto: foo.bar.Foo: NAME: \"Foo\" is already defined in " + "\"foo.bar\".\n"); +} + +TEST_F(ValidationErrorTest, AlreadyDefinedInOtherFile) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }"); + + BuildFileWithErrors( + "name: \"bar.proto\" " + "message_type { name: \"Foo\" }", + + "bar.proto: Foo: NAME: \"Foo\" is already defined in file " + "\"foo.proto\".\n"); +} + +TEST_F(ValidationErrorTest, PackageAlreadyDefined) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"foo\" }"); + BuildFileWithErrors( + "name: \"bar.proto\" " + "package: \"foo.bar\"", + + "bar.proto: foo: NAME: \"foo\" is already defined (as something other " + "than a package) in file \"foo.proto\".\n"); +} + +TEST_F(ValidationErrorTest, EnumValueAlreadyDefinedInParent) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Foo\" value { name: \"FOO\" number: 1 } } " + "enum_type { name: \"Bar\" value { name: \"FOO\" number: 1 } } ", + + "foo.proto: FOO: NAME: \"FOO\" is already defined.\n" + "foo.proto: FOO: NAME: Note that enum values use C++ scoping rules, " + "meaning that enum values are siblings of their type, not children of " + "it. Therefore, \"FOO\" must be unique within the global scope, not " + "just within \"Bar\".\n"); +} + +TEST_F(ValidationErrorTest, EnumValueAlreadyDefinedInParentNonGlobal) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "package: \"pkg\" " + "enum_type { name: \"Foo\" value { name: \"FOO\" number: 1 } } " + "enum_type { name: \"Bar\" value { name: \"FOO\" number: 1 } } ", + + "foo.proto: pkg.FOO: NAME: \"FOO\" is already defined in \"pkg\".\n" + "foo.proto: pkg.FOO: NAME: Note that enum values use C++ scoping rules, " + "meaning that enum values are siblings of their type, not children of " + "it. Therefore, \"FOO\" must be unique within \"pkg\", not just within " + "\"Bar\".\n"); +} + +TEST_F(ValidationErrorTest, MissingName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { }", + + "foo.proto: : NAME: Missing name.\n"); +} + +TEST_F(ValidationErrorTest, InvalidName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"$\" }", + + "foo.proto: $: NAME: \"$\" is not a valid identifier.\n"); +} + +TEST_F(ValidationErrorTest, InvalidPackageName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "package: \"foo.$\"", + + "foo.proto: foo.$: NAME: \"$\" is not a valid identifier.\n"); +} + +TEST_F(ValidationErrorTest, MissingFileName) { + BuildFileWithErrors( + "", + + ": : OTHER: Missing field: FileDescriptorProto.name.\n"); +} + +TEST_F(ValidationErrorTest, DupeDependency) { + BuildFile("name: \"foo.proto\""); + BuildFileWithErrors( + "name: \"bar.proto\" " + "dependency: \"foo.proto\" " + "dependency: \"foo.proto\" ", + + "bar.proto: bar.proto: OTHER: Import \"foo.proto\" was listed twice.\n"); +} + +TEST_F(ValidationErrorTest, UnknownDependency) { + BuildFileWithErrors( + "name: \"bar.proto\" " + "dependency: \"foo.proto\" ", + + "bar.proto: bar.proto: OTHER: Import \"foo.proto\" has not been loaded.\n"); +} + +TEST_F(ValidationErrorTest, DupeFile) { + BuildFile( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }"); + // Note: We should *not* get redundant errors about "Foo" already being + // defined. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" }", + + "foo.proto: foo.proto: OTHER: A file with this name is already in the " + "pool.\n"); +} + +TEST_F(ValidationErrorTest, FieldInExtensionRange) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 9 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 10 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"baz\" number: 19 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"qux\" number: 20 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " extension_range { start: 10 end: 20 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Extension range 10 to 19 includes field " + "\"bar\" (10).\n" + "foo.proto: Foo.baz: NUMBER: Extension range 10 to 19 includes field " + "\"baz\" (19).\n"); +} + +TEST_F(ValidationErrorTest, OverlappingExtensionRanges) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 20 }" + " extension_range { start: 20 end: 30 }" + " extension_range { start: 19 end: 21 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with " + "already-defined range 10 to 19.\n" + "foo.proto: Foo: NUMBER: Extension range 19 to 20 overlaps with " + "already-defined range 20 to 29.\n"); +} + +TEST_F(ValidationErrorTest, InvalidDefaults) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + + // Invalid number. + " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL type: TYPE_INT32" + " default_value: \"abc\" }" + + // Empty default value. + " field { name: \"bar\" number: 2 label: LABEL_OPTIONAL type: TYPE_INT32" + " default_value: \"\" }" + + // Invalid boolean. + " field { name: \"baz\" number: 3 label: LABEL_OPTIONAL type: TYPE_BOOL" + " default_value: \"abc\" }" + + // Messages can't have defaults. + " field { name: \"qux\" number: 4 label: LABEL_OPTIONAL type: TYPE_MESSAGE" + " default_value: \"abc\" type_name: \"Foo\" }" + + // Same thing, but we don't know that this field has message type until + // we look up the type name. + " field { name: \"quux\" number: 5 label: LABEL_OPTIONAL" + " default_value: \"abc\" type_name: \"Foo\" }" + "}", + + "foo.proto: Foo.foo: DEFAULT_VALUE: Couldn't parse default value.\n" + "foo.proto: Foo.bar: DEFAULT_VALUE: Couldn't parse default value.\n" + "foo.proto: Foo.baz: DEFAULT_VALUE: Boolean default must be true or " + "false.\n" + "foo.proto: Foo.qux: DEFAULT_VALUE: Messages can't have default values.\n" + "foo.proto: Foo.quux: DEFAULT_VALUE: Messages can't have default " + "values.\n"); +} + +TEST_F(ValidationErrorTest, NegativeFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: -1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field numbers must be positive integers.\n"); +} + +TEST_F(ValidationErrorTest, HugeFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 0x70000000 " + " label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NUMBER: Field numbers cannot be greater than " + "536870911.\n"); +} + +TEST_F(ValidationErrorTest, ReservedFieldNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field {name:\"foo\" number: 18999 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"bar\" number: 19000 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"baz\" number: 19999 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field {name:\"qux\" number: 20000 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Field numbers 19000 through 19999 are " + "reserved for the protocol buffer library implementation.\n" + "foo.proto: Foo.baz: NUMBER: Field numbers 19000 through 19999 are " + "reserved for the protocol buffer library implementation.\n"); +} + +TEST_F(ValidationErrorTest, ExtensionMissingExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension { name: \"foo\" number: 1 label: LABEL_OPTIONAL" + " type_name: \"Foo\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee not set for " + "extension field.\n"); +} + +TEST_F(ValidationErrorTest, NonExtensionWithExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + " extension_range { start: 1 end: 2 }" + "}" + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 1 label: LABEL_OPTIONAL" + " type_name: \"Foo\" extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: FieldDescriptorProto.extendee set for " + "non-extension field.\n"); +} + +TEST_F(ValidationErrorTest, FieldNumberConflict) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + " field { name: \"bar\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.bar: NUMBER: Field number 1 has already been used in " + "\"Foo\" by field \"foo\".\n"); +} + +TEST_F(ValidationErrorTest, BadMessageSetExtensionType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"MessageSet\"" + " options { message_set_wire_format: true }" + " extension_range { start: 4 end: 5 }" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:4 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"MessageSet\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional " + "messages.\n"); +} + +TEST_F(ValidationErrorTest, BadMessageSetExtensionLabel) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"MessageSet\"" + " options { message_set_wire_format: true }" + " extension_range { start: 4 end: 5 }" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:4 label:LABEL_REPEATED type:TYPE_MESSAGE" + " type_name: \"Foo\" extendee: \"MessageSet\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Extensions of MessageSets must be optional " + "messages.\n"); +} + +TEST_F(ValidationErrorTest, FieldInMessageSet) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " options { message_set_wire_format: true }" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: NAME: MessageSets cannot have fields, only " + "extensions.\n"); +} + +TEST_F(ValidationErrorTest, NegativeExtensionRangeNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: -10 end: -1 }" + "}", + + "foo.proto: Foo: NUMBER: Extension numbers must be positive integers.\n"); +} + +TEST_F(ValidationErrorTest, HugeExtensionRangeNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 1 end: 0x70000000 }" + "}", + + "foo.proto: Foo: NUMBER: Extension numbers cannot be greater than " + "536870911.\n"); +} + +TEST_F(ValidationErrorTest, ExtensionRangeEndBeforeStart) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension_range { start: 10 end: 10 }" + " extension_range { start: 10 end: 5 }" + "}", + + "foo.proto: Foo: NUMBER: Extension range end number must be greater than " + "start number.\n" + "foo.proto: Foo: NUMBER: Extension range end number must be greater than " + "start number.\n"); +} + +TEST_F(ValidationErrorTest, EmptyEnum) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Foo\" }" + // Also use the empty enum in a message to make sure there are no crashes + // during validation (possible if the code attempts to derive a default + // value for the field). + "message_type {" + " name: \"Bar\"" + " field { name: \"foo\" number: 1 label:LABEL_OPTIONAL type_name:\"Foo\" }" + " field { name: \"bar\" number: 2 label:LABEL_OPTIONAL type_name:\"Foo\" " + " default_value: \"NO_SUCH_VALUE\" }" + "}", + + "foo.proto: Foo: NAME: Enums must contain at least one value.\n" + "foo.proto: Bar.bar: DEFAULT_VALUE: Enum type \"Foo\" has no value named " + "\"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ValidationErrorTest, UndefinedExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, NonMessageExtendee) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } }" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: EXTENDEE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, NotAnExtensionNumber) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + "}" + "message_type {" + " name: \"Foo\"" + " extension { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " extendee: \"Bar\" }" + "}", + + "foo.proto: Foo.foo: NUMBER: \"Bar\" does not declare 1 as an extension " + "number.\n"); +} + +TEST_F(ValidationErrorTest, UndefinedFieldType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, FieldTypeDefinedInUndeclaredDependency) { + BuildFile( + "name: \"bar.proto\" " + "message_type { name: \"Bar\" } "); + + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\" }" + "}", + "foo.proto: Foo.foo: TYPE: \"Bar\" seems to be defined in \"bar.proto\", " + "which is not imported by \"foo.proto\". To use it here, please add the " + "necessary import.\n"); +} + +TEST_F(ValidationErrorTest, SearchMostLocalFirst) { + // The following should produce an error that Bar.Baz is not defined: + // message Bar { message Baz {} } + // message Foo { + // message Bar { + // // Placing "message Baz{}" here, or removing Foo.Bar altogether, + // // would fix the error. + // } + // optional Bar.Baz baz = 1; + // } + // An one point the lookup code incorrectly did not produce an error in this + // case, because when looking for Bar.Baz, it would try "Foo.Bar.Baz" first, + // fail, and ten try "Bar.Baz" and succeed, even though "Bar" should actually + // refer to the inner Bar, not the outer one. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Bar\"" + " nested_type { name: \"Baz\" }" + "}" + "message_type {" + " name: \"Foo\"" + " nested_type { name: \"Bar\" }" + " field { name:\"baz\" number:1 label:LABEL_OPTIONAL" + " type_name:\"Bar.Baz\" }" + "}", + + "foo.proto: Foo.baz: TYPE: \"Bar.Baz\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, PackageOriginallyDeclaredInTransitiveDependent) { + // Imagine we have the following: + // + // foo.proto: + // package foo.bar; + // bar.proto: + // package foo.bar; + // import "foo.proto"; + // message Bar {} + // baz.proto: + // package foo; + // import "bar.proto" + // message Baz { optional bar.Bar qux = 1; } + // + // When validating baz.proto, we will look up "bar.Bar". As part of this + // lookup, we first lookup "bar" then try to find "Bar" within it. "bar" + // should resolve to "foo.bar". Note, though, that "foo.bar" was originally + // defined in foo.proto, which is not a direct dependency of baz.proto. The + // implementation of FindSymbol() normally only returns symbols in direct + // dependencies, not indirect ones. This test insures that this does not + // prevent it from finding "foo.bar". + + BuildFile( + "name: \"foo.proto\" " + "package: \"foo.bar\" "); + BuildFile( + "name: \"bar.proto\" " + "package: \"foo.bar\" " + "dependency: \"foo.proto\" " + "message_type { name: \"Bar\" }"); + BuildFile( + "name: \"baz.proto\" " + "package: \"foo\" " + "dependency: \"bar.proto\" " + "message_type { " + " name: \"Baz\" " + " field { name:\"qux\" number:1 label:LABEL_OPTIONAL " + " type_name:\"bar.Bar\" }" + "}"); +} + +TEST_F(ValidationErrorTest, FieldTypeNotAType) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"bar\" }" + " field { name:\"bar\" number:2 label:LABEL_OPTIONAL type:TYPE_INT32 }" + "}", + + "foo.proto: Foo.foo: TYPE: \"bar\" is not a type.\n"); +} + +TEST_F(ValidationErrorTest, EnumFieldTypeIsMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Bar\" } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_ENUM" + " type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not an enum type.\n"); +} + +TEST_F(ValidationErrorTest, MessageFieldTypeIsEnum) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE" + " type_name:\"Bar\" }" + "}", + + "foo.proto: Foo.foo: TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, BadEnumDefaultValue) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type_name:\"Bar\"" + " default_value:\"NO_SUCH_VALUE\" }" + "}", + + "foo.proto: Foo.foo: DEFAULT_VALUE: Enum type \"Bar\" has no value named " + "\"NO_SUCH_VALUE\".\n"); +} + +TEST_F(ValidationErrorTest, PrimitiveWithTypeName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_INT32" + " type_name:\"Foo\" }" + "}", + + "foo.proto: Foo.foo: TYPE: Field with primitive type has type_name.\n"); +} + +TEST_F(ValidationErrorTest, NonPrimitiveWithoutTypeName) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"Foo\"" + " field { name:\"foo\" number:1 label:LABEL_OPTIONAL type:TYPE_MESSAGE }" + "}", + + "foo.proto: Foo.foo: TYPE: Field with message or enum type missing " + "type_name.\n"); +} + +TEST_F(ValidationErrorTest, InputTypeNotDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }" + "}", + + "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, InputTypeNotAMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Bar\" output_type: \"Foo\" }" + "}", + + "foo.proto: TestService.A: INPUT_TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, OutputTypeNotDefined) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }" + "}", + + "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not defined.\n"); +} + +TEST_F(ValidationErrorTest, OutputTypeNotAMessage) { + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "enum_type { name: \"Bar\" value { name:\"DUMMY\" number:0 } } " + "service {" + " name: \"TestService\"" + " method { name: \"A\" input_type: \"Foo\" output_type: \"Bar\" }" + "}", + + "foo.proto: TestService.A: OUTPUT_TYPE: \"Bar\" is not a message type.\n"); +} + +TEST_F(ValidationErrorTest, RollbackAfterError) { + // Build a file which contains every kind of construct but references an + // undefined type. All these constructs will be added to the symbol table + // before the undefined type error is noticed. The DescriptorPool will then + // have to roll everything back. + BuildFileWithErrors( + "name: \"foo.proto\" " + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "} " + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"BAR\" number:1 }" + "} " + "service {" + " name: \"TestService\"" + " method {" + " name: \"Baz\"" + " input_type: \"NoSuchType\"" // error + " output_type: \"TestMessage\"" + " }" + "}", + + "foo.proto: TestService.Baz: INPUT_TYPE: \"NoSuchType\" is not defined.\n"); + + // Make sure that if we build the same file again with the error fixed, + // it works. If the above rollback was incomplete, then some symbols will + // be left defined, and this second attempt will fail since it tries to + // re-define the same symbols. + BuildFile( + "name: \"foo.proto\" " + "message_type {" + " name: \"TestMessage\"" + " field { name:\"foo\" label:LABEL_OPTIONAL type:TYPE_INT32 number:1 }" + "} " + "enum_type {" + " name: \"TestEnum\"" + " value { name:\"BAR\" number:1 }" + "} " + "service {" + " name: \"TestService\"" + " method { name:\"Baz\"" + " input_type:\"TestMessage\"" + " output_type:\"TestMessage\" }" + "}"); +} + +TEST_F(ValidationErrorTest, ErrorsReportedToLogError) { + // Test that errors are reported to GOOGLE_LOG(ERROR) if no error collector is + // provided. + + FileDescriptorProto file_proto; + ASSERT_TRUE(TextFormat::ParseFromString( + "name: \"foo.proto\" " + "message_type { name: \"Foo\" } " + "message_type { name: \"Foo\" } ", + &file_proto)); + + vector errors; + + { + ScopedMemoryLog log; + EXPECT_TRUE(pool_.BuildFile(file_proto) == NULL); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(2, errors.size()); + + EXPECT_EQ("Invalid proto descriptor for file \"foo.proto\":", errors[0]); + EXPECT_EQ(" Foo: \"Foo\" is already defined.", errors[1]); +} + +// =================================================================== +// DescriptorDatabase + +static void AddToDatabase(SimpleDescriptorDatabase* database, + const char* file_text) { + FileDescriptorProto file_proto; + EXPECT_TRUE(TextFormat::ParseFromString(file_text, &file_proto)); + database->Add(file_proto); +} + +class DatabaseBackedPoolTest : public testing::Test { + protected: + DatabaseBackedPoolTest() {} + + SimpleDescriptorDatabase database_; + + virtual void SetUp() { + AddToDatabase(&database_, + "name: \"foo.proto\" " + "message_type { name:\"Foo\" extension_range { start: 1 end: 100 } } " + "enum_type { name:\"TestEnum\" value { name:\"DUMMY\" number:0 } } " + "service { name:\"TestService\" } "); + AddToDatabase(&database_, + "name: \"bar.proto\" " + "dependency: \"foo.proto\" " + "message_type { name:\"Bar\" } " + "extension { name:\"foo_ext\" extendee: \".Foo\" number:5 " + " label:LABEL_OPTIONAL type:TYPE_INT32 } "); + } + + // We can't inject a file containing errors into a DescriptorPool, so we + // need an actual mock DescriptorDatabase to test errors. + class ErrorDescriptorDatabase : public DescriptorDatabase { + public: + ErrorDescriptorDatabase() {} + ~ErrorDescriptorDatabase() {} + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + // error.proto and error2.proto cyclically import each other. + if (filename == "error.proto") { + output->Clear(); + output->set_name("error.proto"); + output->add_dependency("error2.proto"); + return true; + } else if (filename == "error2.proto") { + output->Clear(); + output->set_name("error2.proto"); + output->add_dependency("error.proto"); + return true; + } else { + return false; + } + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + return false; + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + return false; + } + }; + + // A DescriptorDatabase that counts how many times each method has been + // called and forwards to some other DescriptorDatabase. + class CallCountingDatabase : public DescriptorDatabase { + public: + CallCountingDatabase(DescriptorDatabase* wrapped_db) + : wrapped_db_(wrapped_db) { + Clear(); + } + ~CallCountingDatabase() {} + + DescriptorDatabase* wrapped_db_; + + int call_count_; + + void Clear() { + call_count_ = 0; + } + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileByName(filename, output); + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileContainingSymbol(symbol_name, output); + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + ++call_count_; + return wrapped_db_->FindFileContainingExtension( + containing_type, field_number, output); + } + }; + + // A DescriptorDatabase which falsely always returns foo.proto when searching + // for any symbol or extension number. This shouldn't cause the + // DescriptorPool to reload foo.proto if it is already loaded. + class FalsePositiveDatabase : public DescriptorDatabase { + public: + FalsePositiveDatabase(DescriptorDatabase* wrapped_db) + : wrapped_db_(wrapped_db) {} + ~FalsePositiveDatabase() {} + + DescriptorDatabase* wrapped_db_; + + // implements DescriptorDatabase --------------------------------- + bool FindFileByName(const string& filename, + FileDescriptorProto* output) { + return wrapped_db_->FindFileByName(filename, output); + } + bool FindFileContainingSymbol(const string& symbol_name, + FileDescriptorProto* output) { + return FindFileByName("foo.proto", output); + } + bool FindFileContainingExtension(const string& containing_type, + int field_number, + FileDescriptorProto* output) { + return FindFileByName("foo.proto", output); + } + }; +}; + +TEST_F(DatabaseBackedPoolTest, FindFileByName) { + DescriptorPool pool(&database_); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + EXPECT_EQ("Foo", foo->message_type(0)->name()); + + EXPECT_EQ(foo, pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindFileByName("no_such_file.proto") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindDependencyBeforeDependent) { + DescriptorPool pool(&database_); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + EXPECT_EQ("Foo", foo->message_type(0)->name()); + + const FileDescriptor* bar = pool.FindFileByName("bar.proto"); + ASSERT_TRUE(bar != NULL); + EXPECT_EQ("bar.proto", bar->name()); + ASSERT_EQ(1, bar->message_type_count()); + EXPECT_EQ("Bar", bar->message_type(0)->name()); + + ASSERT_EQ(1, bar->dependency_count()); + EXPECT_EQ(foo, bar->dependency(0)); +} + +TEST_F(DatabaseBackedPoolTest, FindDependentBeforeDependency) { + DescriptorPool pool(&database_); + + const FileDescriptor* bar = pool.FindFileByName("bar.proto"); + ASSERT_TRUE(bar != NULL); + EXPECT_EQ("bar.proto", bar->name()); + ASSERT_EQ(1, bar->message_type_count()); + ASSERT_EQ("Bar", bar->message_type(0)->name()); + + const FileDescriptor* foo = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(foo != NULL); + EXPECT_EQ("foo.proto", foo->name()); + ASSERT_EQ(1, foo->message_type_count()); + ASSERT_EQ("Foo", foo->message_type(0)->name()); + + ASSERT_EQ(1, bar->dependency_count()); + EXPECT_EQ(foo, bar->dependency(0)); +} + +TEST_F(DatabaseBackedPoolTest, FindFileContainingSymbol) { + DescriptorPool pool(&database_); + + const FileDescriptor* file = pool.FindFileContainingSymbol("Foo"); + ASSERT_TRUE(file != NULL); + EXPECT_EQ("foo.proto", file->name()); + EXPECT_EQ(file, pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindFileContainingSymbol("NoSuchSymbol") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindMessageTypeByName) { + DescriptorPool pool(&database_); + + const Descriptor* type = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(type != NULL); + EXPECT_EQ("Foo", type->name()); + EXPECT_EQ(type->file(), pool.FindFileByName("foo.proto")); + + EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchType") == NULL); +} + +TEST_F(DatabaseBackedPoolTest, FindExtensionByNumber) { + DescriptorPool pool(&database_); + + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + + const FieldDescriptor* extension = pool.FindExtensionByNumber(foo, 5); + ASSERT_TRUE(extension != NULL); + EXPECT_EQ("foo_ext", extension->name()); + EXPECT_EQ(extension->file(), pool.FindFileByName("bar.proto")); + + EXPECT_TRUE(pool.FindExtensionByNumber(foo, 12) == NULL); +} + +TEST_F(DatabaseBackedPoolTest, ErrorWithoutErrorCollector) { + ErrorDescriptorDatabase error_database; + DescriptorPool pool(&error_database); + + vector errors; + + { + ScopedMemoryLog log; + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + errors = log.GetMessages(ERROR); + } + + EXPECT_FALSE(errors.empty()); +} + +TEST_F(DatabaseBackedPoolTest, ErrorWithErrorCollector) { + ErrorDescriptorDatabase error_database; + MockErrorCollector error_collector; + DescriptorPool pool(&error_database, &error_collector); + + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + EXPECT_EQ( + "error.proto: error.proto: OTHER: File recursively imports itself: " + "error.proto -> error2.proto -> error.proto\n" + "error2.proto: error2.proto: OTHER: Import \"error.proto\" was not " + "found or had errors.\n" + "error.proto: error.proto: OTHER: Import \"error2.proto\" was not " + "found or had errors.\n", + error_collector.text_); +} + +TEST_F(DatabaseBackedPoolTest, UnittestProto) { + // Try to load all of unittest.proto from a DescriptorDatabase. This should + // thoroughly test all paths through DescriptorBuilder to insure that there + // are no deadlocking problems when pool_->mutex_ is non-NULL. + const FileDescriptor* original_file = + protobuf_unittest::TestAllTypes::descriptor()->file(); + + DescriptorPoolDatabase database(*DescriptorPool::generated_pool()); + DescriptorPool pool(&database); + const FileDescriptor* file_from_database = + pool.FindFileByName(original_file->name()); + + ASSERT_TRUE(file_from_database != NULL); + + FileDescriptorProto original_file_proto; + original_file->CopyTo(&original_file_proto); + + FileDescriptorProto file_from_database_proto; + file_from_database->CopyTo(&file_from_database_proto); + + EXPECT_EQ(original_file_proto.DebugString(), + file_from_database_proto.DebugString()); +} + +TEST_F(DatabaseBackedPoolTest, DoesntRetryDbUnnecessarily) { + // Searching for a child of an existing descriptor should never fall back + // to the DescriptorDatabase even if it isn't found, because we know all + // children are already loaded. + CallCountingDatabase call_counter(&database_); + DescriptorPool pool(&call_counter); + + const FileDescriptor* file = pool.FindFileByName("foo.proto"); + ASSERT_TRUE(file != NULL); + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + const EnumDescriptor* test_enum = pool.FindEnumTypeByName("TestEnum"); + ASSERT_TRUE(test_enum != NULL); + const ServiceDescriptor* test_service = pool.FindServiceByName("TestService"); + ASSERT_TRUE(test_service != NULL); + + EXPECT_NE(0, call_counter.call_count_); + call_counter.Clear(); + + EXPECT_TRUE(foo->FindFieldByName("no_such_field") == NULL); + EXPECT_TRUE(foo->FindExtensionByName("no_such_extension") == NULL); + EXPECT_TRUE(foo->FindNestedTypeByName("NoSuchMessageType") == NULL); + EXPECT_TRUE(foo->FindEnumTypeByName("NoSuchEnumType") == NULL); + EXPECT_TRUE(foo->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(test_enum->FindValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(test_service->FindMethodByName("NoSuchMethod") == NULL); + + EXPECT_TRUE(file->FindMessageTypeByName("NoSuchMessageType") == NULL); + EXPECT_TRUE(file->FindEnumTypeByName("NoSuchEnumType") == NULL); + EXPECT_TRUE(file->FindEnumValueByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(file->FindServiceByName("NO_SUCH_VALUE") == NULL); + EXPECT_TRUE(file->FindExtensionByName("no_such_extension") == NULL); + EXPECT_EQ(0, call_counter.call_count_); +} + +TEST_F(DatabaseBackedPoolTest, DoesntReloadFilesUncesessarily) { + // If FindFileContainingSymbol() or FindFileContainingExtension() return a + // file that is already in the DescriptorPool, it should not attempt to + // reload the file. + FalsePositiveDatabase false_positive_database(&database_); + MockErrorCollector error_collector; + DescriptorPool pool(&false_positive_database, &error_collector); + + // First make sure foo.proto is loaded. + const Descriptor* foo = pool.FindMessageTypeByName("Foo"); + ASSERT_TRUE(foo != NULL); + + // Try inducing false positives. + EXPECT_TRUE(pool.FindMessageTypeByName("NoSuchSymbol") == NULL); + EXPECT_TRUE(pool.FindExtensionByNumber(foo, 22) == NULL); + + // No errors should have been reported. (If foo.proto was incorrectly + // loaded multiple times, errors would have been reported.) + EXPECT_EQ("", error_collector.text_); +} + +TEST_F(DatabaseBackedPoolTest, DoesntReloadKnownBadFiles) { + ErrorDescriptorDatabase error_database; + MockErrorCollector error_collector; + DescriptorPool pool(&error_database, &error_collector); + + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + error_collector.text_.clear(); + EXPECT_TRUE(pool.FindFileByName("error.proto") == NULL); + EXPECT_EQ("", error_collector.text_); +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc new file mode 100644 index 00000000..c355bf5f --- /dev/null +++ b/src/google/protobuf/dynamic_message.cc @@ -0,0 +1,532 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// DynamicMessage is implemented by constructing a data structure which +// has roughly the same memory layout as a generated message would have. +// Then, we use GeneratedMessageReflection to implement our reflection +// interface. All the other operations we need to implement (e.g. +// parsing, copying, etc.) are already implemented in terms of +// Reflection, so the rest is easy. +// +// The up side of this strategy is that it's very efficient. We don't +// need to use hash_maps or generic representations of fields. The +// down side is that this is a low-level memory management hack which +// can be tricky to get right. +// +// As mentioned in the header, we only expose a DynamicMessageFactory +// publicly, not the DynamicMessage class itself. This is because +// GenericMessageReflection wants to have a pointer to a "default" +// copy of the class, with all fields initialized to their default +// values. We only want to construct one of these per message type, +// so DynamicMessageFactory stores a cache of default messages for +// each type it sees (each unique Descriptor pointer). The code +// refers to the "default" copy of the class as the "prototype". +// +// Note on memory allocation: This module often calls "operator new()" +// to allocate untyped memory, rather than calling something like +// "new uint8[]". This is because "operator new()" means "Give me some +// space which I can use as I please." while "new uint8[]" means "Give +// me an array of 8-bit integers.". In practice, the later may return +// a pointer that is not aligned correctly for general use. I believe +// Item 8 of "More Effective C++" discusses this in more detail, though +// I don't have the book on me right now so I'm not sure. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; +using internal::ExtensionSet; +using internal::GeneratedMessageReflection; +using internal::GenericRepeatedField; + + +// =================================================================== +// Some helper tables and functions... + +namespace { + +// Compute the byte size of the in-memory representation of the field. +int FieldSpaceUsed(const FieldDescriptor* field) { + typedef FieldDescriptor FD; // avoid line wrapping + if (field->label() == FD::LABEL_REPEATED) { + switch (field->cpp_type()) { + case FD::CPPTYPE_INT32 : return sizeof(RepeatedField); + case FD::CPPTYPE_INT64 : return sizeof(RepeatedField); + case FD::CPPTYPE_UINT32 : return sizeof(RepeatedField); + case FD::CPPTYPE_UINT64 : return sizeof(RepeatedField); + case FD::CPPTYPE_DOUBLE : return sizeof(RepeatedField); + case FD::CPPTYPE_FLOAT : return sizeof(RepeatedField); + case FD::CPPTYPE_BOOL : return sizeof(RepeatedField); + case FD::CPPTYPE_ENUM : return sizeof(RepeatedField); + case FD::CPPTYPE_MESSAGE: return sizeof(RepeatedPtrField); + + case FD::CPPTYPE_STRING: + return sizeof(RepeatedPtrField); + break; + } + } else { + switch (field->cpp_type()) { + case FD::CPPTYPE_INT32 : return sizeof(int32 ); + case FD::CPPTYPE_INT64 : return sizeof(int64 ); + case FD::CPPTYPE_UINT32 : return sizeof(uint32 ); + case FD::CPPTYPE_UINT64 : return sizeof(uint64 ); + case FD::CPPTYPE_DOUBLE : return sizeof(double ); + case FD::CPPTYPE_FLOAT : return sizeof(float ); + case FD::CPPTYPE_BOOL : return sizeof(bool ); + case FD::CPPTYPE_ENUM : return sizeof(int ); + case FD::CPPTYPE_MESSAGE: return sizeof(Message*); + + case FD::CPPTYPE_STRING: + return sizeof(string*); + break; + } + } + + GOOGLE_LOG(DFATAL) << "Can't get here."; + return 0; +} + +struct DescendingFieldSizeOrder { + inline bool operator()(const FieldDescriptor* a, + const FieldDescriptor* b) { + // All repeated fields come first. + if (a->is_repeated()) { + if (b->is_repeated()) { + // Repeated fields and are not ordered with respect to each other. + return false; + } else { + return true; + } + } else if (b->is_repeated()) { + return false; + } else { + // Remaining fields in descending order by size. + return FieldSpaceUsed(a) > FieldSpaceUsed(b); + } + } +}; + +inline int DivideRoundingUp(int i, int j) { + return (i + (j - 1)) / j; +} + +static const int kSafeAlignment = sizeof(uint64); + +// Rounds the given byte offset up to the next offset aligned such that any +// type may be stored at it. +inline int AlignOffset(int offset) { + return DivideRoundingUp(offset, kSafeAlignment) * kSafeAlignment; +} + +#define bitsizeof(T) (sizeof(T) * 8) + +} // namespace + +// =================================================================== + +class DynamicMessage : public Message { + public: + struct TypeInfo { + int size; + int has_bits_offset; + int unknown_fields_offset; + int extensions_offset; + + // Not owned by the TypeInfo. + DynamicMessageFactory* factory; // The factory that created this object. + const DescriptorPool* pool; // The factory's DescriptorPool. + const Descriptor* type; // Type of this DynamicMessage. + + // Warning: The order in which the following pointers are defined is + // important (the prototype must be deleted *before* the offsets). + scoped_array offsets; + scoped_ptr reflection; + scoped_ptr prototype; + }; + + DynamicMessage(const TypeInfo* type_info); + ~DynamicMessage(); + + // Called on the prototype after construction to initialize message fields. + void CrossLinkPrototypes(); + + // implements Message ---------------------------------------------- + + Message* New() const; + + int GetCachedSize() const; + void SetCachedSize(int size) const; + + const Descriptor* GetDescriptor() const; + const Reflection* GetReflection() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessage); + + inline bool is_prototype() const { + return type_info_->prototype == this || + // If type_info_->prototype is NULL, then we must be constructing + // the prototype now, which means we must be the prototype. + type_info_->prototype == NULL; + } + + inline void* OffsetToPointer(int offset) { + return reinterpret_cast(this) + offset; + } + inline const void* OffsetToPointer(int offset) const { + return reinterpret_cast(this) + offset; + } + + const TypeInfo* type_info_; + + // TODO(kenton): Make this an atomic when C++ supports it. + mutable int cached_byte_size_; +}; + +DynamicMessage::DynamicMessage(const TypeInfo* type_info) + : type_info_(type_info), + cached_byte_size_(0) { + // We need to call constructors for various fields manually and set + // default values where appropriate. We use placement new to call + // constructors. If you haven't heard of placement new, I suggest Googling + // it now. We use placement new even for primitive types that don't have + // constructors for consistency. (In theory, placement new should be used + // any time you are trying to convert untyped memory to typed memory, though + // in practice that's not strictly necessary for types that don't have a + // constructor.) + + const Descriptor* descriptor = type_info_->type; + + new(OffsetToPointer(type_info_->unknown_fields_offset)) UnknownFieldSet; + + if (type_info_->extensions_offset != -1) { + new(OffsetToPointer(type_info_->extensions_offset)) + ExtensionSet(&type_info_->type, type_info_->pool, type_info_->factory); + } + + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + void* field_ptr = OffsetToPointer(type_info_->offsets[i]); + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, TYPE) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + if (!field->is_repeated()) { \ + new(field_ptr) TYPE(field->default_value_##TYPE()); \ + } else { \ + new(field_ptr) RepeatedField(); \ + } \ + break; + + HANDLE_TYPE(INT32 , int32 ); + HANDLE_TYPE(INT64 , int64 ); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE(FLOAT , float ); + HANDLE_TYPE(BOOL , bool ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_ENUM: + if (!field->is_repeated()) { + new(field_ptr) int(field->default_value_enum()->number()); + } else { + new(field_ptr) RepeatedField(); + } + break; + + case FieldDescriptor::CPPTYPE_STRING: + if (!field->is_repeated()) { + if (is_prototype()) { + new(field_ptr) const string*(&field->default_value_string()); + } else { + string* default_value = + *reinterpret_cast( + type_info_->prototype->OffsetToPointer( + type_info_->offsets[i])); + new(field_ptr) string*(default_value); + } + } else { + new(field_ptr) RepeatedPtrField(); + } + break; + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // If this object is the prototype, its CPPTYPE_MESSAGE fields + // must be initialized later, in CrossLinkPrototypes(), so we don't + // initialize them here. + if (!is_prototype()) { + if (!field->is_repeated()) { + new(field_ptr) Message*(NULL); + } else { + const RepeatedPtrField* prototype_field = + reinterpret_cast*>( + type_info_->prototype->OffsetToPointer( + type_info_->offsets[i])); + new(field_ptr) RepeatedPtrField( + prototype_field->prototype()); + } + } + break; + } + } + } +} + +DynamicMessage::~DynamicMessage() { + const Descriptor* descriptor = type_info_->type; + + reinterpret_cast( + OffsetToPointer(type_info_->unknown_fields_offset))->~UnknownFieldSet(); + + if (type_info_->extensions_offset != -1) { + reinterpret_cast( + OffsetToPointer(type_info_->extensions_offset))->~ExtensionSet(); + } + + // We need to manually run the destructors for repeated fields and strings, + // just as we ran their constructors in the the DynamicMessage constructor. + // Additionally, if any singular embedded messages have been allocated, we + // need to delete them, UNLESS we are the prototype message of this type, + // in which case any embedded messages are other prototypes and shouldn't + // be touched. + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + void* field_ptr = OffsetToPointer(type_info_->offsets[i]); + + if (field->is_repeated()) { + GenericRepeatedField* field = + reinterpret_cast(field_ptr); + field->~GenericRepeatedField(); + + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + string* ptr = *reinterpret_cast(field_ptr); + if (ptr != &field->default_value_string()) { + delete ptr; + } + } else if ((field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) && + !is_prototype()) { + Message* message = *reinterpret_cast(field_ptr); + if (message != NULL) { + delete message; + } + } + } +} + +void DynamicMessage::CrossLinkPrototypes() { + // This should only be called on the prototype message. + GOOGLE_CHECK(is_prototype()); + + DynamicMessageFactory* factory = type_info_->factory; + const Descriptor* descriptor = type_info_->type; + + // Cross-link default messages. + for (int i = 0; i < descriptor->field_count(); i++) { + const FieldDescriptor* field = descriptor->field(i); + void* field_ptr = OffsetToPointer(type_info_->offsets[i]); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + // For fields with message types, we need to cross-link with the + // prototype for the field's type. + const Message* field_prototype = + factory->GetPrototype(field->message_type()); + + if (field->is_repeated()) { + // For repeated fields, we actually construct the RepeatedPtrField + // here, but only for fields with message types. All other repeated + // fields are constructed in DynamicMessage's constructor. + new(field_ptr) RepeatedPtrField(field_prototype); + } else { + // For singular fields, the field is just a pointer which should + // point to the prototype. (OK to const_cast here because the + // prototype itself will only be available const to the outside + // world.) + new(field_ptr) Message*(const_cast(field_prototype)); + } + } + } +} + +Message* DynamicMessage::New() const { + void* new_base = reinterpret_cast(operator new(type_info_->size)); + memset(new_base, 0, type_info_->size); + return new(new_base) DynamicMessage(type_info_); +} + +int DynamicMessage::GetCachedSize() const { + return cached_byte_size_; +} + +void DynamicMessage::SetCachedSize(int size) const { + // This is theoretically not thread-compatible, but in practice it works + // because if multiple threads write this simultaneously, they will be + // writing the exact same value. + cached_byte_size_ = size; +} + +const Descriptor* DynamicMessage::GetDescriptor() const { + return type_info_->type; +} + +const Reflection* DynamicMessage::GetReflection() const { + return type_info_->reflection.get(); +} + +// =================================================================== + +struct DynamicMessageFactory::PrototypeMap { + typedef hash_map Map; + Map map_; +}; + +DynamicMessageFactory::DynamicMessageFactory() + : pool_(NULL), prototypes_(new PrototypeMap) { +} + +DynamicMessageFactory::DynamicMessageFactory(const DescriptorPool* pool) + : pool_(pool), prototypes_(new PrototypeMap) { +} + +DynamicMessageFactory::~DynamicMessageFactory() { + for (PrototypeMap::Map::iterator iter = prototypes_->map_.begin(); + iter != prototypes_->map_.end(); ++iter) { + delete iter->second; + } +} + + +const Message* DynamicMessageFactory::GetPrototype(const Descriptor* type) { + const DynamicMessage::TypeInfo** target = &prototypes_->map_[type]; + if (*target != NULL) { + // Already exists. + return (*target)->prototype.get(); + } + + DynamicMessage::TypeInfo* type_info = new DynamicMessage::TypeInfo; + *target = type_info; + + type_info->type = type; + type_info->pool = (pool_ == NULL) ? type->file()->pool() : pool_; + type_info->factory = this; + + // We need to construct all the structures passed to + // GeneratedMessageReflection's constructor. This includes: + // - A block of memory that contains space for all the message's fields. + // - An array of integers indicating the byte offset of each field within + // this block. + // - A big bitfield containing a bit for each field indicating whether + // or not that field is set. + + // Compute size and offsets. + int* offsets = new int[type->field_count()]; + type_info->offsets.reset(offsets); + + // Sort the fields of this message in descending order by size. We + // assume that if we then pack the fields tightly in this order, all fields + // will end up properly-aligned, since all field sizes are powers of two or + // are multiples of the system word size. + scoped_array ordered_fields( + new const FieldDescriptor*[type->field_count()]); + for (int i = 0; i < type->field_count(); i++) { + ordered_fields[i] = type->field(i); + } + stable_sort(&ordered_fields[0], &ordered_fields[type->field_count()], + DescendingFieldSizeOrder()); + + // Decide all field offsets by packing in order. + // We place the DynamicMessage object itself at the beginning of the allocated + // space. + int size = sizeof(DynamicMessage); + size = AlignOffset(size); + + // Next the has_bits, which is an array of uint32s. + type_info->has_bits_offset = size; + int has_bits_array_size = + DivideRoundingUp(type->field_count(), bitsizeof(uint32)); + size += has_bits_array_size * sizeof(uint32); + size = AlignOffset(size); + + // The ExtensionSet, if any. + if (type->extension_range_count() > 0) { + type_info->extensions_offset = size; + size += sizeof(ExtensionSet); + size = AlignOffset(size); + } else { + // No extensions. + type_info->extensions_offset = -1; + } + + // All the fields. We don't need to align after each field because they are + // sorted in descending size order, and the size of a type is always a + // multiple of its alignment. + for (int i = 0; i < type->field_count(); i++) { + offsets[ordered_fields[i]->index()] = size; + size += FieldSpaceUsed(ordered_fields[i]); + } + + // Add the UnknownFieldSet to the end. + size = AlignOffset(size); + type_info->unknown_fields_offset = size; + size += sizeof(UnknownFieldSet); + + // Align the final size to make sure no clever allocators think that + // alignment is not necessary. + size = AlignOffset(size); + type_info->size = size; + + // Allocate the prototype. + void* base = operator new(size); + memset(base, 0, size); + DynamicMessage* prototype = new(base) DynamicMessage(type_info); + type_info->prototype.reset(prototype); + + // Construct the reflection object. + type_info->reflection.reset( + new GeneratedMessageReflection( + type_info->type, + type_info->prototype.get(), + type_info->offsets.get(), + type_info->has_bits_offset, + type_info->unknown_fields_offset, + type_info->extensions_offset, + type_info->pool)); + + // Cross link prototypes. + prototype->CrossLinkPrototypes(); + + return prototype; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/dynamic_message.h b/src/google/protobuf/dynamic_message.h new file mode 100644 index 00000000..e5a9f908 --- /dev/null +++ b/src/google/protobuf/dynamic_message.h @@ -0,0 +1,105 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Defines an implementation of Message which can emulate types which are not +// known at compile-time. + +#ifndef GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ +#define GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// Defined in other files. +class Descriptor; // descriptor.h +class DescriptorPool; // descriptor.h + +// Constructs implementations of Message which can emulate types which are not +// known at compile-time. +// +// Sometimes you want to be able to manipulate protocol types that you don't +// know about at compile time. It would be nice to be able to construct +// a Message object which implements the message type given by any arbitrary +// Descriptor. DynamicMessage provides this. +// +// As it turns out, a DynamicMessage needs to construct extra +// information about its type in order to operate. Most of this information +// can be shared between all DynamicMessages of the same type. But, caching +// this information in some sort of global map would be a bad idea, since +// the cached information for a particular descriptor could outlive the +// descriptor itself. To avoid this problem, DynamicMessageFactory +// encapsulates this "cache". All DynamicMessages of the same type created +// from the same factory will share the same support data. Any Descriptors +// used with a particular factory must outlive the factory. +class LIBPROTOBUF_EXPORT DynamicMessageFactory : public MessageFactory { + public: + // Construct a DynamicMessageFactory that will search for extensions in + // the DescriptorPool in which the exendee is defined. + DynamicMessageFactory(); + + // Construct a DynamicMessageFactory that will search for extensions in + // the given DescriptorPool. + DynamicMessageFactory(const DescriptorPool* pool); + ~DynamicMessageFactory(); + + // implements MessageFactory --------------------------------------- + + // Given a Descriptor, constructs the default (prototype) Message of that + // type. You can then call that message's New() method to construct a + // mutable message of that type. + // + // Calling this method twice with the same Descriptor returns the same + // object. The returned object remains property of the factory and will + // be destroyed when the factory is destroyed. Also, any objects created + // by calling the prototype's New() method share some data with the + // prototype, so these must be destoyed before the DynamicMessageFactory + // is destroyed. + // + // The given descriptor must outlive the returned message, and hence must + // outlive the DynamicMessageFactory. + // + // Note that while GetPrototype() is idempotent, it is not const. This + // implies that it is not thread-safe to call GetPrototype() on the same + // DynamicMessageFactory in two different threads simultaneously. However, + // the returned objects are just as thread-safe as any other Message. + const Message* GetPrototype(const Descriptor* type); + + private: + const DescriptorPool* pool_; + + // This struct just contains a hash_map. We can't #include from + // this header due to hacks needed for hash_map portability in the open source + // release. Namely, stubs/hash.h, which defines hash_map portably, is not a + // public header (for good reason), but dynamic_message.h is, and public + // headers may only #include other public headers. + struct PrototypeMap; + scoped_ptr prototypes_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DynamicMessageFactory); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_DYNAMIC_MESSAGE_H__ + diff --git a/src/google/protobuf/dynamic_message_unittest.cc b/src/google/protobuf/dynamic_message_unittest.cc new file mode 100644 index 00000000..f7ed51f4 --- /dev/null +++ b/src/google/protobuf/dynamic_message_unittest.cc @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Since the reflection interface for DynamicMessage is implemented by +// GenericMessageReflection, the only thing we really have to test is +// that DynamicMessage correctly sets up the information that +// GenericMessageReflection needs to use. So, we focus on that in this +// test. Other tests, such as generic_message_reflection_unittest and +// reflection_ops_unittest, cover the rest of the functionality used by +// DynamicMessage. + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { + +class DynamicMessageTest : public testing::Test { + protected: + DescriptorPool pool_; + DynamicMessageFactory factory_; + const Descriptor* descriptor_; + const Message* prototype_; + const Descriptor* extensions_descriptor_; + const Message* extensions_prototype_; + + DynamicMessageTest(): factory_(&pool_) {} + + virtual void SetUp() { + // We want to make sure that DynamicMessage works (particularly with + // extensions) even if we use descriptors that are *not* from compiled-in + // types, so we make copies of the descriptors for unittest.proto and + // unittest_import.proto. + FileDescriptorProto unittest_file; + FileDescriptorProto unittest_import_file; + + unittest::TestAllTypes::descriptor()->file()->CopyTo(&unittest_file); + unittest_import::ImportMessage::descriptor()->file()->CopyTo( + &unittest_import_file); + + ASSERT_TRUE(pool_.BuildFile(unittest_import_file) != NULL); + ASSERT_TRUE(pool_.BuildFile(unittest_file) != NULL); + + descriptor_ = pool_.FindMessageTypeByName("protobuf_unittest.TestAllTypes"); + ASSERT_TRUE(descriptor_ != NULL); + prototype_ = factory_.GetPrototype(descriptor_); + + extensions_descriptor_ = + pool_.FindMessageTypeByName("protobuf_unittest.TestAllExtensions"); + ASSERT_TRUE(extensions_descriptor_ != NULL); + extensions_prototype_ = factory_.GetPrototype(extensions_descriptor_); + } +}; + +TEST_F(DynamicMessageTest, Descriptor) { + // Check that the descriptor on the DynamicMessage matches the descriptor + // passed to GetPrototype(). + EXPECT_EQ(prototype_->GetDescriptor(), descriptor_); +} + +TEST_F(DynamicMessageTest, OnePrototype) { + // Check that requesting the same prototype twice produces the same object. + EXPECT_EQ(prototype_, factory_.GetPrototype(descriptor_)); +} + +TEST_F(DynamicMessageTest, Defaults) { + // Check that all default values are set correctly in the initial message. + TestUtil::ReflectionTester reflection_tester(descriptor_); + reflection_tester.ExpectClearViaReflection(*prototype_); +} + +TEST_F(DynamicMessageTest, IndependentOffsets) { + // Check that all fields have independent offsets by setting each + // one to a unique value then checking that they all still have those + // unique values (i.e. they don't stomp each other). + scoped_ptr message(prototype_->New()); + TestUtil::ReflectionTester reflection_tester(descriptor_); + + reflection_tester.SetAllFieldsViaReflection(message.get()); + reflection_tester.ExpectAllFieldsSetViaReflection(*message); +} + +TEST_F(DynamicMessageTest, Extensions) { + // Check that extensions work. + scoped_ptr message(extensions_prototype_->New()); + TestUtil::ReflectionTester reflection_tester(extensions_descriptor_); + + reflection_tester.SetAllFieldsViaReflection(message.get()); + reflection_tester.ExpectAllFieldsSetViaReflection(*message); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/extension_set.cc b/src/google/protobuf/extension_set.cc new file mode 100644 index 00000000..f679d7ae --- /dev/null +++ b/src/google/protobuf/extension_set.cc @@ -0,0 +1,703 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +// ------------------------------------------------------------------- +// Lookup functions + +const FieldDescriptor* +ExtensionSet::FindKnownExtensionOrDie(int number) const { + const FieldDescriptor* descriptor = + descriptor_pool_->FindExtensionByNumber(*extendee_, number); + if (descriptor == NULL) { + // This extension doesn't exist, so we have to crash. However, let's + // try to provide an informative error message. + if (descriptor_pool_ == DescriptorPool::generated_pool() && + message_factory_ == MessageFactory::generated_factory()) { + // This is probably the ExtensionSet for a generated class. + GOOGLE_LOG(FATAL) << ": No extension is registered for \"" + << (*extendee_)->full_name() << "\" with number " + << number << ". Perhaps you were trying to access it via " + "the Reflection interface, but you provided a " + "FieldDescriptor which did not come from a linked-in " + "message type? This is not permitted; linkin-in message " + "types cannot use non-linked-in extensions. Try " + "converting to a DynamicMessage first."; + } else { + // This is probably a DynamicMessage. + GOOGLE_LOG(FATAL) << ": No extension is registered for \"" + << (*extendee_)->full_name() << "\" with number " + << number << ". If you were using a DynamicMessage, " + "remember that you are only allowed to access extensions " + "which are defined in the DescriptorPool which you passed " + "to DynamicMessageFactory's constructor."; + } + } + return descriptor; +} + +const Message* +ExtensionSet::GetPrototype(const Descriptor* message_type) const { + return message_factory_->GetPrototype(message_type); +} + +// =================================================================== +// Constructors and basic methods. + +ExtensionSet::ExtensionSet(const Descriptor* const* extendee, + const DescriptorPool* pool, + MessageFactory* factory) + : extendee_(extendee), + descriptor_pool_(pool), + message_factory_(factory) { +} + +ExtensionSet::~ExtensionSet() { + for (map::iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + iter->second.Free(); + } +} + +void ExtensionSet::AppendToList(vector* output) const { + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + bool has = false; + if (iter->second.descriptor->is_repeated()) { + has = iter->second.GetSize() > 0; + } else { + has = !iter->second.is_cleared; + } + + if (has) { + output->push_back(iter->second.descriptor); + } + } +} + +bool ExtensionSet::Has(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return false; + GOOGLE_DCHECK(!iter->second.descriptor->is_repeated()); + return !iter->second.is_cleared; +} + +int ExtensionSet::ExtensionSize(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return false; + return iter->second.GetSize(); +} + +void ExtensionSet::ClearExtension(int number) { + map::iterator iter = extensions_.find(number); + if (iter == extensions_.end()) return; + iter->second.Clear(); +} + +// =================================================================== +// Field accessors + +#define GOOGLE_DCHECK_TYPE(DESCRIPTOR, LABEL, CPPTYPE) \ + GOOGLE_DCHECK_EQ(DESCRIPTOR->label(), FieldDescriptor::LABEL_##LABEL); \ + GOOGLE_DCHECK_EQ(DESCRIPTOR->cpp_type(), FieldDescriptor::CPPTYPE_##CPPTYPE) + +// ------------------------------------------------------------------- +// Primitives + +#define PRIMITIVE_ACCESSORS(UPPERCASE, LOWERCASE, CAMELCASE) \ + \ +LOWERCASE ExtensionSet::Get##CAMELCASE(int number) const { \ + map::const_iterator iter = extensions_.find(number); \ + if (iter == extensions_.end()) { \ + /* Not present. Return the default value. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, UPPERCASE); \ + return descriptor->default_value_##LOWERCASE(); \ + } else { \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, UPPERCASE); \ + return iter->second.LOWERCASE##_value; \ + } \ +} \ + \ +void ExtensionSet::Set##CAMELCASE(int number, LOWERCASE value) { \ + Extension* extension = &extensions_[number]; \ + if (extension->descriptor == NULL) { \ + /* Not previoulsy present. Initialize it. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, UPPERCASE); \ + extension->descriptor = descriptor; \ + extension->LOWERCASE##_value = descriptor->default_value_##LOWERCASE(); \ + } else { \ + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, UPPERCASE); \ + extension->is_cleared = false; \ + } \ + extension->LOWERCASE##_value = value; \ +} \ + \ +LOWERCASE ExtensionSet::GetRepeated##CAMELCASE(int number, int index) const { \ + map::const_iterator iter = extensions_.find(number); \ + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, UPPERCASE); \ + return iter->second.repeated_##LOWERCASE##_value->Get(index); \ +} \ + \ +void ExtensionSet::SetRepeated##CAMELCASE( \ + int number, int index, LOWERCASE value) { \ + map::iterator iter = extensions_.find(number); \ + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; \ + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, UPPERCASE); \ + iter->second.repeated_##LOWERCASE##_value->Set(index, value); \ +} \ + \ +void ExtensionSet::Add##CAMELCASE(int number, LOWERCASE value) { \ + Extension* extension = &extensions_[number]; \ + if (extension->descriptor == NULL) { \ + /* Not previoulsy present. Initialize it. */ \ + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); \ + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, UPPERCASE); \ + extension->repeated_##LOWERCASE##_value = new RepeatedField(); \ + extension->descriptor = descriptor; \ + } else { \ + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, UPPERCASE); \ + } \ + extension->repeated_##LOWERCASE##_value->Add(value); \ +} + +PRIMITIVE_ACCESSORS( INT32, int32, Int32) +PRIMITIVE_ACCESSORS( INT64, int64, Int64) +PRIMITIVE_ACCESSORS(UINT32, uint32, UInt32) +PRIMITIVE_ACCESSORS(UINT64, uint64, UInt64) +PRIMITIVE_ACCESSORS( FLOAT, float, Float) +PRIMITIVE_ACCESSORS(DOUBLE, double, Double) +PRIMITIVE_ACCESSORS( BOOL, bool, Bool) + +#undef PRIMITIVE_ACCESSORS + +// ------------------------------------------------------------------- +// Enums + +int ExtensionSet::GetEnum(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, ENUM); + return descriptor->default_value_enum()->number(); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, ENUM); + return iter->second.enum_value; + } +} + +void ExtensionSet::SetEnum(int number, int value) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, ENUM); + extension->descriptor = descriptor; + extension->enum_value = descriptor->default_value_enum()->number(); + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, ENUM); + extension->is_cleared = false; + } + GOOGLE_DCHECK(extension->descriptor->enum_type()->FindValueByNumber(value) != NULL); + extension->enum_value = value; +} + +int ExtensionSet::GetRepeatedEnum(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, ENUM); + return iter->second.repeated_enum_value->Get(index); +} + +void ExtensionSet::SetRepeatedEnum(int number, int index, int value) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, ENUM); + GOOGLE_DCHECK(iter->second.descriptor->enum_type() + ->FindValueByNumber(value) != NULL); + iter->second.repeated_enum_value->Set(index, value); +} + +void ExtensionSet::AddEnum(int number, int value) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, ENUM); + extension->repeated_enum_value = new RepeatedField(); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, ENUM); + } + GOOGLE_DCHECK(extension->descriptor->enum_type()->FindValueByNumber(value) != NULL); + extension->repeated_enum_value->Add(value); +} + +// ------------------------------------------------------------------- +// Strings + +const string& ExtensionSet::GetString(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, STRING); + return descriptor->default_value_string(); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, STRING); + return *iter->second.string_value; + } +} + +string* ExtensionSet::MutableString(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, STRING); + extension->descriptor = descriptor; + extension->string_value = new string; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, STRING); + extension->is_cleared = false; + } + return extension->string_value; +} + +const string& ExtensionSet::GetRepeatedString(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, STRING); + return iter->second.repeated_string_value->Get(index); +} + +string* ExtensionSet::MutableRepeatedString(int number, int index) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, STRING); + return iter->second.repeated_string_value->Mutable(index); +} + +string* ExtensionSet::AddString(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, STRING); + extension->repeated_string_value = new RepeatedPtrField(); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, STRING); + } + return extension->repeated_string_value->Add(); +} + +// ------------------------------------------------------------------- +// Messages + +const Message& ExtensionSet::GetMessage(int number) const { + map::const_iterator iter = extensions_.find(number); + if (iter == extensions_.end()) { + // Not present. Return the default value. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, MESSAGE); + return *GetPrototype(descriptor->message_type()); + } else { + GOOGLE_DCHECK_TYPE(iter->second.descriptor, OPTIONAL, MESSAGE); + return *iter->second.message_value; + } +} + +Message* ExtensionSet::MutableMessage(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, OPTIONAL, MESSAGE); + extension->descriptor = descriptor; + extension->message_value = GetPrototype(descriptor->message_type())->New(); + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, OPTIONAL, MESSAGE); + extension->is_cleared = false; + } + return extension->message_value; +} + +const Message& ExtensionSet::GetRepeatedMessage(int number, int index) const { + map::const_iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, MESSAGE); + return iter->second.repeated_message_value->Get(index); +} + +Message* ExtensionSet::MutableRepeatedMessage(int number, int index) { + map::iterator iter = extensions_.find(number); + GOOGLE_CHECK(iter != extensions_.end()) << "Index out-of-bounds (field is empty)."; + GOOGLE_DCHECK_TYPE(iter->second.descriptor, REPEATED, MESSAGE); + return iter->second.repeated_message_value->Mutable(index); +} + +Message* ExtensionSet::AddMessage(int number) { + Extension* extension = &extensions_[number]; + if (extension->descriptor == NULL) { + // Not previoulsy present. Initialize it. + const FieldDescriptor* descriptor = FindKnownExtensionOrDie(number); + GOOGLE_DCHECK_TYPE(descriptor, REPEATED, MESSAGE); + extension->repeated_message_value = + new RepeatedPtrField(GetPrototype(descriptor->message_type())); + extension->descriptor = descriptor; + } else { + GOOGLE_DCHECK_TYPE(extension->descriptor, REPEATED, MESSAGE); + } + return extension->repeated_message_value->Add(); +} + +#undef GOOGLE_DCHECK_TYPE + +// =================================================================== + +void ExtensionSet::Clear() { + for (map::iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + iter->second.Clear(); + } +} + +namespace { + +// A helper function for merging RepeatedFields... +// TODO(kenton): Implement this as a method of RepeatedField? Make generated +// MergeFrom methods use it? + +template +void MergeRepeatedFields(const RepeatedField& source, + RepeatedField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add(source.Get(i)); + } +} + +void MergeRepeatedFields(const RepeatedPtrField& source, + RepeatedPtrField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add()->assign(source.Get(i)); + } +} + +void MergeRepeatedFields(const RepeatedPtrField& source, + RepeatedPtrField* destination) { + destination->Reserve(destination->size() + source.size()); + for (int i = 0; i < source.size(); i++) { + destination->Add()->MergeFrom(source.Get(i)); + } +} + +} // namespace + +void ExtensionSet::MergeFrom(const ExtensionSet& other) { + GOOGLE_DCHECK_EQ(*extendee_, *other.extendee_); + + for (map::const_iterator iter = other.extensions_.begin(); + iter != other.extensions_.end(); ++iter) { + const FieldDescriptor* field = iter->second.descriptor; + if (field->is_repeated()) { + const Extension& other_extension = iter->second; + Extension* extension = &extensions_[iter->first]; + switch (field->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE, REPEATED_TYPE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + if (extension->descriptor == NULL) { \ + extension->descriptor = field; \ + extension->repeated_##LOWERCASE##_value = \ + new REPEATED_TYPE; \ + } \ + MergeRepeatedFields( \ + *other_extension.repeated_##LOWERCASE##_value, \ + extension->repeated_##LOWERCASE##_value); \ + break; + + HANDLE_TYPE( INT32, int32, RepeatedField < int32>); + HANDLE_TYPE( INT64, int64, RepeatedField < int64>); + HANDLE_TYPE( UINT32, uint32, RepeatedField < uint32>); + HANDLE_TYPE( UINT64, uint64, RepeatedField < uint64>); + HANDLE_TYPE( FLOAT, float, RepeatedField < float>); + HANDLE_TYPE( DOUBLE, double, RepeatedField < double>); + HANDLE_TYPE( BOOL, bool, RepeatedField < bool>); + HANDLE_TYPE( ENUM, enum, RepeatedField < int>); + HANDLE_TYPE( STRING, string, RepeatedPtrField< string>); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + if (extension->descriptor == NULL) { + extension->descriptor = field; + extension->repeated_message_value = new RepeatedPtrField( + other_extension.repeated_message_value->prototype()); + } + MergeRepeatedFields( + *other_extension.repeated_message_value, + extension->repeated_message_value); + break; + } + } else { + if (!iter->second.is_cleared) { + switch (field->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE, CAMELCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + Set##CAMELCASE(iter->first, iter->second.LOWERCASE##_value); \ + break; + + HANDLE_TYPE( INT32, int32, Int32); + HANDLE_TYPE( INT64, int64, Int64); + HANDLE_TYPE(UINT32, uint32, UInt32); + HANDLE_TYPE(UINT64, uint64, UInt64); + HANDLE_TYPE( FLOAT, float, Float); + HANDLE_TYPE(DOUBLE, double, Double); + HANDLE_TYPE( BOOL, bool, Bool); + HANDLE_TYPE( ENUM, enum, Enum); +#undef HANDLE_TYPE + case FieldDescriptor::CPPTYPE_STRING: + SetString(iter->first, *iter->second.string_value); + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + MutableMessage(iter->first)->MergeFrom(*iter->second.message_value); + break; + } + } + } + } +} + +bool ExtensionSet::IsInitialized() const { + // Extensions are never requried. However, we need to check that all + // embedded messages are initialized. + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + const Extension& extension = iter->second; + if (extension.descriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (extension.descriptor->is_repeated()) { + for (int i = 0; i < extension.repeated_message_value->size(); i++) { + if (!extension.repeated_message_value->Get(i).IsInitialized()) { + return false; + } + } + } else { + if (!extension.is_cleared) { + if (!extension.message_value->IsInitialized()) return false; + } + } + } + } + + return true; +} + +bool ExtensionSet::ParseField(uint32 tag, io::CodedInputStream* input, + Message* message) { + const FieldDescriptor* field = + message->GetReflection() + ->FindKnownExtensionByNumber(WireFormat::GetTagFieldNumber(tag)); + + return WireFormat::ParseAndMergeField(tag, field, message, input); +} + +bool ExtensionSet::SerializeWithCachedSizes( + int start_field_number, int end_field_number, + const Message& message, + io::CodedOutputStream* output) const { + map::const_iterator iter; + for (iter = extensions_.lower_bound(start_field_number); + iter != extensions_.end() && iter->first < end_field_number; + ++iter) { + if (!iter->second.SerializeFieldWithCachedSizes(message, output)) { + return false; + } + } + + return true; +} + +int ExtensionSet::ByteSize(const Message& message) const { + int total_size = 0; + + for (map::const_iterator iter = extensions_.begin(); + iter != extensions_.end(); ++iter) { + total_size += iter->second.ByteSize(message); + } + + return total_size; +} + +// =================================================================== +// Methods of ExtensionSet::Extension + +void ExtensionSet::Extension::Clear() { + if (descriptor->is_repeated()) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + repeated_##LOWERCASE##_value->Clear(); \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + } else { + if (!is_cleared) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + LOWERCASE##_value = descriptor->default_value_##LOWERCASE(); \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE(UINT32, uint32); + HANDLE_TYPE(UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE(DOUBLE, double); + HANDLE_TYPE( BOOL, bool); +#undef HANDLE_TYPE + case FieldDescriptor::CPPTYPE_ENUM: + enum_value = descriptor->default_value_enum()->number(); + break; + case FieldDescriptor::CPPTYPE_STRING: + if (descriptor->has_default_value()) { + string_value->assign(descriptor->default_value_string()); + } else { + string_value->clear(); + } + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + message_value->Clear(); + break; + } + + is_cleared = true; + } + } +} + +bool ExtensionSet::Extension::SerializeFieldWithCachedSizes( + const Message& message, + io::CodedOutputStream* output) const { + if (descriptor->is_repeated() || !is_cleared) { + return WireFormat::SerializeFieldWithCachedSizes( + descriptor, message, output); + } else { + return true; + } +} + +int64 ExtensionSet::Extension::ByteSize(const Message& message) const { + if (descriptor->is_repeated() || !is_cleared) { + return WireFormat::FieldByteSize(descriptor, message); + } else { + // Cleared, non-repeated field. + return 0; + } +} + +int ExtensionSet::Extension::GetSize() const { + GOOGLE_DCHECK(descriptor->is_repeated()); + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + return repeated_##LOWERCASE##_value->size() + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + return 0; +} + +void ExtensionSet::Extension::Free() { + if (descriptor->is_repeated()) { + switch (descriptor->cpp_type()) { +#define HANDLE_TYPE(UPPERCASE, LOWERCASE) \ + case FieldDescriptor::CPPTYPE_##UPPERCASE: \ + delete repeated_##LOWERCASE##_value; \ + break + + HANDLE_TYPE( INT32, int32); + HANDLE_TYPE( INT64, int64); + HANDLE_TYPE( UINT32, uint32); + HANDLE_TYPE( UINT64, uint64); + HANDLE_TYPE( FLOAT, float); + HANDLE_TYPE( DOUBLE, double); + HANDLE_TYPE( BOOL, bool); + HANDLE_TYPE( ENUM, enum); + HANDLE_TYPE( STRING, string); + HANDLE_TYPE(MESSAGE, message); +#undef HANDLE_TYPE + } + } else { + switch (descriptor->cpp_type()) { + case FieldDescriptor::CPPTYPE_STRING: + delete string_value; + break; + case FieldDescriptor::CPPTYPE_MESSAGE: + delete message_value; + break; + default: + break; + } + } +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/extension_set.h b/src/google/protobuf/extension_set.h new file mode 100644 index 00000000..c8e124f8 --- /dev/null +++ b/src/google/protobuf/extension_set.h @@ -0,0 +1,556 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_EXTENSION_SET_H__ +#define GOOGLE_PROTOBUF_EXTENSION_SET_H__ + +#include +#include +#include +#include +#include + +#include + +namespace google { +namespace protobuf { + class Descriptor; // descriptor.h + class FieldDescriptor; // descriptor.h + class DescriptorPool; // descriptor.h + class Message; // message.h + class MessageFactory; // message.h + namespace io { + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h + } + template class RepeatedField; // repeated_field.h + template class RepeatedPtrField; // repeated_field.h +} + +namespace protobuf { +namespace internal { + +// This is an internal helper class intended for use within the protocol buffer +// library and generated classes. Clients should not use it directly. Instead, +// use the generated accessors such as GetExtension() of the class being +// extended. +// +// This class manages extensions for a protocol message object. The +// message's HasExtension(), GetExtension(), MutableExtension(), and +// ClearExtension() methods are just thin wrappers around the embedded +// ExtensionSet. When parsing, if a tag number is encountered which is +// inside one of the message type's extension ranges, the tag is passed +// off to the ExtensionSet for parsing. Etc. +class LIBPROTOBUF_EXPORT ExtensionSet { + public: + // Construct an ExtensionSet. + // extendee: Descriptor for the type being extended. We pass in a pointer + // to a pointer to the extendee to get around an initialization + // problem: when we create the ExtensionSet for a message type, + // its descriptor may not exist yet. But we know where that + // descriptor pointer will be placed, and by the time it's used + // by this ExtensionSet it will be fully initialized, so passing + // a pointer to that location works. Note that this problem + // will only occur for messages defined in descriptor.proto. + // pool: DescriptorPool to search for extension definitions. + // factory: MessageFactory used to construct implementations of messages + // for extensions with message type. This factory must be able + // to construct any message type found in "pool". + // All three objects remain property of the caller and must outlive the + // ExtensionSet. + ExtensionSet(const Descriptor* const* extendee, + const DescriptorPool* pool, + MessageFactory* factory); + + ~ExtensionSet(); + + // Add all fields which are currently present to the given vector. This + // is useful to implement Reflection::ListFields(). + void AppendToList(vector* output) const; + + // ================================================================= + // Accessors + // + // Generated message classes include type-safe templated wrappers around + // these methods. Generally you should use those rather than call these + // directly, unless you are doing low-level memory management. + // + // When calling any of these accessors, the extension number requested + // MUST exist in the DescriptorPool provided to the constructor. Otheriwse, + // the method will fail an assert. Normally, though, you would not call + // these directly; you would either call the generated accessors of your + // message class (e.g. GetExtension()) or you would call the accessors + // of the reflection interface. In both cases, it is impossible to + // trigger this assert failure: the generated accessors only accept + // linked-in extension types as parameters, while the Reflection interface + // requires you to provide the FieldDescriptor describing the extension. + // + // When calling any of these accessors, a protocol-compiler-generated + // implementation of the extension corresponding to the number MUST + // be linked in, and the FieldDescriptor used to refer to it MUST be + // the one generated by that linked-in code. Otherwise, the method will + // die on an assert failure. The message objects returned by the message + // accessors are guaranteed to be of the correct linked-in type. + // + // These methods pretty much match Reflection except that: + // - They're not virtual. + // - They identify fields by number rather than FieldDescriptors. + // - They identify enum values using integers rather than descriptors. + // - Strings provide Mutable() in addition to Set() accessors. + + bool Has(int number) const; + int ExtensionSize(int number) const; // Size of a repeated extension. + void ClearExtension(int number); + + // singular fields ------------------------------------------------- + + int32 GetInt32 (int number) const; + int64 GetInt64 (int number) const; + uint32 GetUInt32(int number) const; + uint64 GetUInt64(int number) const; + float GetFloat (int number) const; + double GetDouble(int number) const; + bool GetBool (int number) const; + int GetEnum (int number) const; + const string & GetString (int number) const; + const Message& GetMessage(int number) const; + + void SetInt32 (int number, int32 value); + void SetInt64 (int number, int64 value); + void SetUInt32(int number, uint32 value); + void SetUInt64(int number, uint64 value); + void SetFloat (int number, float value); + void SetDouble(int number, double value); + void SetBool (int number, bool value); + void SetEnum (int number, int value); + void SetString(int number, const string& value); + string * MutableString (int number); + Message* MutableMessage(int number); + + // repeated fields ------------------------------------------------- + + int32 GetRepeatedInt32 (int number, int index) const; + int64 GetRepeatedInt64 (int number, int index) const; + uint32 GetRepeatedUInt32(int number, int index) const; + uint64 GetRepeatedUInt64(int number, int index) const; + float GetRepeatedFloat (int number, int index) const; + double GetRepeatedDouble(int number, int index) const; + bool GetRepeatedBool (int number, int index) const; + int GetRepeatedEnum (int number, int index) const; + const string & GetRepeatedString (int number, int index) const; + const Message& GetRepeatedMessage(int number, int index) const; + + void SetRepeatedInt32 (int number, int index, int32 value); + void SetRepeatedInt64 (int number, int index, int64 value); + void SetRepeatedUInt32(int number, int index, uint32 value); + void SetRepeatedUInt64(int number, int index, uint64 value); + void SetRepeatedFloat (int number, int index, float value); + void SetRepeatedDouble(int number, int index, double value); + void SetRepeatedBool (int number, int index, bool value); + void SetRepeatedEnum (int number, int index, int value); + void SetRepeatedString(int number, int index, const string& value); + string * MutableRepeatedString (int number, int index); + Message* MutableRepeatedMessage(int number, int index); + + void AddInt32 (int number, int32 value); + void AddInt64 (int number, int64 value); + void AddUInt32(int number, uint32 value); + void AddUInt64(int number, uint64 value); + void AddFloat (int number, float value); + void AddDouble(int number, double value); + void AddBool (int number, bool value); + void AddEnum (int number, int value); + void AddString(int number, const string& value); + string * AddString (int number); + Message* AddMessage(int number); + + // ----------------------------------------------------------------- + // TODO(kenton): Hardcore memory management accessors + + // ================================================================= + // convenience methods for implementing methods of Message + // + // These could all be implemented in terms of the other methods of this + // class, but providing them here helps keep the generated code size down. + + void Clear(); + void MergeFrom(const ExtensionSet& other); + bool IsInitialized() const; + + // These parsing and serialization functions all want a pointer to the + // message object because they hand off the actual work to WireFormat, + // which works in terms of a reflection interface. Yes, this means there + // are some redundant virtual function calls that end up being made, but + // it probably doesn't matter much in practice, and the alternative would + // involve reproducing a lot of WireFormat's functionality. + + // Parses a single extension from the input. The input should start out + // positioned immediately after the tag. + bool ParseField(uint32 tag, io::CodedInputStream* input, Message* message); + + // Write all extension fields with field numbers in the range + // [start_field_number, end_field_number) + // to the output stream, using the cached sizes computed when ByteSize() was + // last called. Note that the range bounds are inclusive-exclusive. + bool SerializeWithCachedSizes(int start_field_number, + int end_field_number, + const Message& message, + io::CodedOutputStream* output) const; + + // Returns the total serialized size of all the extensions. + int ByteSize(const Message& message) const; + + private: + // Like FindKnownExtension(), but GOOGLE_CHECK-fail if not found. + const FieldDescriptor* FindKnownExtensionOrDie(int number) const; + + // Get the prototype for the message. + const Message* GetPrototype(const Descriptor* message_type) const; + + struct Extension { + union { + int32 int32_value; + int64 int64_value; + uint32 uint32_value; + uint64 uint64_value; + float float_value; + double double_value; + bool bool_value; + int enum_value; + string* string_value; + Message* message_value; + + RepeatedField * repeated_int32_value; + RepeatedField * repeated_int64_value; + RepeatedField * repeated_uint32_value; + RepeatedField * repeated_uint64_value; + RepeatedField * repeated_float_value; + RepeatedField * repeated_double_value; + RepeatedField * repeated_bool_value; + RepeatedField * repeated_enum_value; + RepeatedPtrField* repeated_string_value; + RepeatedPtrField* repeated_message_value; + }; + + const FieldDescriptor* descriptor; + + // For singular types, indicates if the extension is "cleared". This + // happens when an extension is set and then later cleared by the caller. + // We want to keep the Extension object around for reuse, so instead of + // removing it from the map, we just set is_cleared = true. This has no + // meaning for repeated types; for those, the size of the RepeatedField + // simply becomes zero when cleared. + bool is_cleared; + + Extension(): descriptor(NULL), is_cleared(false) {} + + // Some helper methods for operations on a single Extension. + bool SerializeFieldWithCachedSizes( + const Message& message, + io::CodedOutputStream* output) const; + int64 ByteSize(const Message& message) const; + void Clear(); + int GetSize() const; + void Free(); + }; + + // The Extension struct is small enough to be passed by value, so we use it + // directly as the value type in the map rather than use pointers. We use + // a map rather than hash_map here because we expect most ExtensionSets will + // only contain a small number of extensions whereas hash_map is optimized + // for 100 elements or more. Also, we want AppendToList() to order fields + // by field number. + map extensions_; + const Descriptor* const* extendee_; + const DescriptorPool* descriptor_pool_; + MessageFactory* message_factory_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ExtensionSet); +}; + +// These are just for convenience... +inline void ExtensionSet::SetString(int number, const string& value) { + MutableString(number)->assign(value); +} +inline void ExtensionSet::SetRepeatedString(int number, int index, + const string& value) { + MutableRepeatedString(number, index)->assign(value); +} +inline void ExtensionSet::AddString(int number, const string& value) { + AddString(number)->assign(value); +} + +// =================================================================== +// Implementation details +// +// DO NOT DEPEND ON ANYTHING BELOW THIS POINT. This is for use from +// generated code only. + +// ------------------------------------------------------------------- +// Template magic + +// First we have a set of classes representing "type traits" for different +// field types. A type traits class knows how to implement basic accessors +// for extensions of a particular type given an ExtensionSet. The signature +// for a type traits class looks like this: +// +// class TypeTraits { +// public: +// typedef ? ConstType; +// typedef ? MutableType; +// +// static inline ConstType Get(int number, const ExtensionSet& set); +// static inline void Set(int number, ConstType value, ExtensionSet* set); +// static inline MutableType Mutable(int number, ExtensionSet* set); +// +// // Variants for repeated fields. +// static inline ConstType Get(int number, const ExtensionSet& set, +// int index); +// static inline void Set(int number, int index, +// ConstType value, ExtensionSet* set); +// static inline MutableType Mutable(int number, int index, +// ExtensionSet* set); +// static inline void Add(int number, ConstType value, ExtensionSet* set); +// static inline MutableType Add(int number, ExtensionSet* set); +// }; +// +// Not all of these methods make sense for all field types. For example, the +// "Mutable" methods only make sense for strings and messages, and the +// repeated methods only make sense for repeated types. So, each type +// traits class implements only the set of methods from this signature that it +// actually supports. This will cause a compiler error if the user tries to +// access an extension using a method that doesn't make sense for its type. +// For example, if "foo" is an extension of type "optional int32", then if you +// try to write code like: +// my_message.MutableExtension(foo) +// you will get a compile error because PrimitiveTypeTraits does not +// have a "Mutable()" method. + +// ------------------------------------------------------------------- +// PrimitiveTypeTraits + +// Since the ExtensionSet has different methods for each primitive type, +// we must explicitly define the methods of the type traits class for each +// known type. +template +class PrimitiveTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set); + static inline void Set(int number, ConstType value, ExtensionSet* set); +}; + +template +class RepeatedPrimitiveTypeTraits { + public: + typedef Type ConstType; + + static inline Type Get(int number, const ExtensionSet& set, int index); + static inline void Set(int number, int index, Type value, ExtensionSet* set); + static inline void Add(int number, Type value, ExtensionSet* set); +}; + +#define PROTOBUF_DEFINE_PRIMITIVE_TYPE(TYPE, METHOD) \ +template<> inline TYPE PrimitiveTypeTraits::Get( \ + int number, const ExtensionSet& set) { \ + return set.Get##METHOD(number); \ +} \ +template<> inline void PrimitiveTypeTraits::Set( \ + int number, ConstType value, ExtensionSet* set) { \ + set->Set##METHOD(number, value); \ +} \ + \ +template<> inline TYPE RepeatedPrimitiveTypeTraits::Get( \ + int number, const ExtensionSet& set, int index) { \ + return set.GetRepeated##METHOD(number, index); \ +} \ +template<> inline void RepeatedPrimitiveTypeTraits::Set( \ + int number, int index, ConstType value, ExtensionSet* set) { \ + set->SetRepeated##METHOD(number, index, value); \ +} \ +template<> inline void RepeatedPrimitiveTypeTraits::Add( \ + int number, ConstType value, ExtensionSet* set) { \ + set->Add##METHOD(number, value); \ +} + +PROTOBUF_DEFINE_PRIMITIVE_TYPE( int32, Int32) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( int64, Int64) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint32, UInt32) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(uint64, UInt64) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( float, Float) +PROTOBUF_DEFINE_PRIMITIVE_TYPE(double, Double) +PROTOBUF_DEFINE_PRIMITIVE_TYPE( bool, Bool) + +#undef PROTOBUF_DEFINE_PRIMITIVE_TYPE + +// ------------------------------------------------------------------- +// StringTypeTraits + +// Strings support both Set() and Mutable(). +class LIBPROTOBUF_EXPORT StringTypeTraits { + public: + typedef const string& ConstType; + typedef string* MutableType; + + static inline const string& Get(int number, const ExtensionSet& set) { + return set.GetString(number); + } + static inline void Set(int number, const string& value, ExtensionSet* set) { + set->SetString(number, value); + } + static inline string* Mutable(int number, ExtensionSet* set) { + return set->MutableString(number); + } +}; + +class LIBPROTOBUF_EXPORT RepeatedStringTypeTraits { + public: + typedef const string& ConstType; + typedef string* MutableType; + + static inline const string& Get(int number, const ExtensionSet& set, + int index) { + return set.GetRepeatedString(number, index); + } + static inline void Set(int number, int index, + const string& value, ExtensionSet* set) { + set->SetRepeatedString(number, index, value); + } + static inline string* Mutable(int number, int index, ExtensionSet* set) { + return set->MutableRepeatedString(number, index); + } + static inline void Add(int number, const string& value, ExtensionSet* set) { + set->AddString(number, value); + } + static inline string* Add(int number, ExtensionSet* set) { + return set->AddString(number); + } +}; + +// ------------------------------------------------------------------- +// EnumTypeTraits + +// ExtensionSet represents enums using integers internally, so we have to +// static_cast around. +template +class EnumTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set) { + return static_cast(set.GetEnum(number)); + } + static inline void Set(int number, ConstType value, ExtensionSet* set) { + set->SetEnum(number, value); + } +}; + +template +class RepeatedEnumTypeTraits { + public: + typedef Type ConstType; + + static inline ConstType Get(int number, const ExtensionSet& set, int index) { + return static_cast(set.GetRepeatedEnum(number, index)); + } + static inline void Set(int number, int index, + ConstType value, ExtensionSet* set) { + set->SetRepeatedEnum(number, index, value); + } + static inline void Add(int number, ConstType value, ExtensionSet* set) { + set->AddEnum(number, value); + } +}; + +// ------------------------------------------------------------------- +// MessageTypeTraits + +// ExtensionSet guarantees that when manipulating extensions with message +// types, the implementation used will be the compiled-in class representing +// that type. So, we can static_cast down to the exact type we expect. +template +class MessageTypeTraits { + public: + typedef const Type& ConstType; + typedef Type* MutableType; + + static inline ConstType Get(int number, const ExtensionSet& set) { + return static_cast(set.GetMessage(number)); + } + static inline MutableType Mutable(int number, ExtensionSet* set) { + return static_cast(set->MutableMessage(number)); + } +}; + +template +class RepeatedMessageTypeTraits { + public: + typedef const Type& ConstType; + typedef Type* MutableType; + + static inline ConstType Get(int number, const ExtensionSet& set, int index) { + return static_cast(set.GetRepeatedMessage(number, index)); + } + static inline MutableType Mutable(int number, int index, ExtensionSet* set) { + return static_cast(set->MutableRepeatedMessage(number, index)); + } + static inline MutableType Add(int number, ExtensionSet* set) { + return static_cast(set->AddMessage(number)); + } +}; + +// ------------------------------------------------------------------- +// ExtensionIdentifier + +// This is the type of actual extension objects. E.g. if you have: +// extends Foo with optional int32 bar = 1234; +// then "bar" will be defined in C++ as: +// ExtensionIdentifier> bar(1234); +// +// Note that we could, in theory, supply the field number as a template +// parameter, and thus make an instance of ExtensionIdentifier have no +// actual contents. However, if we did that, then using at extension +// identifier would not necessarily cause the compiler to output any sort +// of reference to any simple defined in the extension's .pb.o file. Some +// linkers will actually drop object files that are not explicitly referenced, +// but that would be bad because it would cause this extension to not be +// registered at static initialization, and therefore using it would crash. + +template +class ExtensionIdentifier { + public: + typedef TypeTraitsType TypeTraits; + typedef ExtendeeType Extendee; + + ExtensionIdentifier(int number): number_(number) {} + inline int number() const { return number_; } + private: + const int number_; +}; + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_EXTENSION_SET_H__ diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc new file mode 100644 index 00000000..c10f8900 --- /dev/null +++ b/src/google/protobuf/extension_set_unittest.cc @@ -0,0 +1,195 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +// This test closely mirrors google/protobuf/compiler/cpp/unittest.cc +// except that it uses extensions rather than regular fields. + +TEST(ExtensionSetTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllExtensions message; + + TestUtil::ExpectExtensionsClear(message); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ExtensionSetTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + TestUtil::ExpectAllExtensionsSet(message); + + TestUtil::ModifyRepeatedExtensions(&message); + TestUtil::ExpectRepeatedExtensionsModified(message); +} + +TEST(ExtensionSetTest, Clear) { + // Set every field to a unique value, clear the message, then check that + // it is cleared. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + message.Clear(); + TestUtil::ExpectExtensionsClear(message); + + // Unlike with the defaults test, we do NOT expect that requesting embedded + // messages will return a pointer to the default instance. Instead, they + // should return the objects that were created when mutable_blah() was + // called. + EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ExtensionSetTest, ClearOneField) { + // Set every field to a unique value, then clear one value and insure that + // only that one value is cleared. + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + int64 original_value = + message.GetExtension(unittest::optional_int64_extension); + + // Clear the field and make sure it shows up as cleared. + message.ClearExtension(unittest::optional_int64_extension); + EXPECT_FALSE(message.HasExtension(unittest::optional_int64_extension)); + EXPECT_EQ(0, message.GetExtension(unittest::optional_int64_extension)); + + // Other adjacent fields should not be cleared. + EXPECT_TRUE(message.HasExtension(unittest::optional_int32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint32_extension)); + + // Make sure if we set it again, then all fields are set. + message.SetExtension(unittest::optional_int64_extension, original_value); + TestUtil::ExpectAllExtensionsSet(message); +} + +TEST(ExtensionSetTest, CopyFrom) { + unittest::TestAllExtensions message1, message2; + string data; + + TestUtil::SetAllExtensions(&message1); + message2.CopyFrom(message1); + TestUtil::ExpectAllExtensionsSet(message2); +} + +TEST(ExtensionSetTest, Serialization) { + // Serialize as TestAllExtensions and parse as TestAllTypes to insure wire + // compatibility of extensions. + unittest::TestAllExtensions source; + unittest::TestAllTypes destination; + string data; + + TestUtil::SetAllExtensions(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(destination); +} + +TEST(ExtensionSetTest, Parsing) { + // Serialize as TestAllTypes and parse as TestAllExtensions. + unittest::TestAllTypes source; + unittest::TestAllExtensions destination; + string data; + + TestUtil::SetAllFields(&source); + source.SerializeToString(&data); + EXPECT_TRUE(destination.ParseFromString(data)); + TestUtil::ExpectAllExtensionsSet(destination); +} + +TEST(ExtensionSetTest, IsInitialized) { + // Test that IsInitialized() returns false if required fields in nested + // extensions are missing. + unittest::TestAllExtensions message; + + EXPECT_TRUE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::single); + EXPECT_FALSE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::single)->set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::single)->set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::single)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); + + message.AddExtension(unittest::TestRequired::multi); + EXPECT_FALSE(message.IsInitialized()); + + message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2); + EXPECT_FALSE(message.IsInitialized()); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3); + EXPECT_TRUE(message.IsInitialized()); +} + +TEST(ExtensionSetTest, MutableString) { + // Test the mutable string accessors. + unittest::TestAllExtensions message; + + message.MutableExtension(unittest::optional_string_extension)->assign("foo"); + EXPECT_TRUE(message.HasExtension(unittest::optional_string_extension)); + EXPECT_EQ("foo", message.GetExtension(unittest::optional_string_extension)); + + message.AddExtension(unittest::repeated_string_extension)->assign("bar"); + ASSERT_EQ(1, message.ExtensionSize(unittest::repeated_string_extension)); + EXPECT_EQ("bar", + message.GetExtension(unittest::repeated_string_extension, 0)); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc new file mode 100644 index 00000000..65530c7e --- /dev/null +++ b/src/google/protobuf/generated_message_reflection.cc @@ -0,0 +1,756 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { const string kEmptyString; } + +// =================================================================== +// Helpers for reporting usage errors (e.g. trying to use GetInt32() on +// a string field). + +namespace { + +void ReportReflectionUsageError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, const char* description) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : " << description; +} + +const char* cpptype_names_[FieldDescriptor::MAX_CPPTYPE + 1] = { + "INVALID_CPPTYPE", + "CPPTYPE_INT32", + "CPPTYPE_INT64", + "CPPTYPE_UINT32", + "CPPTYPE_UINT64", + "CPPTYPE_DOUBLE", + "CPPTYPE_FLOAT", + "CPPTYPE_BOOL", + "CPPTYPE_ENUM", + "CPPTYPE_STRING", + "CPPTYPE_MESSAGE" +}; + +static void ReportReflectionUsageTypeError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, + FieldDescriptor::CppType expected_type) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : Field is not the right type for this message:\n" + " Expected : " << cpptype_names_[expected_type] << "\n" + " Field type: " << cpptype_names_[field->cpp_type()]; +} + +static void ReportReflectionUsageEnumTypeError( + const Descriptor* descriptor, const FieldDescriptor* field, + const char* method, const EnumValueDescriptor* value) { + GOOGLE_LOG(FATAL) + << "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::" << method << "\n" + " Message type: " << descriptor->full_name() << "\n" + " Field : " << field->full_name() << "\n" + " Problem : Enum value did not match field type:\n" + " Expected : " << field->enum_type()->full_name() << "\n" + " Actual : " << value->full_name(); +} + +#define USAGE_CHECK(CONDITION, METHOD, ERROR_DESCRIPTION) \ + if (!(CONDITION)) \ + ReportReflectionUsageError(descriptor_, field, #METHOD, ERROR_DESCRIPTION) +#define USAGE_CHECK_EQ(A, B, METHOD, ERROR_DESCRIPTION) \ + USAGE_CHECK((A) == (B), METHOD, ERROR_DESCRIPTION) +#define USAGE_CHECK_NE(A, B, METHOD, ERROR_DESCRIPTION) \ + USAGE_CHECK((A) != (B), METHOD, ERROR_DESCRIPTION) + +#define USAGE_CHECK_TYPE(METHOD, CPPTYPE) \ + if (field->cpp_type() != FieldDescriptor::CPPTYPE_##CPPTYPE) \ + ReportReflectionUsageTypeError(descriptor_, field, #METHOD, \ + FieldDescriptor::CPPTYPE_##CPPTYPE) + +#define USAGE_CHECK_ENUM_VALUE(METHOD) \ + if (value->type() != field->enum_type()) \ + ReportReflectionUsageEnumTypeError(descriptor_, field, #METHOD, value) + +#define USAGE_CHECK_MESSAGE_TYPE(METHOD) \ + USAGE_CHECK_EQ(field->containing_type(), descriptor_, \ + METHOD, "Field does not match message type."); +#define USAGE_CHECK_SINGULAR(METHOD) \ + USAGE_CHECK_NE(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \ + "Field is repeated; the method requires a singular field.") +#define USAGE_CHECK_REPEATED(METHOD) \ + USAGE_CHECK_EQ(field->label(), FieldDescriptor::LABEL_REPEATED, METHOD, \ + "Field is singular; the method requires a repeated field.") + +#define USAGE_CHECK_ALL(METHOD, LABEL, CPPTYPE) \ + USAGE_CHECK_MESSAGE_TYPE(METHOD); \ + USAGE_CHECK_##LABEL(METHOD); \ + USAGE_CHECK_TYPE(METHOD, CPPTYPE) + +} // namespace + +// =================================================================== + +GeneratedMessageReflection::GeneratedMessageReflection( + const Descriptor* descriptor, + const Message* default_instance, + const int offsets[], + int has_bits_offset, + int unknown_fields_offset, + int extensions_offset, + const DescriptorPool* descriptor_pool) + : descriptor_ (descriptor), + default_instance_ (default_instance), + offsets_ (offsets), + has_bits_offset_ (has_bits_offset), + unknown_fields_offset_(unknown_fields_offset), + extensions_offset_(extensions_offset), + descriptor_pool_ ((descriptor_pool == NULL) ? + DescriptorPool::generated_pool() : + descriptor_pool) { +} + +GeneratedMessageReflection::~GeneratedMessageReflection() {} + +const UnknownFieldSet& GeneratedMessageReflection::GetUnknownFields( + const Message& message) const { + const void* ptr = reinterpret_cast(&message) + + unknown_fields_offset_; + return *reinterpret_cast(ptr); +} +UnknownFieldSet* GeneratedMessageReflection::MutableUnknownFields( + Message* message) const { + void* ptr = reinterpret_cast(message) + unknown_fields_offset_; + return reinterpret_cast(ptr); +} + +// ------------------------------------------------------------------- + +bool GeneratedMessageReflection::HasField(const Message& message, + const FieldDescriptor* field) const { + USAGE_CHECK_MESSAGE_TYPE(HasField); + USAGE_CHECK_SINGULAR(HasField); + + if (field->is_extension()) { + return GetExtensionSet(message).Has(field->number()); + } else { + return HasBit(message, field); + } +} + +int GeneratedMessageReflection::FieldSize(const Message& message, + const FieldDescriptor* field) const { + USAGE_CHECK_MESSAGE_TYPE(HasField); + USAGE_CHECK_REPEATED(HasField); + + if (field->is_extension()) { + return GetExtensionSet(message).ExtensionSize(field->number()); + } else { + return GetRaw(message, field).GenericSize(); + } +} + +void GeneratedMessageReflection::ClearField( + Message* message, const FieldDescriptor* field) const { + USAGE_CHECK_MESSAGE_TYPE(ClearField); + + if (field->is_extension()) { + MutableExtensionSet(message)->ClearExtension(field->number()); + } else if (!field->is_repeated()) { + if (HasBit(*message, field)) { + ClearBit(message, field); + + // We need to set the field back to its default value. + switch (field->cpp_type()) { +#define CLEAR_TYPE(CPPTYPE, TYPE) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + *MutableRaw(message, field) = \ + field->default_value_##TYPE(); \ + break; + + CLEAR_TYPE(INT32 , int32 ); + CLEAR_TYPE(INT64 , int64 ); + CLEAR_TYPE(UINT32, uint32); + CLEAR_TYPE(UINT64, uint64); + CLEAR_TYPE(FLOAT , float ); + CLEAR_TYPE(DOUBLE, double); + CLEAR_TYPE(BOOL , bool ); +#undef CLEAR_TYPE + + case FieldDescriptor::CPPTYPE_ENUM: + *MutableRaw(message, field) = + field->default_value_enum()->number(); + break; + + case FieldDescriptor::CPPTYPE_STRING: { + const string* default_ptr = DefaultRaw(field); + string** value = MutableRaw(message, field); + if (*value != default_ptr) { + if (field->has_default_value()) { + (*value)->assign(field->default_value_string()); + } else { + (*value)->clear(); + } + } + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: + (*MutableRaw(message, field))->Clear(); + break; + } + } + } else { + MutableRaw(message, field)->GenericClear(); + } +} + +namespace { +// Comparison functor for sorting FieldDescriptors by field number. +struct FieldNumberSorter { + bool operator()(const FieldDescriptor* left, + const FieldDescriptor* right) const { + return left->number() < right->number(); + } +}; +} // namespace + +void GeneratedMessageReflection::ListFields( + const Message& message, + vector* output) const { + output->clear(); + + // Optimization: The default instance never has any fields set. + if (&message == default_instance_) return; + + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (field->is_repeated()) { + if (GetRaw(message, field).GenericSize() > 0) { + output->push_back(field); + } + } else { + if (HasBit(message, field)) { + output->push_back(field); + } + } + } + + if (extensions_offset_ != -1) { + GetExtensionSet(message).AppendToList(output); + } + + // ListFields() must sort output by field number. + sort(output->begin(), output->end(), FieldNumberSorter()); +} + +// ------------------------------------------------------------------- + +#undef DEFINE_PRIMITIVE_ACCESSORS +#define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE) \ + PASSTYPE GeneratedMessageReflection::Get##TYPENAME( \ + const Message& message, const FieldDescriptor* field) const { \ + USAGE_CHECK_ALL(Get##TYPENAME, SINGULAR, CPPTYPE); \ + if (field->is_extension()) { \ + return GetExtensionSet(message).Get##TYPENAME(field->number()); \ + } else { \ + return GetField(message, field); \ + } \ + } \ + \ + void GeneratedMessageReflection::Set##TYPENAME( \ + Message* message, const FieldDescriptor* field, \ + PASSTYPE value) const { \ + USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE); \ + if (field->is_extension()) { \ + return MutableExtensionSet(message)->Set##TYPENAME( \ + field->number(), value); \ + } else { \ + SetField(message, field, value); \ + } \ + } \ + \ + PASSTYPE GeneratedMessageReflection::GetRepeated##TYPENAME( \ + const Message& message, \ + const FieldDescriptor* field, int index) const { \ + USAGE_CHECK_ALL(GetRepeated##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + return GetExtensionSet(message).GetRepeated##TYPENAME( \ + field->number(), index); \ + } else { \ + return GetRepeatedField(message, field, index); \ + } \ + } \ + \ + void GeneratedMessageReflection::SetRepeated##TYPENAME( \ + Message* message, const FieldDescriptor* field, \ + int index, PASSTYPE value) const { \ + USAGE_CHECK_ALL(SetRepeated##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + MutableExtensionSet(message)->SetRepeated##TYPENAME( \ + field->number(), index, value); \ + } else { \ + SetRepeatedField(message, field, index, value); \ + } \ + } \ + \ + void GeneratedMessageReflection::Add##TYPENAME( \ + Message* message, const FieldDescriptor* field, \ + PASSTYPE value) const { \ + USAGE_CHECK_ALL(Add##TYPENAME, REPEATED, CPPTYPE); \ + if (field->is_extension()) { \ + MutableExtensionSet(message)->Add##TYPENAME(field->number(), value); \ + } else { \ + AddField(message, field, value); \ + } \ + } + +DEFINE_PRIMITIVE_ACCESSORS(Int32 , int32 , int32 , INT32 ) +DEFINE_PRIMITIVE_ACCESSORS(Int64 , int64 , int64 , INT64 ) +DEFINE_PRIMITIVE_ACCESSORS(UInt32, uint32, uint32, UINT32) +DEFINE_PRIMITIVE_ACCESSORS(UInt64, uint64, uint64, UINT64) +DEFINE_PRIMITIVE_ACCESSORS(Float , float , float , FLOAT ) +DEFINE_PRIMITIVE_ACCESSORS(Double, double, double, DOUBLE) +DEFINE_PRIMITIVE_ACCESSORS(Bool , bool , bool , BOOL ) +#undef DEFINE_PRIMITIVE_ACCESSORS + +// ------------------------------------------------------------------- + +string GeneratedMessageReflection::GetString( + const Message& message, const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetString, SINGULAR, STRING); + if (field->is_extension()) { + return GetExtensionSet(message).GetString(field->number()); + } else { + return *GetField(message, field); + } +} + +const string& GeneratedMessageReflection::GetStringReference( + const Message& message, + const FieldDescriptor* field, string* scratch) const { + USAGE_CHECK_ALL(GetStringReference, SINGULAR, STRING); + if (field->is_extension()) { + return GetExtensionSet(message).GetString(field->number()); + } else { + return *GetField(message, field); + } +} + + +void GeneratedMessageReflection::SetString( + Message* message, const FieldDescriptor* field, + const string& value) const { + USAGE_CHECK_ALL(SetString, SINGULAR, STRING); + if (field->is_extension()) { + return MutableExtensionSet(message)->SetString(field->number(), value); + } else { + string** ptr = MutableField(message, field); + if (*ptr == DefaultRaw(field)) { + *ptr = new string(value); + } else { + (*ptr)->assign(value); + } + } +} + + +string GeneratedMessageReflection::GetRepeatedString( + const Message& message, const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedString, REPEATED, STRING); + if (field->is_extension()) { + return GetExtensionSet(message).GetRepeatedString(field->number(), index); + } else { + return GetRepeatedField(message, field, index); + } +} + +const string& GeneratedMessageReflection::GetRepeatedStringReference( + const Message& message, const FieldDescriptor* field, + int index, string* scratch) const { + USAGE_CHECK_ALL(GetRepeatedStringReference, REPEATED, STRING); + if (field->is_extension()) { + return GetExtensionSet(message).GetRepeatedString(field->number(), index); + } else { + return GetRepeatedField(message, field, index); + } +} + + +void GeneratedMessageReflection::SetRepeatedString( + Message* message, const FieldDescriptor* field, + int index, const string& value) const { + USAGE_CHECK_ALL(SetRepeatedString, REPEATED, STRING); + if (field->is_extension()) { + MutableExtensionSet(message)->SetRepeatedString( + field->number(), index, value); + } else { + SetRepeatedField(message, field, index, value); + } +} + + +void GeneratedMessageReflection::AddString( + Message* message, const FieldDescriptor* field, + const string& value) const { + USAGE_CHECK_ALL(AddString, REPEATED, STRING); + if (field->is_extension()) { + MutableExtensionSet(message)->AddString(field->number(), value); + } else { + AddField(message, field, value); + } +} + + +// ------------------------------------------------------------------- + +const EnumValueDescriptor* GeneratedMessageReflection::GetEnum( + const Message& message, const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetEnum, SINGULAR, ENUM); + + int value; + if (field->is_extension()) { + value = GetExtensionSet(message).GetEnum(field->number()); + } else { + value = GetField(message, field); + } + const EnumValueDescriptor* result = + field->enum_type()->FindValueByNumber(value); + GOOGLE_CHECK(result != NULL); + return result; +} + +void GeneratedMessageReflection::SetEnum( + Message* message, const FieldDescriptor* field, + const EnumValueDescriptor* value) const { + USAGE_CHECK_ALL(SetEnum, SINGULAR, ENUM); + USAGE_CHECK_ENUM_VALUE(SetEnum); + + if (field->is_extension()) { + MutableExtensionSet(message)->SetEnum(field->number(), value->number()); + } else { + SetField(message, field, value->number()); + } +} + +const EnumValueDescriptor* GeneratedMessageReflection::GetRepeatedEnum( + const Message& message, const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedEnum, REPEATED, ENUM); + + int value; + if (field->is_extension()) { + value = GetExtensionSet(message).GetRepeatedEnum(field->number(), index); + } else { + value = GetRepeatedField(message, field, index); + } + const EnumValueDescriptor* result = + field->enum_type()->FindValueByNumber(value); + GOOGLE_CHECK(result != NULL); + return result; +} + +void GeneratedMessageReflection::SetRepeatedEnum( + Message* message, + const FieldDescriptor* field, int index, + const EnumValueDescriptor* value) const { + USAGE_CHECK_ALL(SetRepeatedEnum, REPEATED, ENUM); + USAGE_CHECK_ENUM_VALUE(SetRepeatedEnum); + + if (field->is_extension()) { + MutableExtensionSet(message)->SetRepeatedEnum( + field->number(), index, value->number()); + } else { + SetRepeatedField(message, field, index, value->number()); + } +} + +void GeneratedMessageReflection::AddEnum( + Message* message, const FieldDescriptor* field, + const EnumValueDescriptor* value) const { + USAGE_CHECK_ALL(AddEnum, REPEATED, ENUM); + USAGE_CHECK_ENUM_VALUE(AddEnum); + + if (field->is_extension()) { + MutableExtensionSet(message)->AddEnum(field->number(), value->number()); + } else { + AddField(message, field, value->number()); + } +} + +// ------------------------------------------------------------------- + +const Message& GeneratedMessageReflection::GetMessage( + const Message& message, const FieldDescriptor* field) const { + USAGE_CHECK_ALL(GetMessage, SINGULAR, MESSAGE); + + if (field->is_extension()) { + return GetExtensionSet(message).GetMessage(field->number()); + } else { + const Message* result = GetRaw(message, field); + if (result == NULL) { + result = DefaultRaw(field); + } + return *result; + } +} + +Message* GeneratedMessageReflection::MutableMessage( + Message* message, const FieldDescriptor* field) const { + USAGE_CHECK_ALL(MutableMessage, SINGULAR, MESSAGE); + + if (field->is_extension()) { + return MutableExtensionSet(message)->MutableMessage(field->number()); + } else { + Message** result = MutableField(message, field); + if (*result == NULL) { + const Message* default_message = DefaultRaw(field); + *result = default_message->New(); + (*result)->CopyFrom(*default_message); + } + return *result; + } +} + +const Message& GeneratedMessageReflection::GetRepeatedMessage( + const Message& message, const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(GetRepeatedMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return GetExtensionSet(message).GetRepeatedMessage(field->number(), index); + } else { + return GetRepeatedField(message, field, index); + } +} + +Message* GeneratedMessageReflection::MutableRepeatedMessage( + Message* message, const FieldDescriptor* field, int index) const { + USAGE_CHECK_ALL(MutableRepeatedMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return MutableExtensionSet(message)->MutableRepeatedMessage( + field->number(), index); + } else { + return MutableRepeatedField(message, field, index); + } +} + +Message* GeneratedMessageReflection::AddMessage( + Message* message, const FieldDescriptor* field) const { + USAGE_CHECK_ALL(AddMessage, REPEATED, MESSAGE); + + if (field->is_extension()) { + return MutableExtensionSet(message)->AddMessage(field->number()); + } else { + return AddField(message, field); + } +} + +// ------------------------------------------------------------------- + +const FieldDescriptor* GeneratedMessageReflection::FindKnownExtensionByName( + const string& name) const { + if (extensions_offset_ == -1) return NULL; + + const FieldDescriptor* result = descriptor_pool_->FindExtensionByName(name); + if (result != NULL && result->containing_type() == descriptor_) { + return result; + } + + if (descriptor_->options().message_set_wire_format()) { + // MessageSet extensions may be identified by type name. + const Descriptor* type = descriptor_pool_->FindMessageTypeByName(name); + if (type != NULL) { + // Look for a matching extension in the foreign type's scope. + for (int i = 0; i < type->extension_count(); i++) { + const FieldDescriptor* extension = type->extension(i); + if (extension->containing_type() == descriptor_ && + extension->type() == FieldDescriptor::TYPE_MESSAGE && + extension->is_optional() && + extension->message_type() == type) { + // Found it. + return extension; + } + } + } + } + + return NULL; +} + +const FieldDescriptor* GeneratedMessageReflection::FindKnownExtensionByNumber( + int number) const { + if (extensions_offset_ == -1) return NULL; + return descriptor_pool_->FindExtensionByNumber(descriptor_, number); +} + +// =================================================================== +// Some private helpers. + +// These simple template accessors obtain pointers (or references) to +// the given field. +template +inline const Type& GeneratedMessageReflection::GetRaw( + const Message& message, const FieldDescriptor* field) const { + const void* ptr = reinterpret_cast(&message) + + offsets_[field->index()]; + return *reinterpret_cast(ptr); +} + +template +inline Type* GeneratedMessageReflection::MutableRaw( + Message* message, const FieldDescriptor* field) const { + void* ptr = reinterpret_cast(message) + offsets_[field->index()]; + return reinterpret_cast(ptr); +} + +template +inline const Type& GeneratedMessageReflection::DefaultRaw( + const FieldDescriptor* field) const { + const void* ptr = reinterpret_cast(default_instance_) + + offsets_[field->index()]; + return *reinterpret_cast(ptr); +} + +inline const uint32* GeneratedMessageReflection::GetHasBits( + const Message& message) const { + const void* ptr = reinterpret_cast(&message) + has_bits_offset_; + return reinterpret_cast(ptr); +} +inline uint32* GeneratedMessageReflection::MutableHasBits( + Message* message) const { + void* ptr = reinterpret_cast(message) + has_bits_offset_; + return reinterpret_cast(ptr); +} + +inline const ExtensionSet& GeneratedMessageReflection::GetExtensionSet( + const Message& message) const { + GOOGLE_DCHECK_NE(extensions_offset_, -1); + const void* ptr = reinterpret_cast(&message) + + extensions_offset_; + return *reinterpret_cast(ptr); +} +inline ExtensionSet* GeneratedMessageReflection::MutableExtensionSet( + Message* message) const { + GOOGLE_DCHECK_NE(extensions_offset_, -1); + void* ptr = reinterpret_cast(message) + extensions_offset_; + return reinterpret_cast(ptr); +} + +// Simple accessors for manipulating has_bits_. +inline bool GeneratedMessageReflection::HasBit( + const Message& message, const FieldDescriptor* field) const { + return GetHasBits(message)[field->index() / 32] & + (1 << (field->index() % 32)); +} + +inline void GeneratedMessageReflection::SetBit( + Message* message, const FieldDescriptor* field) const { + MutableHasBits(message)[field->index() / 32] |= (1 << (field->index() % 32)); +} + +inline void GeneratedMessageReflection::ClearBit( + Message* message, const FieldDescriptor* field) const { + MutableHasBits(message)[field->index() / 32] &= ~(1 << (field->index() % 32)); +} + +// Template implementations of basic accessors. Inline because each +// template instance is only called from one location. These are +// used for all types except messages. +template +inline const Type& GeneratedMessageReflection::GetField( + const Message& message, const FieldDescriptor* field) const { + return GetRaw(message, field); +} + +template +inline void GeneratedMessageReflection::SetField( + Message* message, const FieldDescriptor* field, const Type& value) const { + *MutableRaw(message, field) = value; + SetBit(message, field); +} + +template +inline Type* GeneratedMessageReflection::MutableField( + Message* message, const FieldDescriptor* field) const { + SetBit(message, field); + return MutableRaw(message, field); +} + +template +inline const Type& GeneratedMessageReflection::GetRepeatedField( + const Message& message, const FieldDescriptor* field, int index) const { + return *reinterpret_cast( + GetRaw(message, field).GenericGet(index)); +} + +template +inline void GeneratedMessageReflection::SetRepeatedField( + Message* message, const FieldDescriptor* field, + int index, const Type& value) const { + GenericRepeatedField* repeated = + MutableRaw(message, field); + *reinterpret_cast(repeated->GenericMutable(index)) = value; +} + +template +inline Type* GeneratedMessageReflection::MutableRepeatedField( + Message* message, const FieldDescriptor* field, int index) const { + GenericRepeatedField* repeated = + MutableRaw(message, field); + return reinterpret_cast(repeated->GenericMutable(index)); +} + +template +inline void GeneratedMessageReflection::AddField( + Message* message, const FieldDescriptor* field, const Type& value) const { + GenericRepeatedField* repeated = + MutableRaw(message, field); + *reinterpret_cast(repeated->GenericAdd()) = value; +} + +template +inline Type* GeneratedMessageReflection::AddField( + Message* message, const FieldDescriptor* field) const { + GenericRepeatedField* repeated = + MutableRaw(message, field); + return reinterpret_cast(repeated->GenericAdd()); +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h new file mode 100644 index 00000000..48df8dc1 --- /dev/null +++ b/src/google/protobuf/generated_message_reflection.h @@ -0,0 +1,367 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ + +#include +#include +#include +#include + + +namespace google { +namespace protobuf { + class DescriptorPool; + // Generated code needs this to have been forward-declared. Easier to do it + // here than to print it inside every .pb.h file. + class EnumDescriptor; +} + +namespace protobuf { +namespace internal { + +// Defined in this file. +class GeneratedMessageReflection; + +// Defined in other files. +class ExtensionSet; // extension_set.h + +// THIS CLASS IS NOT INTENDED FOR DIRECT USE. It is intended for use +// by generated code. This class is just a big hack that reduces code +// size. +// +// A GeneratedMessageReflection is an implementation of Reflection +// which expects all fields to be backed by simple variables located in +// memory. The locations are given using a base pointer and a set of +// offsets. +// +// It is required that the user represents fields of each type in a standard +// way, so that GeneratedMessageReflection can cast the void* pointer to +// the appropriate type. For primitive fields and string fields, each field +// should be represented using the obvious C++ primitive type. Enums and +// Messages are different: +// - Singular Message fields are stored as a pointer to a Message. These +// should start out NULL, except for in the default instance where they +// should start out pointing to other default instances. +// - Enum fields are stored as an int. This int must always contain +// a valid value, such that EnumDescriptor::FindValueByNumber() would +// not return NULL. +// - Repeated fields are stored as RepeatedFields or RepeatedPtrFields +// of whatever type the individual field would be. Strings and +// Messages use RepeatedPtrFields while everything else uses +// RepeatedFields. +class LIBPROTOBUF_EXPORT GeneratedMessageReflection : public Reflection { + public: + // Constructs a GeneratedMessageReflection. + // Parameters: + // descriptor: The descriptor for the message type being implemented. + // default_instance: The default instance of the message. This is only + // used to obtain pointers to default instances of embedded + // messages, which GetMessage() will return if the particular + // sub-message has not been initialized yet. (Thus, all + // embedded message fields *must* have non-NULL pointers + // in the default instance.) + // offsets: An array of ints giving the byte offsets, relative to + // the start of the message object, of each field. These can + // be computed at compile time using the + // GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET() macro, defined + // below. + // has_bits_offset: Offset in the message of an array of uint32s of size + // descriptor->field_count()/32, rounded up. This is a + // bitfield where each bit indicates whether or not the + // corresponding field of the message has been initialized. + // The bit for field index i is obtained by the expression: + // has_bits[i / 32] & (1 << (i % 32)) + // unknown_fields_offset: Offset in the message of the UnknownFieldSet for + // the message. + // extensions_offset: Offset in the message of the ExtensionSet for the + // message, or -1 if the message type has no extension + // ranges. + // pool: DescriptorPool to search for extension definitions. Only + // used by FindKnownExtensionByName() and + // FindKnownExtensionByNumber(). + GeneratedMessageReflection(const Descriptor* descriptor, + const Message* default_instance, + const int offsets[], + int has_bits_offset, + int unknown_fields_offset, + int extensions_offset, + const DescriptorPool* pool); + ~GeneratedMessageReflection(); + + // implements Reflection ------------------------------------------- + + const UnknownFieldSet& GetUnknownFields(const Message& message) const; + UnknownFieldSet* MutableUnknownFields(Message* message) const; + + bool HasField(const Message& message, const FieldDescriptor* field) const; + int FieldSize(const Message& message, const FieldDescriptor* field) const; + void ClearField(Message* message, const FieldDescriptor* field) const; + void ListFields(const Message& message, + vector* output) const; + + int32 GetInt32 (const Message& message, + const FieldDescriptor* field) const; + int64 GetInt64 (const Message& message, + const FieldDescriptor* field) const; + uint32 GetUInt32(const Message& message, + const FieldDescriptor* field) const; + uint64 GetUInt64(const Message& message, + const FieldDescriptor* field) const; + float GetFloat (const Message& message, + const FieldDescriptor* field) const; + double GetDouble(const Message& message, + const FieldDescriptor* field) const; + bool GetBool (const Message& message, + const FieldDescriptor* field) const; + string GetString(const Message& message, + const FieldDescriptor* field) const; + const string& GetStringReference(const Message& message, + const FieldDescriptor* field, + string* scratch) const; + const EnumValueDescriptor* GetEnum(const Message& message, + const FieldDescriptor* field) const; + const Message& GetMessage(const Message& message, + const FieldDescriptor* field) const; + + void SetInt32 (Message* message, + const FieldDescriptor* field, int32 value) const; + void SetInt64 (Message* message, + const FieldDescriptor* field, int64 value) const; + void SetUInt32(Message* message, + const FieldDescriptor* field, uint32 value) const; + void SetUInt64(Message* message, + const FieldDescriptor* field, uint64 value) const; + void SetFloat (Message* message, + const FieldDescriptor* field, float value) const; + void SetDouble(Message* message, + const FieldDescriptor* field, double value) const; + void SetBool (Message* message, + const FieldDescriptor* field, bool value) const; + void SetString(Message* message, + const FieldDescriptor* field, + const string& value) const; + void SetEnum (Message* message, const FieldDescriptor* field, + const EnumValueDescriptor* value) const; + Message* MutableMessage(Message* message, const FieldDescriptor* field) const; + + int32 GetRepeatedInt32 (const Message& message, + const FieldDescriptor* field, int index) const; + int64 GetRepeatedInt64 (const Message& message, + const FieldDescriptor* field, int index) const; + uint32 GetRepeatedUInt32(const Message& message, + const FieldDescriptor* field, int index) const; + uint64 GetRepeatedUInt64(const Message& message, + const FieldDescriptor* field, int index) const; + float GetRepeatedFloat (const Message& message, + const FieldDescriptor* field, int index) const; + double GetRepeatedDouble(const Message& message, + const FieldDescriptor* field, int index) const; + bool GetRepeatedBool (const Message& message, + const FieldDescriptor* field, int index) const; + string GetRepeatedString(const Message& message, + const FieldDescriptor* field, int index) const; + const string& GetRepeatedStringReference(const Message& message, + const FieldDescriptor* field, + int index, string* scratch) const; + const EnumValueDescriptor* GetRepeatedEnum(const Message& message, + const FieldDescriptor* field, + int index) const; + const Message& GetRepeatedMessage(const Message& message, + const FieldDescriptor* field, + int index) const; + + // Set the value of a field. + void SetRepeatedInt32 (Message* message, + const FieldDescriptor* field, int index, int32 value) const; + void SetRepeatedInt64 (Message* message, + const FieldDescriptor* field, int index, int64 value) const; + void SetRepeatedUInt32(Message* message, + const FieldDescriptor* field, int index, uint32 value) const; + void SetRepeatedUInt64(Message* message, + const FieldDescriptor* field, int index, uint64 value) const; + void SetRepeatedFloat (Message* message, + const FieldDescriptor* field, int index, float value) const; + void SetRepeatedDouble(Message* message, + const FieldDescriptor* field, int index, double value) const; + void SetRepeatedBool (Message* message, + const FieldDescriptor* field, int index, bool value) const; + void SetRepeatedString(Message* message, + const FieldDescriptor* field, int index, + const string& value) const; + void SetRepeatedEnum(Message* message, const FieldDescriptor* field, + int index, const EnumValueDescriptor* value) const; + // Get a mutable pointer to a field with a message type. + Message* MutableRepeatedMessage(Message* message, + const FieldDescriptor* field, + int index) const; + + void AddInt32 (Message* message, + const FieldDescriptor* field, int32 value) const; + void AddInt64 (Message* message, + const FieldDescriptor* field, int64 value) const; + void AddUInt32(Message* message, + const FieldDescriptor* field, uint32 value) const; + void AddUInt64(Message* message, + const FieldDescriptor* field, uint64 value) const; + void AddFloat (Message* message, + const FieldDescriptor* field, float value) const; + void AddDouble(Message* message, + const FieldDescriptor* field, double value) const; + void AddBool (Message* message, + const FieldDescriptor* field, bool value) const; + void AddString(Message* message, + const FieldDescriptor* field, const string& value) const; + void AddEnum(Message* message, + const FieldDescriptor* field, + const EnumValueDescriptor* value) const; + Message* AddMessage(Message* message, const FieldDescriptor* field) const; + + const FieldDescriptor* FindKnownExtensionByName(const string& name) const; + const FieldDescriptor* FindKnownExtensionByNumber(int number) const; + + private: + friend class GeneratedMessage; + + const Descriptor* descriptor_; + const Message* default_instance_; + const int* offsets_; + + int has_bits_offset_; + int unknown_fields_offset_; + int extensions_offset_; + + const DescriptorPool* descriptor_pool_; + + template + inline const Type& GetRaw(const Message& message, + const FieldDescriptor* field) const; + template + inline Type* MutableRaw(Message* message, + const FieldDescriptor* field) const; + template + inline const Type& DefaultRaw(const FieldDescriptor* field) const; + inline const Message* GetMessagePrototype(const FieldDescriptor* field) const; + + inline const uint32* GetHasBits(const Message& message) const; + inline uint32* MutableHasBits(Message* message) const; + inline const ExtensionSet& GetExtensionSet(const Message& message) const; + inline ExtensionSet* MutableExtensionSet(Message* message) const; + + inline bool HasBit(const Message& message, + const FieldDescriptor* field) const; + inline void SetBit(Message* message, + const FieldDescriptor* field) const; + inline void ClearBit(Message* message, + const FieldDescriptor* field) const; + + template + inline const Type& GetField(const Message& message, + const FieldDescriptor* field) const; + template + inline void SetField(Message* message, + const FieldDescriptor* field, const Type& value) const; + template + inline Type* MutableField(Message* message, + const FieldDescriptor* field) const; + template + inline const Type& GetRepeatedField(const Message& message, + const FieldDescriptor* field, + int index) const; + template + inline void SetRepeatedField(Message* message, + const FieldDescriptor* field, int index, + const Type& value) const; + template + inline Type* MutableRepeatedField(Message* message, + const FieldDescriptor* field, + int index) const; + template + inline void AddField(Message* message, + const FieldDescriptor* field, const Type& value) const; + template + inline Type* AddField(Message* message, + const FieldDescriptor* field) const; + + int GetExtensionNumberOrDie(const Descriptor* type) const; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GeneratedMessageReflection); +}; + +// Returns the offset of the given field within the given aggregate type. +// This is equivalent to the ANSI C offsetof() macro. However, according +// to the C++ standard, offsetof() only works on POD types, and GCC +// enforces this requirement with a warning. In practice, this rule is +// unnecessarily strict; there is probably no compiler or platform on +// which the offsets of the direct fields of a class are non-constant. +// Fields inherited from superclasses *can* have non-constant offsets, +// but that's not what this macro will be used for. +// +// Note that we calculate relative to the pointer value 16 here since if we +// just use zero, GCC complains about dereferencing a NULL pointer. We +// choose 16 rather than some other number just in case the compiler would +// be confused by an unaligned pointer. +#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \ + (reinterpret_cast( \ + &reinterpret_cast(16)->FIELD) - \ + reinterpret_cast(16)) + +// There are some places in proto2 where dynamic_cast would be useful as an +// optimization. For example, take Message::MergeFrom(const Message& other). +// For a given generated message FooMessage, we generate these two methods: +// void MergeFrom(const FooMessage& other); +// void MergeFrom(const Message& other); +// The former method can be implemented directly in terms of FooMessage's +// inline accessors, but the latter method must work with the reflection +// interface. However, if the parameter to the latter method is actually of +// type FooMessage, then we'd like to be able to just call the other method +// as an optimization. So, we use dynamic_cast to check this. +// +// That said, dynamic_cast requires RTTI, which many people like to disable +// for performance and code size reasons. When RTTI is not available, we +// still need to produce correct results. So, in this case we have to fall +// back to using reflection, which is what we would have done anyway if the +// objects were not of the exact same class. +// +// dynamic_cast_if_available() implements this logic. If RTTI is +// enabled, it does a dynamic_cast. If RTTI is disabled, it just returns +// NULL. +// +// If you need to compile without RTTI, simply #define GOOGLE_PROTOBUF_NO_RTTI. +// On MSVC, this should be detected automatically. +template +inline To dynamic_cast_if_available(From from) { +#if defined(GOOGLE_PROTOBUF_NO_RTTI) || (defined(_MSC_VER)&&!defined(_CPPRTTI)) + return NULL; +#else + return dynamic_cast(from); +#endif +} + + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_GENERATED_MESSAGE_REFLECTION_H__ diff --git a/src/google/protobuf/generated_message_reflection_unittest.cc b/src/google/protobuf/generated_message_reflection_unittest.cc new file mode 100644 index 00000000..d0e96bd8 --- /dev/null +++ b/src/google/protobuf/generated_message_reflection_unittest.cc @@ -0,0 +1,254 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// To test GeneratedMessageReflection, we actually let the protocol compiler +// generate a full protocol message implementation and then test its +// reflection interface. This is much easier and more maintainable than +// trying to create our own Message class for GeneratedMessageReflection +// to wrap. +// +// The tests here closely mirror some of the tests in +// compiler/cpp/unittest, except using the reflection interface +// rather than generated accessors. + +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace { + +// Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. +const FieldDescriptor* F(const string& name) { + const FieldDescriptor* result = + unittest::TestAllTypes::descriptor()->FindFieldByName(name); + GOOGLE_CHECK(result != NULL); + return result; +} + +TEST(GeneratedMessageReflectionTest, Defaults) { + // Check that all default values are set correctly in the initial message. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + reflection_tester.ExpectClearViaReflection(message); + + const Reflection* reflection = message.GetReflection(); + + // Messages should return pointers to default instances until first use. + // (This is not checked by ExpectClear() since it is not actually true after + // the fields have been set and then cleared.) + EXPECT_EQ(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &reflection->GetMessage(message, F("optionalgroup"))); + EXPECT_EQ(&unittest::TestAllTypes::NestedMessage::default_instance(), + &reflection->GetMessage(message, F("optional_nested_message"))); + EXPECT_EQ(&unittest::ForeignMessage::default_instance(), + &reflection->GetMessage(message, F("optional_foreign_message"))); + EXPECT_EQ(&unittest_import::ImportMessage::default_instance(), + &reflection->GetMessage(message, F("optional_import_message"))); +} + +TEST(GeneratedMessageReflectionTest, Accessors) { + // Set every field to a unique value then go back and check all those + // values. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + reflection_tester.SetAllFieldsViaReflection(&message); + TestUtil::ExpectAllFieldsSet(message); + reflection_tester.ExpectAllFieldsSetViaReflection(message); + + reflection_tester.ModifyRepeatedFieldsViaReflection(&message); + TestUtil::ExpectRepeatedFieldsModified(message); +} + +TEST(GeneratedMessageReflectionTest, GetStringReference) { + // Test that GetStringReference() returns the underlying string when it is + // a normal string field. + unittest::TestAllTypes message; + message.set_optional_string("foo"); + message.add_repeated_string("foo"); + + const Reflection* reflection = message.GetReflection(); + string scratch; + + EXPECT_EQ(&message.optional_string(), + &reflection->GetStringReference(message, F("optional_string"), &scratch)) + << "For simple string fields, GetStringReference() should return a " + "reference to the underlying string."; + EXPECT_EQ(&message.repeated_string(0), + &reflection->GetRepeatedStringReference(message, F("repeated_string"), + 0, &scratch)) + << "For simple string fields, GetRepeatedStringReference() should return " + "a reference to the underlying string."; +} + + +TEST(GeneratedMessageReflectionTest, DefaultsAfterClear) { + // Check that after setting all fields and then clearing, getting an + // embedded message does NOT return the default instance. + unittest::TestAllTypes message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllTypes::descriptor()); + + TestUtil::SetAllFields(&message); + message.Clear(); + + const Reflection* reflection = message.GetReflection(); + + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &reflection->GetMessage(message, F("optionalgroup"))); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &reflection->GetMessage(message, F("optional_nested_message"))); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &reflection->GetMessage(message, F("optional_foreign_message"))); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &reflection->GetMessage(message, F("optional_import_message"))); +} + +TEST(GeneratedMessageReflectionTest, Extensions) { + // Set every extension to a unique value then go back and check all those + // values. + unittest::TestAllExtensions message; + TestUtil::ReflectionTester reflection_tester( + unittest::TestAllExtensions::descriptor()); + + reflection_tester.SetAllFieldsViaReflection(&message); + TestUtil::ExpectAllExtensionsSet(message); + reflection_tester.ExpectAllFieldsSetViaReflection(message); + + reflection_tester.ModifyRepeatedFieldsViaReflection(&message); + TestUtil::ExpectRepeatedExtensionsModified(message); +} + +TEST(GeneratedMessageReflectionTest, FindExtensionTypeByNumber) { + const Reflection* reflection = + unittest::TestAllExtensions::default_instance().GetReflection(); + + const FieldDescriptor* extension1 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "optional_int32_extension"); + const FieldDescriptor* extension2 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "repeated_string_extension"); + + EXPECT_EQ(extension1, + reflection->FindKnownExtensionByNumber(extension1->number())); + EXPECT_EQ(extension2, + reflection->FindKnownExtensionByNumber(extension2->number())); + + // Non-existent extension. + EXPECT_TRUE(reflection->FindKnownExtensionByNumber(62341) == NULL); + + // Extensions of TestAllExtensions should not show up as extensions of + // other types. + EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()-> + FindKnownExtensionByNumber(extension1->number()) == NULL); +} + +TEST(GeneratedMessageReflectionTest, FindKnownExtensionByName) { + const Reflection* reflection = + unittest::TestAllExtensions::default_instance().GetReflection(); + + const FieldDescriptor* extension1 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "optional_int32_extension"); + const FieldDescriptor* extension2 = + unittest::TestAllExtensions::descriptor()->file()->FindExtensionByName( + "repeated_string_extension"); + + EXPECT_EQ(extension1, + reflection->FindKnownExtensionByName(extension1->full_name())); + EXPECT_EQ(extension2, + reflection->FindKnownExtensionByName(extension2->full_name())); + + // Non-existent extension. + EXPECT_TRUE(reflection->FindKnownExtensionByName("no_such_ext") == NULL); + + // Extensions of TestAllExtensions should not show up as extensions of + // other types. + EXPECT_TRUE(unittest::TestAllTypes::default_instance().GetReflection()-> + FindKnownExtensionByName(extension1->full_name()) == NULL); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(GeneratedMessageReflectionTest, UsageErrors) { + unittest::TestAllTypes message; + const Reflection* reflection = message.GetReflection(); + const Descriptor* descriptor = message.GetDescriptor(); + +#define f(NAME) descriptor->FindFieldByName(NAME) + + // Testing every single failure mode would be too much work. Let's just + // check a few. + EXPECT_DEATH( + reflection->GetInt32( + message, descriptor->FindFieldByName("optional_int64")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::GetInt32\n" + " Message type: protobuf_unittest\\.TestAllTypes\n" + " Field : protobuf_unittest\\.TestAllTypes\\.optional_int64\n" + " Problem : Field is not the right type for this message:\n" + " Expected : CPPTYPE_INT32\n" + " Field type: CPPTYPE_INT64"); + EXPECT_DEATH( + reflection->GetInt32( + message, descriptor->FindFieldByName("repeated_int32")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::GetInt32\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.TestAllTypes.repeated_int32\n" + " Problem : Field is repeated; the method requires a singular field."); + EXPECT_DEATH( + reflection->GetInt32( + message, unittest::ForeignMessage::descriptor()->FindFieldByName("c")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::GetInt32\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.ForeignMessage.c\n" + " Problem : Field does not match message type."); + EXPECT_DEATH( + reflection->HasField( + message, unittest::ForeignMessage::descriptor()->FindFieldByName("c")), + "Protocol Buffer reflection usage error:\n" + " Method : google::protobuf::Reflection::HasField\n" + " Message type: protobuf_unittest.TestAllTypes\n" + " Field : protobuf_unittest.ForeignMessage.c\n" + " Problem : Field does not match message type."); + +#undef f +} + +#endif // GTEST_HAS_DEATH_TEST + + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/coded_stream.cc b/src/google/protobuf/io/coded_stream.cc new file mode 100644 index 00000000..58c44dc1 --- /dev/null +++ b/src/google/protobuf/io/coded_stream.cc @@ -0,0 +1,757 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This implementation is heavily optimized to make reads and writes +// of small values (especially varints) as fast as possible. In +// particular, we optimize for the common case that a read or a write +// will not cross the end of the buffer, since we can avoid a lot +// of branching in this case. + +#include +#include +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace io { + +namespace { + +static const int kDefaultTotalBytesLimit = 64 << 20; // 64MB + +static const int kDefaultTotalBytesWarningThreshold = 32 << 20; // 32MB +static const int kDefaultRecursionLimit = 64; + +static const int kMaxVarintBytes = 10; +static const int kMaxVarint32Bytes = 5; + + +} // namespace + +// CodedInputStream ================================================== + +CodedInputStream::CodedInputStream(ZeroCopyInputStream* input) + : input_(input), + buffer_(NULL), + buffer_size_(0), + total_bytes_read_(0), + overflow_bytes_(0), + + last_tag_(0), + legitimate_message_end_(false), + + aliasing_enabled_(false), + + current_limit_(INT_MAX), + buffer_size_after_limit_(0), + + total_bytes_limit_(kDefaultTotalBytesLimit), + total_bytes_warning_threshold_(kDefaultTotalBytesWarningThreshold), + + recursion_depth_(0), + recursion_limit_(kDefaultRecursionLimit) { +} + +CodedInputStream::~CodedInputStream() { + int backup_bytes = buffer_size_ + buffer_size_after_limit_ + overflow_bytes_; + if (backup_bytes > 0) { + // We still have bytes left over from the last buffer. Back up over + // them. + input_->BackUp(backup_bytes); + } +} + + +inline void CodedInputStream::RecomputeBufferLimits() { + buffer_size_ += buffer_size_after_limit_; + int closest_limit = min(current_limit_, total_bytes_limit_); + if (closest_limit < total_bytes_read_) { + // The limit position is in the current buffer. We must adjust + // the buffer size accordingly. + buffer_size_after_limit_ = total_bytes_read_ - closest_limit; + buffer_size_ -= buffer_size_after_limit_; + } else { + buffer_size_after_limit_ = 0; + } +} + +CodedInputStream::Limit CodedInputStream::PushLimit(int byte_limit) { + // Current position relative to the beginning of the stream. + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + + Limit old_limit = current_limit_; + + // security: byte_limit is possibly evil, so check for negative values + // and overflow. + if (byte_limit >= 0 && + byte_limit <= INT_MAX - current_position) { + current_limit_ = current_position + byte_limit; + } else { + // Negative or overflow. + current_limit_ = INT_MAX; + } + + // We need to enforce all limits, not just the new one, so if the previous + // limit was before the new requested limit, we continue to enforce the + // previous limit. + current_limit_ = min(current_limit_, old_limit); + + RecomputeBufferLimits(); + return old_limit; +} + +void CodedInputStream::PopLimit(Limit limit) { + // The limit passed in is actually the *old* limit, which we returned from + // PushLimit(). + current_limit_ = limit; + RecomputeBufferLimits(); + + // We may no longer be at a legitimate message end. ReadTag() needs to be + // called again to find out. + legitimate_message_end_ = false; +} + +int CodedInputStream::BytesUntilLimit() { + if (current_limit_ == INT_MAX) return -1; + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + + return current_limit_ - current_position; +} + +void CodedInputStream::SetTotalBytesLimit( + int total_bytes_limit, int warning_threshold) { + // Make sure the limit isn't already past, since this could confuse other + // code. + int current_position = total_bytes_read_ - + (buffer_size_ + buffer_size_after_limit_); + total_bytes_limit_ = max(current_position, total_bytes_limit); + total_bytes_warning_threshold_ = warning_threshold; + RecomputeBufferLimits(); +} + +void CodedInputStream::PrintTotalBytesLimitError() { + GOOGLE_LOG(ERROR) << "A protocol message was rejected because it was too " + "big (more than " << total_bytes_limit_ + << " bytes). To increase the limit (or to disable these " + "warnings), see CodedInputStream::SetTotalBytesLimit() " + "in google/protobuf/io/coded_stream.h."; +} + +bool CodedInputStream::Skip(int count) { + if (count < 0) return false; // security: count is often user-supplied + + if (count <= buffer_size_) { + // Just skipping within the current buffer. Easy. + Advance(count); + return true; + } + + if (buffer_size_after_limit_ > 0) { + // We hit a limit inside this buffer. Advance to the limit and fail. + Advance(buffer_size_); + return false; + } + + count -= buffer_size_; + buffer_ = NULL; + buffer_size_ = 0; + + // Make sure this skip doesn't try to skip past the current limit. + int closest_limit = min(current_limit_, total_bytes_limit_); + int bytes_until_limit = closest_limit - total_bytes_read_; + if (bytes_until_limit < count) { + // We hit the limit. Skip up to it then fail. + total_bytes_read_ = closest_limit; + input_->Skip(bytes_until_limit); + return false; + } + + total_bytes_read_ += count; + return input_->Skip(count); +} + +bool CodedInputStream::ReadRaw(void* buffer, int size) { + while (buffer_size_ < size) { + // Reading past end of buffer. Copy what we have, then refresh. + memcpy(buffer, buffer_, buffer_size_); + buffer = reinterpret_cast(buffer) + buffer_size_; + size -= buffer_size_; + if (!Refresh()) return false; + } + + memcpy(buffer, buffer_, size); + Advance(size); + + return true; +} + +bool CodedInputStream::ReadString(string* buffer, int size) { + if (size < 0) return false; // security: size is often user-supplied + + if (!buffer->empty()) { + buffer->clear(); + } + + if (size < buffer_size_) { + STLStringResizeUninitialized(buffer, size); + memcpy((uint8*)buffer->data(), buffer_, size); + Advance(size); + return true; + } + + while (buffer_size_ < size) { + // Some STL implementations "helpfully" crash on buffer->append(NULL, 0). + if (buffer_size_ != 0) { + // Note: string1.append(string2) is O(string2.size()) (as opposed to + // O(string1.size() + string2.size()), which would be bad). + buffer->append(reinterpret_cast(buffer_), buffer_size_); + } + size -= buffer_size_; + if (!Refresh()) return false; + } + + buffer->append(reinterpret_cast(buffer_), size); + Advance(size); + + return true; +} + + +bool CodedInputStream::ReadLittleEndian32(uint32* value) { + uint8 bytes[sizeof(*value)]; + + const uint8* ptr; + if (buffer_size_ >= sizeof(*value)) { + // Fast path: Enough bytes in the buffer to read directly. + ptr = buffer_; + Advance(sizeof(*value)); + } else { + // Slow path: Had to read past the end of the buffer. + if (!ReadRaw(bytes, sizeof(*value))) return false; + ptr = bytes; + } + + *value = (static_cast(ptr[0]) ) | + (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | + (static_cast(ptr[3]) << 24); + return true; +} + +bool CodedInputStream::ReadLittleEndian64(uint64* value) { + uint8 bytes[sizeof(*value)]; + + const uint8* ptr; + if (buffer_size_ >= sizeof(*value)) { + // Fast path: Enough bytes in the buffer to read directly. + ptr = buffer_; + Advance(sizeof(*value)); + } else { + // Slow path: Had to read past the end of the buffer. + if (!ReadRaw(bytes, sizeof(*value))) return false; + ptr = bytes; + } + + uint32 part0 = (static_cast(ptr[0]) ) | + (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | + (static_cast(ptr[3]) << 24); + uint32 part1 = (static_cast(ptr[4]) ) | + (static_cast(ptr[5]) << 8) | + (static_cast(ptr[6]) << 16) | + (static_cast(ptr[7]) << 24); + *value = static_cast(part0) | + (static_cast(part1) << 32); + return true; +} + +bool CodedInputStream::ReadVarint32Fallback(uint32* value) { + if (buffer_size_ >= kMaxVarintBytes || + // Optimization: If the varint ends at exactly the end of the buffer, + // we can detect that and still use the fast path. + (buffer_size_ != 0 && !(buffer_[buffer_size_-1] & 0x80))) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this read won't cross the end, so we can skip the checks. + const uint8* ptr = buffer_; + uint32 b; + uint32 result; + + b = *(ptr++); result = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); result |= b << 28; if (!(b & 0x80)) goto done; + + // If the input is larger than 32 bits, we still need to read it all + // and discard the high-order bits. + for (int i = 0; i < kMaxVarintBytes - kMaxVarint32Bytes; i++) { + b = *(ptr++); if (!(b & 0x80)) goto done; + } + + // We have overrun the maximum size of a varint (10 bytes). Assume + // the data is corrupt. + return false; + + done: + Advance(ptr - buffer_); + *value = result; + return true; + + } else { + // Optimization: If we're at a limit, detect that quickly. (This is + // common when reading tags.) + while (buffer_size_ == 0) { + // Detect cases where we definitely hit a byte limit without calling + // Refresh(). + if (// If we hit a limit, buffer_size_after_limit_ will be non-zero. + buffer_size_after_limit_ > 0 && + // Make sure that the limit we hit is not total_bytes_limit_, since + // in that case we still need to call Refresh() so that it prints an + // error. + total_bytes_read_ - buffer_size_after_limit_ < total_bytes_limit_) { + // We hit a byte limit. + legitimate_message_end_ = true; + return false; + } + + // Call refresh. + if (!Refresh()) { + // Refresh failed. Make sure that it failed due to EOF, not because + // we hit total_bytes_limit_, which, unlike normal limits, is not a + // valid place to end a message. + int current_position = total_bytes_read_ - buffer_size_after_limit_; + if (current_position >= total_bytes_limit_) { + // Hit total_bytes_limit_. But if we also hit the normal limit, + // we're still OK. + legitimate_message_end_ = current_limit_ == total_bytes_limit_; + } else { + legitimate_message_end_ = true; + } + return false; + } + } + + // Slow path: Just do a 64-bit read. + uint64 result; + if (!ReadVarint64(&result)) return false; + *value = (uint32)result; + return true; + } +} + +bool CodedInputStream::ReadVarint64(uint64* value) { + if (buffer_size_ >= kMaxVarintBytes || + // Optimization: If the varint ends at exactly the end of the buffer, + // we can detect that and still use the fast path. + (buffer_size_ != 0 && !(buffer_[buffer_size_-1] & 0x80))) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this read won't cross the end, so we can skip the checks. + + const uint8* ptr = buffer_; + uint32 b; + + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32 part0 = 0, part1 = 0, part2 = 0; + + b = *(ptr++); part0 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); part0 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 14; if (!(b & 0x80)) goto done; + b = *(ptr++); part1 |= (b & 0x7F) << 21; if (!(b & 0x80)) goto done; + b = *(ptr++); part2 = (b & 0x7F) ; if (!(b & 0x80)) goto done; + b = *(ptr++); part2 |= (b & 0x7F) << 7; if (!(b & 0x80)) goto done; + + // We have overrun the maximum size of a varint (10 bytes). The data + // must be corrupt. + return false; + + done: + Advance(ptr - buffer_); + *value = (static_cast(part0) ) | + (static_cast(part1) << 28) | + (static_cast(part2) << 56); + return true; + + } else { + // Slow path: This read might cross the end of the buffer, so we + // need to check and refresh the buffer if and when it does. + + uint64 result = 0; + int count = 0; + uint32 b; + + do { + if (count == kMaxVarintBytes) return false; + while (buffer_size_ == 0) { + if (!Refresh()) return false; + } + b = *buffer_; + result |= static_cast(b & 0x7F) << (7 * count); + Advance(1); + ++count; + } while(b & 0x80); + + *value = result; + return true; + } +} + +bool CodedInputStream::Refresh() { + if (buffer_size_after_limit_ > 0 || overflow_bytes_ > 0) { + // We've hit a limit. Stop. + buffer_ += buffer_size_; + buffer_size_ = 0; + + int current_position = total_bytes_read_ - buffer_size_after_limit_; + + if (current_position >= total_bytes_limit_ && + total_bytes_limit_ != current_limit_) { + // Hit total_bytes_limit_. + PrintTotalBytesLimitError(); + } + + return false; + } + + if (total_bytes_warning_threshold_ >= 0 && + total_bytes_read_ >= total_bytes_warning_threshold_) { + GOOGLE_LOG(WARNING) << "Reading dangerously large protocol message. If the " + "message turns out to be larger than " + << total_bytes_limit_ << " bytes, parsing will be halted " + "for security reasons. To increase the limit (or to " + "disable these warnings), see " + "CodedInputStream::SetTotalBytesLimit() in " + "google/protobuf/io/coded_stream.h."; + + // Don't warn again for this stream. + total_bytes_warning_threshold_ = -1; + } + + const void* void_buffer; + if (input_->Next(&void_buffer, &buffer_size_)) { + buffer_ = reinterpret_cast(void_buffer); + GOOGLE_CHECK_GE(buffer_size_, 0); + + if (total_bytes_read_ <= INT_MAX - buffer_size_) { + total_bytes_read_ += buffer_size_; + } else { + // Overflow. Reset buffer_size_ to not include the bytes beyond INT_MAX. + // We can't get that far anyway, because total_bytes_limit_ is guaranteed + // to be less than it. We need to keep track of the number of bytes + // we discarded, though, so that we can call input_->BackUp() to back + // up over them on destruction. + + // The following line is equivalent to: + // overflow_bytes_ = total_bytes_read_ + buffer_size_ - INT_MAX; + // except that it avoids overflows. Signed integer overflow has + // undefined results according to the C standard. + overflow_bytes_ = total_bytes_read_ - (INT_MAX - buffer_size_); + buffer_size_ -= overflow_bytes_; + total_bytes_read_ = INT_MAX; + } + + RecomputeBufferLimits(); + return true; + } else { + buffer_ = NULL; + buffer_size_ = 0; + return false; + } +} + +// CodedOutputStream ================================================= + +CodedOutputStream::CodedOutputStream(ZeroCopyOutputStream* output) + : output_(output), + buffer_(NULL), + buffer_size_(0), + total_bytes_(0) { +} + +CodedOutputStream::~CodedOutputStream() { + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } +} + +bool CodedOutputStream::WriteRaw(const void* data, int size) { + while (buffer_size_ < size) { + memcpy(buffer_, data, buffer_size_); + size -= buffer_size_; + data = reinterpret_cast(data) + buffer_size_; + if (!Refresh()) return false; + } + + memcpy(buffer_, data, size); + Advance(size); + return true; +} + + +bool CodedOutputStream::WriteLittleEndian32(uint32 value) { + uint8 bytes[sizeof(value)]; + + bool use_fast = buffer_size_ >= sizeof(value); + uint8* ptr = use_fast ? buffer_ : bytes; + + ptr[0] = static_cast(value ); + ptr[1] = static_cast(value >> 8); + ptr[2] = static_cast(value >> 16); + ptr[3] = static_cast(value >> 24); + + if (use_fast) { + Advance(sizeof(value)); + return true; + } else { + return WriteRaw(bytes, sizeof(value)); + } +} + +bool CodedOutputStream::WriteLittleEndian64(uint64 value) { + uint8 bytes[sizeof(value)]; + + uint32 part0 = static_cast(value); + uint32 part1 = static_cast(value >> 32); + + bool use_fast = buffer_size_ >= sizeof(value); + uint8* ptr = use_fast ? buffer_ : bytes; + + ptr[0] = static_cast(part0 ); + ptr[1] = static_cast(part0 >> 8); + ptr[2] = static_cast(part0 >> 16); + ptr[3] = static_cast(part0 >> 24); + ptr[4] = static_cast(part1 ); + ptr[5] = static_cast(part1 >> 8); + ptr[6] = static_cast(part1 >> 16); + ptr[7] = static_cast(part1 >> 24); + + if (use_fast) { + Advance(sizeof(value)); + return true; + } else { + return WriteRaw(bytes, sizeof(value)); + } +} + +bool CodedOutputStream::WriteVarint32Fallback(uint32 value) { + if (buffer_size_ >= kMaxVarint32Bytes) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this write won't cross the end, so we can skip the checks. + uint8* target = buffer_; + + target[0] = static_cast(value | 0x80); + if (value >= (1 << 7)) { + target[1] = static_cast((value >> 7) | 0x80); + if (value >= (1 << 14)) { + target[2] = static_cast((value >> 14) | 0x80); + if (value >= (1 << 21)) { + target[3] = static_cast((value >> 21) | 0x80); + if (value >= (1 << 28)) { + target[4] = static_cast(value >> 28); + Advance(5); + } else { + target[3] &= 0x7F; + Advance(4); + } + } else { + target[2] &= 0x7F; + Advance(3); + } + } else { + target[1] &= 0x7F; + Advance(2); + } + } else { + target[0] &= 0x7F; + Advance(1); + } + + return true; + } else { + // Slow path: This write might cross the end of the buffer, so we + // compose the bytes first then use WriteRaw(). + uint8 bytes[kMaxVarint32Bytes]; + int size = 0; + while (value > 0x7F) { + bytes[size++] = (static_cast(value) & 0x7F) | 0x80; + value >>= 7; + } + bytes[size++] = static_cast(value) & 0x7F; + return WriteRaw(bytes, size); + } +} + +bool CodedOutputStream::WriteVarint64(uint64 value) { + if (buffer_size_ >= kMaxVarintBytes) { + // Fast path: We have enough bytes left in the buffer to guarantee that + // this write won't cross the end, so we can skip the checks. + uint8* target = buffer_; + + // Splitting into 32-bit pieces gives better performance on 32-bit + // processors. + uint32 part0 = static_cast(value ); + uint32 part1 = static_cast(value >> 28); + uint32 part2 = static_cast(value >> 56); + + int size; + + // Here we can't really optimize for small numbers, since the value is + // split into three parts. Cheking for numbers < 128, for instance, + // would require three comparisons, since you'd have to make sure part1 + // and part2 are zero. However, if the caller is using 64-bit integers, + // it is likely that they expect the numbers to often be very large, so + // we probably don't want to optimize for small numbers anyway. Thus, + // we end up with a hardcoded binary search tree... + if (part2 == 0) { + if (part1 == 0) { + if (part0 < (1 << 14)) { + if (part0 < (1 << 7)) { + size = 1; goto size1; + } else { + size = 2; goto size2; + } + } else { + if (part0 < (1 << 21)) { + size = 3; goto size3; + } else { + size = 4; goto size4; + } + } + } else { + if (part1 < (1 << 14)) { + if (part1 < (1 << 7)) { + size = 5; goto size5; + } else { + size = 6; goto size6; + } + } else { + if (part1 < (1 << 21)) { + size = 7; goto size7; + } else { + size = 8; goto size8; + } + } + } + } else { + if (part2 < (1 << 7)) { + size = 9; goto size9; + } else { + size = 10; goto size10; + } + } + + GOOGLE_LOG(FATAL) << "Can't get here."; + + size10: target[9] = static_cast((part2 >> 7) | 0x80); + size9 : target[8] = static_cast((part2 ) | 0x80); + size8 : target[7] = static_cast((part1 >> 21) | 0x80); + size7 : target[6] = static_cast((part1 >> 14) | 0x80); + size6 : target[5] = static_cast((part1 >> 7) | 0x80); + size5 : target[4] = static_cast((part1 ) | 0x80); + size4 : target[3] = static_cast((part0 >> 21) | 0x80); + size3 : target[2] = static_cast((part0 >> 14) | 0x80); + size2 : target[1] = static_cast((part0 >> 7) | 0x80); + size1 : target[0] = static_cast((part0 ) | 0x80); + + target[size-1] &= 0x7F; + Advance(size); + return true; + } else { + // Slow path: This write might cross the end of the buffer, so we + // compose the bytes first then use WriteRaw(). + uint8 bytes[kMaxVarintBytes]; + int size = 0; + while (value > 0x7F) { + bytes[size++] = (static_cast(value) & 0x7F) | 0x80; + value >>= 7; + } + bytes[size++] = static_cast(value) & 0x7F; + return WriteRaw(bytes, size); + } +} + +bool CodedOutputStream::Refresh() { + void* void_buffer; + if (output_->Next(&void_buffer, &buffer_size_)) { + buffer_ = reinterpret_cast(void_buffer); + total_bytes_ += buffer_size_; + return true; + } else { + buffer_ = NULL; + buffer_size_ = 0; + return false; + } +} + +int CodedOutputStream::VarintSize32Fallback(uint32 value) { + if (value < (1 << 7)) { + return 1; + } else if (value < (1 << 14)) { + return 2; + } else if (value < (1 << 21)) { + return 3; + } else if (value < (1 << 28)) { + return 4; + } else { + return 5; + } +} + +int CodedOutputStream::VarintSize64(uint64 value) { + if (value < (1ull << 35)) { + if (value < (1ull << 7)) { + return 1; + } else if (value < (1ull << 14)) { + return 2; + } else if (value < (1ull << 21)) { + return 3; + } else if (value < (1ull << 28)) { + return 4; + } else { + return 5; + } + } else { + if (value < (1ull << 42)) { + return 6; + } else if (value < (1ull << 49)) { + return 7; + } else if (value < (1ull << 56)) { + return 8; + } else if (value < (1ull << 63)) { + return 9; + } else { + return 10; + } + } +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/coded_stream.h b/src/google/protobuf/io/coded_stream.h new file mode 100644 index 00000000..91e5c56a --- /dev/null +++ b/src/google/protobuf/io/coded_stream.h @@ -0,0 +1,592 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the CodedInputStream and CodedOutputStream classes, +// which wrap a ZeroCopyInputStream or ZeroCopyOutputStream, respectively, +// and allow you to read or write individual pieces of data in various +// formats. In particular, these implement the varint encoding for +// integers, a simple variable-length encoding in which smaller numbers +// take fewer bytes. +// +// Typically these classes will only be used internally by the protocol +// buffer library in order to encode and decode protocol buffers. Clients +// of the library only need to know about this class if they wish to write +// custom message parsing or serialization procedures. +// +// CodedOutputStream example: +// // Write some data to "myfile". First we write a 4-byte "magic number" +// // to identify the file type, then write a length-delimited string. The +// // string is composed of a varint giving the length followed by the raw +// // bytes. +// int fd = open("myfile", O_WRONLY); +// ZeroCopyOutputStream* raw_output = new FileOutputStream(fd); +// CodedOutputStream* coded_output = new CodedOutputStream(raw_output); +// +// int magic_number = 1234; +// char text[] = "Hello world!"; +// coded_output->WriteLittleEndian32(magic_number); +// coded_output->WriteVarint32(strlen(text)); +// coded_output->WriteRaw(text, strlen(text)); +// +// delete coded_output; +// delete raw_output; +// close(fd); +// +// CodedInputStream example: +// // Read a file created by the above code. +// int fd = open("myfile", O_RDONLY); +// ZeroCopyInputStream* raw_input = new FileInputStream(fd); +// CodedInputStream coded_input = new CodedInputStream(raw_input); +// +// coded_input->ReadLittleEndian32(&magic_number); +// if (magic_number != 1234) { +// cerr << "File not in expected format." << endl; +// return; +// } +// +// uint32 size; +// coded_input->ReadVarint32(&size); +// +// char* text = new char[size + 1]; +// coded_input->ReadRaw(buffer, size); +// text[size] = '\0'; +// +// delete coded_input; +// delete raw_input; +// close(fd); +// +// cout << "Text is: " << text << endl; +// delete [] text; +// +// For those who are interested, varint encoding is defined as follows: +// +// The encoding operates on unsigned integers of up to 64 bits in length. +// Each byte of the encoded value has the format: +// * bits 0-6: Seven bits of the number being encoded. +// * bit 7: Zero if this is the last byte in the encoding (in which +// case all remaining bits of the number are zero) or 1 if +// more bytes follow. +// The first byte contains the least-significant 7 bits of the number, the +// second byte (if present) contains the next-least-significant 7 bits, +// and so on. So, the binary number 1011000101011 would be encoded in two +// bytes as "10101011 00101100". +// +// In theory, varint could be used to encode integers of any length. +// However, for practicality we set a limit at 64 bits. The maximum encoded +// length of a number is thus 10 bytes. + +#ifndef GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ +#define GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ + +#include +#include + +namespace google { + +namespace protobuf { +namespace io { + +// Defined in this file. +class CodedInputStream; +class CodedOutputStream; + +// Defined in other files. +class ZeroCopyInputStream; // zero_copy_stream.h +class ZeroCopyOutputStream; // zero_copy_stream.h + +// Class which reads and decodes binary data which is composed of varint- +// encoded integers and fixed-width pieces. Wraps a ZeroCopyInputStream. +// Most users will not need to deal with CodedInputStream. +// +// Most methods of CodedInputStream that return a bool return false if an +// underlying I/O error occurs or if the data is malformed. Once such a +// failure occurs, the CodedInputStream is broken and is no longer useful. +class LIBPROTOBUF_EXPORT CodedInputStream { + public: + // Create a CodedInputStream that reads from the given ZeroCopyInputStream. + explicit CodedInputStream(ZeroCopyInputStream* input); + + // Destroy the CodedInputStream and position the underlying + // ZeroCopyInputStream at the first unread byte. If an error occurred while + // reading (causing a method to return false), then the exact position of + // the input stream may be anywhere between the last value that was read + // successfully and the stream's byte limit. + ~CodedInputStream(); + + + // Skips a number of bytes. Returns false if an underlying read error + // occurs. + bool Skip(int count); + + // Read raw bytes, copying them into the given buffer. + bool ReadRaw(void* buffer, int size); + + // Like ReadRaw, but reads into a string. + // + // Implementation Note: ReadString() grows the string gradually as it + // reads in the data, rather than allocating the entire requested size + // upfront. This prevents denial-of-service attacks in which a client + // could claim that a string is going to be MAX_INT bytes long in order to + // crash the server because it can't allocate this much space at once. + bool ReadString(string* buffer, int size); + + + // Read a 32-bit little-endian integer. + bool ReadLittleEndian32(uint32* value); + // Read a 64-bit little-endian integer. + bool ReadLittleEndian64(uint64* value); + + // Read an unsigned integer with Varint encoding, truncating to 32 bits. + // Reading a 32-bit value is equivalent to reading a 64-bit one and casting + // it to uint32, but may be more efficient. + bool ReadVarint32(uint32* value); + // Read an unsigned integer with Varint encoding. + bool ReadVarint64(uint64* value); + + // Read a tag. This calls ReadVarint32() and returns the result, or returns + // zero (which is not a valid tag) if ReadVarint32() fails. Also, it updates + // the last tag value, which can be checked with LastTagWas(). + // Always inline because this is only called in once place per parse loop + // but it is called for every iteration of said loop, so it should be fast. + // GCC doesn't want to inline this by default. + uint32 ReadTag() GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Usually returns true if calling ReadVarint32() now would produce the given + // value. Will always return false if ReadVarint32() would not return the + // given value. If ExpectTag() returns true, it also advances past + // the varint. For best performance, use a compile-time constant as the + // parameter. + // Always inline because this collapses to a small number of instructions + // when given a constant parameter, but GCC doesn't want to inline by default. + bool ExpectTag(uint32 expected) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Usually returns true if no more bytes can be read. Always returns false + // if more bytes can be read. If ExpectAtEnd() returns true, a subsequent + // call to LastTagWas() will act as if ReadTag() had been called and returned + // zero, and ConsumedEntireMessage() will return true. + bool ExpectAtEnd(); + + // If the last call to ReadTag() returned the given value, returns true. + // Otherwise, returns false; + // + // This is needed because parsers for some types of embedded messages + // (with field type TYPE_GROUP) don't actually know that they've reached the + // end of a message until they see an ENDGROUP tag, which was actually part + // of the enclosing message. The enclosing message would like to check that + // tag to make sure it had the right number, so it calls LastTagWas() on + // return from the embedded parser to check. + bool LastTagWas(uint32 expected); + + // When parsing message (but NOT a group), this method must be called + // immediately after MergeFromCodedStream() returns (if it returns true) + // to further verify that the message ended in a legitimate way. For + // example, this verifies that parsing did not end on an end-group tag. + // It also checks for some cases where, due to optimizations, + // MergeFromCodedStream() can incorrectly return true. + bool ConsumedEntireMessage(); + + // Limits ---------------------------------------------------------- + // Limits are used when parsing length-delimited embedded messages. + // After the message's length is read, PushLimit() is used to prevent + // the CodedInputStream from reading beyond that length. Once the + // embedded message has been parsed, PopLimit() is called to undo the + // limit. + + // Opaque type used with PushLimit() and PopLimit(). Do not modify + // values of this type yourself. The only reason that this isn't a + // struct with private internals is for efficiency. + typedef int Limit; + + // Places a limit on the number of bytes that the stream may read, + // starting from the current position. Once the stream hits this limit, + // it will act like the end of the input has been reached until PopLimit() + // is called. + // + // As the names imply, the stream conceptually has a stack of limits. The + // shortest limit on the stack is always enforced, even if it is not the + // top limit. + // + // The value returned by PushLimit() is opaque to the caller, and must + // be passed unchanged to the corresponding call to PopLimit(). + Limit PushLimit(int byte_limit); + + // Pops the last limit pushed by PushLimit(). The input must be the value + // returned by that call to PushLimit(). + void PopLimit(Limit limit); + + // Returns the number of bytes left until the nearest limit on the + // stack is hit, or -1 if no limits are in place. + int BytesUntilLimit(); + + // Total Bytes Limit ----------------------------------------------- + // To prevent malicious users from sending excessively large messages + // and causing integer overflows or memory exhaustion, CodedInputStream + // imposes a hard limit on the total number of bytes it will read. + + // Sets the maximum number of bytes that this CodedInputStream will read + // before refusing to continue. To prevent integer overflows in the + // protocol buffers implementation, as well as to prevent servers from + // allocating enormous amounts of memory to hold parsed messages, the + // maximum message length should be limited to the shortest length that + // will not harm usability. The theoretical shortest message that could + // cause integer overflows is 512MB. The default limit is 64MB. Apps + // should set shorter limits if possible. If warning_threshold is not -1, + // a warning will be printed to stderr after warning_threshold bytes are + // read. An error will always be printed to stderr if the limit is + // reached. + // + // This is unrelated to PushLimit()/PopLimit(). + // + // Hint: If you are reading this because your program is printing a + // warning about dangerously large protocol messages, you may be + // confused about what to do next. The best option is to change your + // design such that excessively large messages are not necessary. + // For example, try to design file formats to consist of many small + // messages rather than a single large one. If this is infeasible, + // you will need to increase the limit. Chances are, though, that + // your code never constructs a CodedInputStream on which the limit + // can be set. You probably parse messages by calling things like + // Message::ParseFromString(). In this case, you will need to change + // your code to instead construct some sort of ZeroCopyInputStream + // (e.g. an ArrayInputStream), construct a CodedInputStream around + // that, then call Message::ParseFromCodedStream() instead. Then + // you can adjust the limit. Yes, it's more work, but you're doing + // something unusual. + void SetTotalBytesLimit(int total_bytes_limit, int warning_threshold); + + // Recursion Limit ------------------------------------------------- + // To prevent corrupt or malicious messages from causing stack overflows, + // we must keep track of the depth of recursion when parsing embedded + // messages and groups. CodedInputStream keeps track of this because it + // is the only object that is passed down the stack during parsing. + + // Sets the maximum recursion depth. The default is 64. + void SetRecursionLimit(int limit); + + // Increments the current recursion depth. Returns true if the depth is + // under the limit, false if it has gone over. + bool IncrementRecursionDepth(); + + // Decrements the recursion depth. + void DecrementRecursionDepth(); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedInputStream); + + ZeroCopyInputStream* input_; + const uint8* buffer_; + int buffer_size_; // size of current buffer + int total_bytes_read_; // total bytes read from input_, including + // the current buffer + + // If total_bytes_read_ surpasses INT_MAX, we record the extra bytes here + // so that we can BackUp() on destruction. + int overflow_bytes_; + + // LastTagWas() stuff. + uint32 last_tag_; // result of last ReadTag(). + + // This is set true by ReadVarint32Fallback() if it is called when exactly + // at EOF, or by ExpectAtEnd() when it returns true. This happens when we + // reach the end of a message and attempt to read another tag. + bool legitimate_message_end_; + + // See EnableAliasing(). + bool aliasing_enabled_; + + // Limits + Limit current_limit_; // if position = -1, no limit is applied + + // For simplicity, if the current buffer crosses a limit (either a normal + // limit created by PushLimit() or the total bytes limit), buffer_size_ + // only tracks the number of bytes before that limit. This field + // contains the number of bytes after it. Note that this implies that if + // buffer_size_ == 0 and buffer_size_after_limit_ > 0, we know we've + // hit a limit. However, if both are zero, it doesn't necessarily mean + // we aren't at a limit -- the buffer may have ended exactly at the limit. + int buffer_size_after_limit_; + + // Maximum number of bytes to read, period. This is unrelated to + // current_limit_. Set using SetTotalBytesLimit(). + int total_bytes_limit_; + int total_bytes_warning_threshold_; + + // Current recursion depth, controlled by IncrementRecursionDepth() and + // DecrementRecursionDepth(). + int recursion_depth_; + // Recursion depth limit, set by SetRecursionLimit(). + int recursion_limit_; + + // Advance the buffer by a given number of bytes. + void Advance(int amount); + + // Recomputes the value of buffer_size_after_limit_. Must be called after + // current_limit_ or total_bytes_limit_ changes. + void RecomputeBufferLimits(); + + // Writes an error message saying that we hit total_bytes_limit_. + void PrintTotalBytesLimitError(); + + // Called when the buffer runs out to request more data. Implies an + // Advance(buffer_size_). + bool Refresh(); + + bool ReadVarint32Fallback(uint32* value); +}; + +// Class which encodes and writes binary data which is composed of varint- +// encoded integers and fixed-width pieces. Wraps a ZeroCopyOutputStream. +// Most users will not need to deal with CodedOutputStream. +// +// Most methods of CodedOutputStream which return a bool return false if an +// underlying I/O error occurs. Once such a failure occurs, the +// CodedOutputStream is broken and is no longer useful. +class LIBPROTOBUF_EXPORT CodedOutputStream { + public: + // Create an CodedOutputStream that writes to the given ZeroCopyOutputStream. + explicit CodedOutputStream(ZeroCopyOutputStream* output); + + // Destroy the CodedOutputStream and position the underlying + // ZeroCopyOutputStream immediately after the last byte written. + ~CodedOutputStream(); + + // Write raw bytes, copying them from the given buffer. + bool WriteRaw(const void* buffer, int size); + + // Equivalent to WriteRaw(str.data(), str.size()). + bool WriteString(const string& str); + + + // Write a 32-bit little-endian integer. + bool WriteLittleEndian32(uint32 value); + // Write a 64-bit little-endian integer. + bool WriteLittleEndian64(uint64 value); + + // Write an unsigned integer with Varint encoding. Writing a 32-bit value + // is equivalent to casting it to uint64 and writing it as a 64-bit value, + // but may be more efficient. + bool WriteVarint32(uint32 value); + // Write an unsigned integer with Varint encoding. + bool WriteVarint64(uint64 value); + + // Equivalent to WriteVarint32() except when the value is negative, + // in which case it must be sign-extended to a full 10 bytes. + bool WriteVarint32SignExtended(int32 value); + + // This is identical to WriteVarint32(), but optimized for writing tags. + // In particular, if the input is a compile-time constant, this method + // compiles down to a couple instructions. + // Always inline because otherwise the aformentioned optimization can't work, + // but GCC by default doesn't want to inline this. + bool WriteTag(uint32 value) GOOGLE_ATTRIBUTE_ALWAYS_INLINE; + + // Returns the number of bytes needed to encode the given value as a varint. + static int VarintSize32(uint32 value); + // Returns the number of bytes needed to encode the given value as a varint. + static int VarintSize64(uint64 value); + + // If negative, 10 bytes. Otheriwse, same as VarintSize32(). + static int VarintSize32SignExtended(int32 value); + + // Returns the total number of bytes written since this object was created. + inline int ByteCount() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CodedOutputStream); + + ZeroCopyOutputStream* output_; + uint8* buffer_; + int buffer_size_; + int total_bytes_; // Sum of sizes of all buffers seen so far. + + // Advance the buffer by a given number of bytes. + void Advance(int amount); + + // Called when the buffer runs out to request more data. Implies an + // Advance(buffer_size_). + bool Refresh(); + + bool WriteVarint32Fallback(uint32 value); + static int VarintSize32Fallback(uint32 value); +}; + +// inline methods ==================================================== +// The vast majority of varints are only one byte. These inline +// methods optimize for that case. + +inline bool CodedInputStream::ReadVarint32(uint32* value) { + if (buffer_size_ != 0 && *buffer_ < 0x80) { + *value = *buffer_; + Advance(1); + return true; + } else { + return ReadVarint32Fallback(value); + } +} + +inline uint32 CodedInputStream::ReadTag() { + if (buffer_size_ != 0 && buffer_[0] < 0x80) { + last_tag_ = buffer_[0]; + Advance(1); + return last_tag_; + } else if (buffer_size_ >= 2 && buffer_[1] < 0x80) { + last_tag_ = (buffer_[0] & 0x7f) + (buffer_[1] << 7); + Advance(2); + return last_tag_; + } else if (ReadVarint32Fallback(&last_tag_)) { + return last_tag_; + } else { + last_tag_ = 0; + return 0; + } +} + +inline bool CodedInputStream::LastTagWas(uint32 expected) { + return last_tag_ == expected; +} + +inline bool CodedInputStream::ConsumedEntireMessage() { + return legitimate_message_end_; +} + +inline bool CodedInputStream::ExpectTag(uint32 expected) { + if (expected < (1 << 7)) { + if (buffer_size_ != 0 && buffer_[0] == expected) { + Advance(1); + return true; + } else { + return false; + } + } else if (expected < (1 << 14)) { + if (buffer_size_ >= 2 && + buffer_[0] == static_cast(expected | 0x80) && + buffer_[1] == static_cast(expected >> 7)) { + Advance(2); + return true; + } else { + return false; + } + } else { + // Don't bother optimizing for larger values. + return false; + } +} + +inline bool CodedInputStream::ExpectAtEnd() { + // If we are at a limit we know no more bytes can be read. Otherwise, it's + // hard to say without calling Refresh(), and we'd rather not do that. + + if (buffer_size_ == 0 && buffer_size_after_limit_ != 0) { + last_tag_ = 0; // Pretend we called ReadTag()... + legitimate_message_end_ = true; // ... and it hit EOF. + return true; + } else { + return false; + } +} + +inline bool CodedOutputStream::WriteVarint32(uint32 value) { + if (value < 0x80 && buffer_size_ > 0) { + *buffer_ = value; + Advance(1); + return true; + } else { + return WriteVarint32Fallback(value); + } +} + +inline bool CodedOutputStream::WriteVarint32SignExtended(int32 value) { + if (value < 0) { + return WriteVarint64(static_cast(value)); + } else { + return WriteVarint32(static_cast(value)); + } +} + +inline bool CodedOutputStream::WriteTag(uint32 value) { + if (value < (1 << 7)) { + if (buffer_size_ != 0) { + buffer_[0] = value; + Advance(1); + return true; + } + } else if (value < (1 << 14)) { + if (buffer_size_ >= 2) { + buffer_[0] = static_cast(value | 0x80); + buffer_[1] = static_cast(value >> 7); + Advance(2); + return true; + } + } + return WriteVarint32Fallback(value); +} + +inline int CodedOutputStream::VarintSize32(uint32 value) { + if (value < (1 << 7)) { + return 1; + } else { + return VarintSize32Fallback(value); + } +} + +inline int CodedOutputStream::VarintSize32SignExtended(int32 value) { + if (value < 0) { + return 10; // TODO(kenton): Make this a symbolic constant. + } else { + return VarintSize32(static_cast(value)); + } +} + +inline bool CodedOutputStream::WriteString(const string& str) { + return WriteRaw(str.data(), str.size()); +} + +inline int CodedOutputStream::ByteCount() const { + return total_bytes_ - buffer_size_; +} + +inline void CodedInputStream::Advance(int amount) { + buffer_ += amount; + buffer_size_ -= amount; +} + +inline void CodedOutputStream::Advance(int amount) { + buffer_ += amount; + buffer_size_ -= amount; +} + +inline void CodedInputStream::SetRecursionLimit(int limit) { + recursion_limit_ = limit; +} + +inline bool CodedInputStream::IncrementRecursionDepth() { + ++recursion_depth_; + return recursion_depth_ <= recursion_limit_; +} + +inline void CodedInputStream::DecrementRecursionDepth() { + if (recursion_depth_ > 0) --recursion_depth_; +} + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_CODED_STREAM_H__ diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc new file mode 100644 index 00000000..c1a88349 --- /dev/null +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -0,0 +1,929 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains tests and benchmarks. + +#include + +#include + +#include + +#include +#include +#include +#include +#include + + +// This declares an unsigned long long integer literal in a portable way. +// (The original macro is way too big and ruins my formatting.) +#undef ULL +#define ULL(x) GOOGLE_ULONGLONG(x) + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// =================================================================== +// Data-Driven Test Infrastructure + +// TEST_1D and TEST_2D are macros I'd eventually like to see added to +// gTest. These macros can be used to declare tests which should be +// run multiple times, once for each item in some input array. TEST_1D +// tests all cases in a single input array. TEST_2D tests all +// combinations of cases from two arrays. The arrays must be statically +// defined such that the GOOGLE_ARRAYSIZE() macro works on them. Example: +// +// int kCases[] = {1, 2, 3, 4} +// TEST_1D(MyFixture, MyTest, kCases) { +// EXPECT_GT(kCases_case, 0); +// } +// +// This test iterates through the numbers 1, 2, 3, and 4 and tests that +// they are all grater than zero. In case of failure, the exact case +// which failed will be printed. The case type must be printable using +// ostream::operator<<. + +#define TEST_1D(FIXTURE, NAME, CASES) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType& CASES##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES " case #" << i << ": " << CASES[i]); \ + DoSingleCase(CASES[i]); \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case) + +#define TEST_2D(FIXTURE, NAME, CASES1, CASES2) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) { \ + for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES1 " case #" << i << ": " << CASES1[i] << ", " \ + << #CASES2 " case #" << j << ": " << CASES2[j]); \ + DoSingleCase(CASES1[i], CASES2[j]); \ + } \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case) + +// =================================================================== + +class CodedStreamTest : public testing::Test { + protected: + static const int kBufferSize = 1024 * 64; + static uint8 buffer_[kBufferSize]; +}; + +uint8 CodedStreamTest::buffer_[CodedStreamTest::kBufferSize]; + +// We test each operation over a variety of block sizes to insure that +// we test cases where reads or writes cross buffer boundaries, cases +// where they don't, and cases where there is so much buffer left that +// we can use special optimized paths that don't worry about bounds +// checks. +const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024}; + +// ------------------------------------------------------------------- +// Varint tests. + +struct VarintCase { + uint8 bytes[10]; // Encoded bytes. + int size; // Encoded size, in bytes. + uint64 value; // Parsed value. +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintCase& c) { + return os << c.value; +} + +VarintCase kVarintCases[] = { + // 32-bit values + {{0x00} , 1, 0}, + {{0x01} , 1, 1}, + {{0x7f} , 1, 127}, + {{0xa2, 0x74}, 2, (0x22 << 0) | (0x74 << 7)}, // 14882 + {{0xbe, 0xf7, 0x92, 0x84, 0x0b}, 5, // 2961488830 + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (ULL(0x0b) << 28)}, + + // 64-bit + {{0xbe, 0xf7, 0x92, 0x84, 0x1b}, 5, // 7256456126 + (0x3e << 0) | (0x77 << 7) | (0x12 << 14) | (0x04 << 21) | + (ULL(0x1b) << 28)}, + {{0x80, 0xe6, 0xeb, 0x9c, 0xc3, 0xc9, 0xa4, 0x49}, 8, // 41256202580718336 + (0x00 << 0) | (0x66 << 7) | (0x6b << 14) | (0x1c << 21) | + (ULL(0x43) << 28) | (ULL(0x49) << 35) | (ULL(0x24) << 42) | + (ULL(0x49) << 49)}, + // 11964378330978735131 + {{0x9b, 0xa8, 0xf9, 0xc2, 0xbb, 0xd6, 0x80, 0x85, 0xa6, 0x01}, 10, + (0x1b << 0) | (0x28 << 7) | (0x79 << 14) | (0x42 << 21) | + (ULL(0x3b) << 28) | (ULL(0x56) << 35) | (ULL(0x00) << 42) | + (ULL(0x05) << 49) | (ULL(0x26) << 56) | (ULL(0x01) << 63)}, +}; + +TEST_2D(CodedStreamTest, ReadVarint32, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_TRUE(coded_input.ReadVarint32(&value)); + EXPECT_EQ(static_cast(kVarintCases_case.value), value); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_2D(CodedStreamTest, ReadTag, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 expected_value = static_cast(kVarintCases_case.value); + EXPECT_EQ(expected_value, coded_input.ReadTag()); + + EXPECT_TRUE(coded_input.LastTagWas(expected_value)); + EXPECT_FALSE(coded_input.LastTagWas(expected_value + 1)); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_1D(CodedStreamTest, ExpectTag, kVarintCases) { + // Leave one byte at the beginning of the buffer so we can read it + // to force the first buffer to be loaded. + buffer_[0] = '\0'; + memcpy(buffer_ + 1, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_)); + + { + CodedInputStream coded_input(&input); + + // Read one byte to force coded_input.Refill() to be called. Otherwise, + // ExpectTag() will return a false negative. + uint8 dummy; + coded_input.ReadRaw(&dummy, 1); + EXPECT_EQ((uint)'\0', (uint)dummy); + + uint32 expected_value = static_cast(kVarintCases_case.value); + + // ExpectTag() produces false negatives for large values. + if (kVarintCases_case.size <= 2) { + EXPECT_FALSE(coded_input.ExpectTag(expected_value + 1)); + EXPECT_TRUE(coded_input.ExpectTag(expected_value)); + } else { + EXPECT_FALSE(coded_input.ExpectTag(expected_value)); + } + } + + if (kVarintCases_case.size <= 2) { + EXPECT_EQ(kVarintCases_case.size + 1, input.ByteCount()); + } else { + EXPECT_EQ(1, input.ByteCount()); + } +} + +TEST_2D(CodedStreamTest, ReadVarint64, kVarintCases, kBlockSizes) { + memcpy(buffer_, kVarintCases_case.bytes, kVarintCases_case.size); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadVarint64(&value)); + EXPECT_EQ(kVarintCases_case.value, value); + } + + EXPECT_EQ(kVarintCases_case.size, input.ByteCount()); +} + +TEST_2D(CodedStreamTest, WriteVarint32, kVarintCases, kBlockSizes) { + if (kVarintCases_case.value > ULL(0x00000000FFFFFFFF)) { + // Skip this test for the 64-bit values. + return; + } + + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint32( + static_cast(kVarintCases_case.value))); + + EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount()); + } + + EXPECT_EQ(kVarintCases_case.size, output.ByteCount()); + EXPECT_EQ(0, + memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size)); +} + +TEST_2D(CodedStreamTest, WriteVarint64, kVarintCases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint64(kVarintCases_case.value)); + + EXPECT_EQ(kVarintCases_case.size, coded_output.ByteCount()); + } + + EXPECT_EQ(kVarintCases_case.size, output.ByteCount()); + EXPECT_EQ(0, + memcmp(buffer_, kVarintCases_case.bytes, kVarintCases_case.size)); +} + +// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +int32 kSignExtendedVarintCases[] = { + 0, 1, -1, 1237894, -37895138 +}; + +TEST_2D(CodedStreamTest, WriteVarint32SignExtended, + kSignExtendedVarintCases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteVarint32SignExtended( + kSignExtendedVarintCases_case)); + + if (kSignExtendedVarintCases_case < 0) { + EXPECT_EQ(10, coded_output.ByteCount()); + } else { + EXPECT_LE(coded_output.ByteCount(), 5); + } + } + + if (kSignExtendedVarintCases_case < 0) { + EXPECT_EQ(10, output.ByteCount()); + } else { + EXPECT_LE(output.ByteCount(), 5); + } + + // Read value back in as a varint64 and insure it matches. + ArrayInputStream input(buffer_, sizeof(buffer_)); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadVarint64(&value)); + + EXPECT_EQ(kSignExtendedVarintCases_case, static_cast(value)); + } + + EXPECT_EQ(output.ByteCount(), input.ByteCount()); +} + +#endif + + +// ------------------------------------------------------------------- +// Varint failure test. + +struct VarintErrorCase { + uint8 bytes[12]; + int size; + bool can_parse; +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintErrorCase& c) { + return os << "size " << c.size; +} + +const VarintErrorCase kVarintErrorCases[] = { + // Control case. (Insures that there isn't something else wrong that + // makes parsing always fail.) + {{0x00}, 1, true}, + + // No input data. + {{}, 0, false}, + + // Input ends unexpectedly. + {{0xf0, 0xab}, 2, false}, + + // Input ends unexpectedly after 32 bits. + {{0xf0, 0xab, 0xc9, 0x9a, 0xf8, 0xb2}, 6, false}, + + // Longer than 10 bytes. + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01}, + 11, false}, +}; + +TEST_2D(CodedStreamTest, ReadVarint32Error, kVarintErrorCases, kBlockSizes) { + memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size); + ArrayInputStream input(buffer_, kVarintErrorCases_case.size, + kBlockSizes_case); + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint32(&value)); +} + +TEST_2D(CodedStreamTest, ReadVarint64Error, kVarintErrorCases, kBlockSizes) { + memcpy(buffer_, kVarintErrorCases_case.bytes, kVarintErrorCases_case.size); + ArrayInputStream input(buffer_, kVarintErrorCases_case.size, + kBlockSizes_case); + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_EQ(kVarintErrorCases_case.can_parse, coded_input.ReadVarint64(&value)); +} + +// ------------------------------------------------------------------- +// VarintSize + +struct VarintSizeCase { + uint64 value; + int size; +}; + +inline std::ostream& operator<<(std::ostream& os, const VarintSizeCase& c) { + return os << c.value; +} + +VarintSizeCase kVarintSizeCases[] = { + {0u, 1}, + {1u, 1}, + {127u, 1}, + {128u, 2}, + {758923u, 3}, + {4000000000u, 5}, + {ULL(41256202580718336), 8}, + {ULL(11964378330978735131), 10}, +}; + +TEST_1D(CodedStreamTest, VarintSize32, kVarintSizeCases) { + if (kVarintSizeCases_case.value > 0xffffffffu) { + // Skip 64-bit values. + return; + } + + EXPECT_EQ(kVarintSizeCases_case.size, + CodedOutputStream::VarintSize32( + static_cast(kVarintSizeCases_case.value))); +} + +TEST_1D(CodedStreamTest, VarintSize64, kVarintSizeCases) { + EXPECT_EQ(kVarintSizeCases_case.size, + CodedOutputStream::VarintSize64(kVarintSizeCases_case.value)); +} + +// ------------------------------------------------------------------- +// Fixed-size int tests + +struct Fixed32Case { + uint8 bytes[sizeof(uint32)]; // Encoded bytes. + uint32 value; // Parsed value. +}; + +struct Fixed64Case { + uint8 bytes[sizeof(uint64)]; // Encoded bytes. + uint64 value; // Parsed value. +}; + +inline std::ostream& operator<<(std::ostream& os, const Fixed32Case& c) { + return os << "0x" << hex << c.value << dec; +} + +inline std::ostream& operator<<(std::ostream& os, const Fixed64Case& c) { + return os << "0x" << hex << c.value << dec; +} + +Fixed32Case kFixed32Cases[] = { + {{0xef, 0xcd, 0xab, 0x90}, 0x90abcdefu}, + {{0x12, 0x34, 0x56, 0x78}, 0x78563412u}, +}; + +Fixed64Case kFixed64Cases[] = { + {{0xef, 0xcd, 0xab, 0x90, 0x12, 0x34, 0x56, 0x78}, ULL(0x7856341290abcdef)}, + {{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, ULL(0x8877665544332211)}, +}; + +TEST_2D(CodedStreamTest, ReadLittleEndian32, kFixed32Cases, kBlockSizes) { + memcpy(buffer_, kFixed32Cases_case.bytes, sizeof(kFixed32Cases_case.bytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint32 value; + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(kFixed32Cases_case.value, value); + } + + EXPECT_EQ(sizeof(uint32), input.ByteCount()); +} + +TEST_2D(CodedStreamTest, ReadLittleEndian64, kFixed64Cases, kBlockSizes) { + memcpy(buffer_, kFixed64Cases_case.bytes, sizeof(kFixed64Cases_case.bytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + uint64 value; + EXPECT_TRUE(coded_input.ReadLittleEndian64(&value)); + EXPECT_EQ(kFixed64Cases_case.value, value); + } + + EXPECT_EQ(sizeof(uint64), input.ByteCount()); +} + +TEST_2D(CodedStreamTest, WriteLittleEndian32, kFixed32Cases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteLittleEndian32(kFixed32Cases_case.value)); + + EXPECT_EQ(sizeof(uint32), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(uint32), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kFixed32Cases_case.bytes, sizeof(uint32))); +} + +TEST_2D(CodedStreamTest, WriteLittleEndian64, kFixed64Cases, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteLittleEndian64(kFixed64Cases_case.value)); + + EXPECT_EQ(sizeof(uint64), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(uint64), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kFixed64Cases_case.bytes, sizeof(uint64))); +} + +// ------------------------------------------------------------------- +// Raw reads and writes + +const char kRawBytes[] = "Some bytes which will be writted and read raw."; + +TEST_1D(CodedStreamTest, ReadRaw, kBlockSizes) { + memcpy(buffer_, kRawBytes, sizeof(kRawBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + char read_buffer[sizeof(kRawBytes)]; + + { + CodedInputStream coded_input(&input); + + EXPECT_TRUE(coded_input.ReadRaw(read_buffer, sizeof(kRawBytes))); + EXPECT_EQ(0, memcmp(kRawBytes, read_buffer, sizeof(kRawBytes))); + } + + EXPECT_EQ(sizeof(kRawBytes), input.ByteCount()); +} + +TEST_1D(CodedStreamTest, WriteRaw, kBlockSizes) { + ArrayOutputStream output(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedOutputStream coded_output(&output); + + EXPECT_TRUE(coded_output.WriteRaw(kRawBytes, sizeof(kRawBytes))); + + EXPECT_EQ(sizeof(kRawBytes), coded_output.ByteCount()); + } + + EXPECT_EQ(sizeof(kRawBytes), output.ByteCount()); + EXPECT_EQ(0, memcmp(buffer_, kRawBytes, sizeof(kRawBytes))); +} + +TEST_1D(CodedStreamTest, ReadString, kBlockSizes) { + memcpy(buffer_, kRawBytes, sizeof(kRawBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, strlen(kRawBytes))); + EXPECT_EQ(kRawBytes, str); + } + + EXPECT_EQ(strlen(kRawBytes), input.ByteCount()); +} + +// Check to make sure ReadString doesn't crash on impossibly large strings. +TEST_1D(CodedStreamTest, ReadStringImpossiblyLarge, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + // Try to read a gigabyte. + EXPECT_FALSE(coded_input.ReadString(&str, 1 << 30)); + } +} + + +// ------------------------------------------------------------------- +// Skip + +const char kSkipTestBytes[] = + ""; +const char kSkipOutputTestBytes[] = + "---------------------------------"; + +TEST_1D(CodedStreamTest, SkipInput, kBlockSizes) { + memcpy(buffer_, kSkipTestBytes, sizeof(kSkipTestBytes)); + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, strlen(""))); + EXPECT_EQ("", str); + EXPECT_TRUE(coded_input.Skip(strlen(""))); + EXPECT_TRUE(coded_input.ReadString(&str, strlen(""))); + EXPECT_EQ("", str); + } + + EXPECT_EQ(strlen(kSkipTestBytes), input.ByteCount()); +} + +// ------------------------------------------------------------------- +// Limits + +TEST_1D(CodedStreamTest, BasicLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit = coded_input.PushLimit(8); + + // Read until we hit the limit. + uint32 value; + EXPECT_EQ(8, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(12, input.ByteCount()); +} + +// Test what happens when we push two limits where the second (top) one is +// shorter. +TEST_1D(CodedStreamTest, SmallLimitOnTopOfBigLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit1 = coded_input.PushLimit(8); + EXPECT_EQ(8, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit2 = coded_input.PushLimit(4); + + uint32 value; + + // Read until we hit limit2, the top and shortest limit. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit2); + + // Read until we hit limit1. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit1); + + // No more limits. + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(12, input.ByteCount()); +} + +// Test what happens when we push two limits where the second (top) one is +// longer. In this case, the top limit is shortened to match the previous +// limit. +TEST_1D(CodedStreamTest, BigLimitOnTopOfSmallLimit, kBlockSizes) { + ArrayInputStream input(buffer_, sizeof(buffer_), kBlockSizes_case); + + { + CodedInputStream coded_input(&input); + + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit1 = coded_input.PushLimit(4); + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + CodedInputStream::Limit limit2 = coded_input.PushLimit(8); + + uint32 value; + + // Read until we hit limit2. Except, wait! limit1 is shorter, so + // we end up hitting that first, despite having 4 bytes to go on + // limit2. + EXPECT_EQ(4, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit2); + + // OK, popped limit2, now limit1 is on top, which we've already hit. + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + EXPECT_FALSE(coded_input.ReadLittleEndian32(&value)); + EXPECT_EQ(0, coded_input.BytesUntilLimit()); + + coded_input.PopLimit(limit1); + + // No more limits. + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + } + + EXPECT_EQ(8, input.ByteCount()); +} + +TEST_F(CodedStreamTest, ExpectAtEnd) { + // Test ExpectAtEnd(), which is based on limits. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + EXPECT_FALSE(coded_input.ExpectAtEnd()); + + CodedInputStream::Limit limit = coded_input.PushLimit(4); + + uint32 value; + EXPECT_TRUE(coded_input.ReadLittleEndian32(&value)); + EXPECT_TRUE(coded_input.ExpectAtEnd()); + + coded_input.PopLimit(limit); + EXPECT_FALSE(coded_input.ExpectAtEnd()); +} + +TEST_F(CodedStreamTest, NegativeLimit) { + // Check what happens when we push a negative limit. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + CodedInputStream::Limit limit = coded_input.PushLimit(-1234); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, NegativeLimitAfterReading) { + // Check what happens when we push a negative limit. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + ASSERT_TRUE(coded_input.Skip(128)); + + CodedInputStream::Limit limit = coded_input.PushLimit(-64); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, OverflowLimit) { + // Check what happens when we push a limit large enough that its absolute + // position is more than 2GB into the stream. + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + ASSERT_TRUE(coded_input.Skip(128)); + + CodedInputStream::Limit limit = coded_input.PushLimit(INT_MAX); + // BytesUntilLimit() returns -1 to mean "no limit", which actually means + // "the limit is INT_MAX relative to the beginning of the stream". + EXPECT_EQ(-1, coded_input.BytesUntilLimit()); + coded_input.PopLimit(limit); +} + +TEST_F(CodedStreamTest, TotalBytesLimit) { + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + coded_input.SetTotalBytesLimit(16, -1); + + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 16)); + + vector errors; + + { + ScopedMemoryLog error_log; + EXPECT_FALSE(coded_input.ReadString(&str, 1)); + errors = error_log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_PRED_FORMAT2(testing::IsSubstring, + "A protocol message was rejected because it was too big", errors[0]); + + coded_input.SetTotalBytesLimit(32, -1); + EXPECT_TRUE(coded_input.ReadString(&str, 16)); +} + +TEST_F(CodedStreamTest, TotalBytesLimitNotValidMessageEnd) { + // total_bytes_limit_ is not a valid place for a message to end. + + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + + // Set both total_bytes_limit and a regular limit at 16 bytes. + coded_input.SetTotalBytesLimit(16, -1); + CodedInputStream::Limit limit = coded_input.PushLimit(16); + + // Read 16 bytes. + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 16)); + + // Read a tag. Should fail, but report being a valid endpoint since it's + // a regular limit. + EXPECT_EQ(0, coded_input.ReadTag()); + EXPECT_TRUE(coded_input.ConsumedEntireMessage()); + + // Pop the limit. + coded_input.PopLimit(limit); + + // Read a tag. Should fail, and report *not* being a valid endpoint, since + // this time we're hitting the total bytes limit. + EXPECT_EQ(0, coded_input.ReadTag()); + EXPECT_FALSE(coded_input.ConsumedEntireMessage()); +} + +TEST_F(CodedStreamTest, RecursionLimit) { + ArrayInputStream input(buffer_, sizeof(buffer_)); + CodedInputStream coded_input(&input); + coded_input.SetRecursionLimit(4); + + // This is way too much testing for a counter. + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 1 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 2 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 6 + coded_input.DecrementRecursionDepth(); // 5 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 6 + coded_input.DecrementRecursionDepth(); // 5 + coded_input.DecrementRecursionDepth(); // 4 + coded_input.DecrementRecursionDepth(); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + coded_input.DecrementRecursionDepth(); // 4 + coded_input.DecrementRecursionDepth(); // 3 + coded_input.DecrementRecursionDepth(); // 2 + coded_input.DecrementRecursionDepth(); // 1 + coded_input.DecrementRecursionDepth(); // 0 + coded_input.DecrementRecursionDepth(); // 0 + coded_input.DecrementRecursionDepth(); // 0 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 1 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 2 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 3 + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 4 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 5 + + coded_input.SetRecursionLimit(6); + EXPECT_TRUE(coded_input.IncrementRecursionDepth()); // 6 + EXPECT_FALSE(coded_input.IncrementRecursionDepth()); // 7 +} + +class ReallyBigInputStream : public ZeroCopyInputStream { + public: + ReallyBigInputStream() : backup_amount_(0), buffer_count_(0) {} + ~ReallyBigInputStream() {} + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size) { + // We only expect BackUp() to be called at the end. + EXPECT_EQ(0, backup_amount_); + + switch (buffer_count_++) { + case 0: + *data = buffer_; + *size = sizeof(buffer_); + return true; + case 1: + // Return an enormously large buffer that, when combined with the 1k + // returned already, should overflow the total_bytes_read_ counter in + // CodedInputStream. Note that we'll only read the first 1024 bytes + // of this buffer so it's OK that we have it point at buffer_. + *data = buffer_; + *size = INT_MAX; + return true; + default: + return false; + } + } + + void BackUp(int count) { + backup_amount_ = count; + } + + bool Skip(int count) { GOOGLE_LOG(FATAL) << "Not implemented."; return false; } + int64 ByteCount() const { GOOGLE_LOG(FATAL) << "Not implemented."; return 0; } + + int backup_amount_; + + private: + char buffer_[1024]; + int64 buffer_count_; +}; + +TEST_F(CodedStreamTest, InputOver2G) { + // CodedInputStream should gracefully handle input over 2G and call + // input.BackUp() with the correct number of bytes on destruction. + ReallyBigInputStream input; + + vector errors; + + { + ScopedMemoryLog error_log; + CodedInputStream coded_input(&input); + string str; + EXPECT_TRUE(coded_input.ReadString(&str, 512)); + EXPECT_TRUE(coded_input.ReadString(&str, 1024)); + errors = error_log.GetMessages(ERROR); + } + + EXPECT_EQ(INT_MAX - 512, input.backup_amount_); + EXPECT_EQ(0, errors.size()); +} + +// =================================================================== + + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/package_info.h b/src/google/protobuf/io/package_info.h new file mode 100644 index 00000000..8272d51d --- /dev/null +++ b/src/google/protobuf/io/package_info.h @@ -0,0 +1,40 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf::io namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +namespace protobuf { + +// Auxiliary classes used for I/O. +// +// The Protocol Buffer library uses the classes in this package to deal with +// I/O and encoding/decoding raw bytes. Most users will not need to +// deal with this package. However, users who want to adapt the system to +// work with their own I/O abstractions -- e.g., to allow Protocol Buffers +// to be read from a different kind of input stream without the need for a +// temporary buffer -- should take a closer look. +namespace io {} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/printer.cc b/src/google/protobuf/io/printer.cc new file mode 100644 index 00000000..7b3e3de4 --- /dev/null +++ b/src/google/protobuf/io/printer.cc @@ -0,0 +1,165 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter) + : variable_delimiter_(variable_delimiter), + output_(output), + buffer_(NULL), + buffer_size_(0), + at_start_of_line_(true), + failed_(false) { +} + +Printer::~Printer() { + // Only BackUp() if we're sure we've successfully called Next() at least once. + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } +} + +void Printer::Print(const map& variables, const char* text) { + int size = strlen(text); + int pos = 0; // The number of bytes we've written so far. + + for (int i = 0; i < size; i++) { + if (text[i] == '\n') { + // Saw newline. If there is more text, we may need to insert an indent + // here. So, write what we have so far, including the '\n'. + Write(text + pos, i - pos + 1); + pos = i + 1; + + // Setting this true will cause the next Write() to insert an indent + // first. + at_start_of_line_ = true; + + } else if (text[i] == variable_delimiter_) { + // Saw the start of a variable name. + + // Write what we have so far. + Write(text + pos, i - pos); + pos = i + 1; + + // Find closing delimiter. + const char* end = strchr(text + pos, variable_delimiter_); + if (end == NULL) { + GOOGLE_LOG(DFATAL) << " Unclosed variable name."; + end = text + pos; + } + int endpos = end - text; + + string varname(text + pos, endpos - pos); + if (varname.empty()) { + // Two delimiters in a row reduce to a literal delimiter character. + Write(&variable_delimiter_, 1); + } else { + // Replace with the variable's value. + map::const_iterator iter = variables.find(varname); + if (iter == variables.end()) { + GOOGLE_LOG(DFATAL) << " Undefined variable: " << varname; + } else { + Write(iter->second.data(), iter->second.size()); + } + } + + // Advance past this variable. + i = endpos; + pos = endpos + 1; + } + } + + // Write the rest. + Write(text + pos, size - pos); +} + +void Printer::Print(const char* text) { + static map empty; + Print(empty, text); +} + +void Printer::Print(const char* text, + const char* variable, const string& value) { + map vars; + vars[variable] = value; + Print(vars, text); +} + +void Printer::Print(const char* text, + const char* variable1, const string& value1, + const char* variable2, const string& value2) { + map vars; + vars[variable1] = value1; + vars[variable2] = value2; + Print(vars, text); +} + +void Printer::Indent() { + indent_ += " "; +} + +void Printer::Outdent() { + if (indent_.empty()) { + GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent()."; + return; + } + + indent_.resize(indent_.size() - 2); +} + +void Printer::Write(const char* data, int size) { + if (failed_) return; + if (size == 0) return; + + if (at_start_of_line_) { + // Insert an indent. + at_start_of_line_ = false; + Write(indent_.data(), indent_.size()); + if (failed_) return; + } + + while (size > buffer_size_) { + // Data exceeds space in the buffer. Copy what we can and request a + // new buffer. + memcpy(buffer_, data, buffer_size_); + data += buffer_size_; + size -= buffer_size_; + void* void_buffer; + failed_ = !output_->Next(&void_buffer, &buffer_size_); + if (failed_) return; + buffer_ = reinterpret_cast(void_buffer); + } + + // Buffer is big enough to receive the data; copy it. + memcpy(buffer_, data, size); + buffer_ += size; + buffer_size_ -= size; +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h new file mode 100644 index 00000000..ee8f46c8 --- /dev/null +++ b/src/google/protobuf/io/printer.h @@ -0,0 +1,109 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Utility class for writing text to a ZeroCopyOutputStream. + +#ifndef GOOGLE_PROTOBUF_IO_PRINTER_H__ +#define GOOGLE_PROTOBUF_IO_PRINTER_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +class ZeroCopyOutputStream; // zero_copy_stream.h + +// This simple utility class assists in code generation. It basically +// allows the caller to define a set of variables and then output some +// text with variable substitutions. Example usage: +// +// Printer printer(output, '$'); +// map vars; +// vars["name"] = "Bob"; +// printer.Print(vars, "My name is $name$."); +// +// The above writes "My name is Bob." to the output stream. +// +// Printer aggressively enforces correct usage, crashing (with assert failures) +// in the case of undefined variables. This helps greatly in debugging code +// which uses it. This class is not intended to be used by production servers. +class LIBPROTOBUF_EXPORT Printer { + public: + // Create a printer that writes text to the given output stream. Use the + // given character as the delimiter for variables. + Printer(ZeroCopyOutputStream* output, char variable_delimiter); + ~Printer(); + + // Print some text after applying variable substitutions. If a particular + // variable in the text is not defined, this will crash. Variables to be + // substituted are identified by their names surrounded by delimiter + // characters (as given to the constructor). The variable bindings are + // defined by the given map. + void Print(const map& variables, const char* text); + + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable, const string& value); + // Like the first Print(), except the substitutions are given as parameters. + void Print(const char* text, const char* variable1, const string& value1, + const char* variable2, const string& value2); + // TODO(kenton): Overloaded versions with more variables? Two seems + // to be enough. + + // Indent text by two spaces. After calling Indent(), two spaces will be + // inserted at the beginning of each line of text. Indent() may be called + // multiple times to produce deeper indents. + void Indent(); + + // Reduces the current indent level by two spaces, or crashes if the indent + // level is zero. + void Outdent(); + + // True if any write to the underlying stream failed. (We don't just + // crash in this case because this is an I/O failure, not a programming + // error.) + bool failed() const { return failed_; } + + private: + // Write some text to the output buffer. + void Write(const char* data, int size); + + const char variable_delimiter_; + + ZeroCopyOutputStream* const output_; + char* buffer_; + int buffer_size_; + + string indent_; + bool at_start_of_line_; + bool failed_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Printer); +}; + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_PRINTER_H__ diff --git a/src/google/protobuf/io/printer_unittest.cc b/src/google/protobuf/io/printer_unittest.cc new file mode 100644 index 00000000..652728bc --- /dev/null +++ b/src/google/protobuf/io/printer_unittest.cc @@ -0,0 +1,210 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// Each test repeats over several block sizes in order to test both cases +// where particular writes cross a buffer boundary and cases where they do +// not. + +TEST(Printer, EmptyPrinter) { + char buffer[8192]; + const int block_size = 100; + ArrayOutputStream output(buffer, GOOGLE_ARRAYSIZE(buffer), block_size); + Printer printer(&output, '\0'); + EXPECT_TRUE(!printer.failed()); +} + +TEST(Printer, BasicPrinting) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '\0'); + + printer.Print("Hello World!"); + printer.Print(" This is the same line.\n"); + printer.Print("But this is a new one.\nAnd this is another one."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World! This is the same line.\n" + "But this is a new one.\n" + "And this is another one."); + } +} + +TEST(Printer, VariableSubstitution) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '$'); + map vars; + + vars["foo"] = "World"; + vars["bar"] = "$foo$"; + vars["abcdefg"] = "1234"; + + printer.Print(vars, "Hello $foo$!\nbar = $bar$\n"); + printer.Print(vars, "$abcdefg$\nA literal dollar sign: $$"); + + vars["foo"] = "blah"; + printer.Print(vars, "\nNow foo = $foo$."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World!\n" + "bar = $foo$\n" + "1234\n" + "A literal dollar sign: $\n" + "Now foo = blah."); + } +} + +TEST(Printer, InlineVariableSubstitution) { + char buffer[8192]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + + { + Printer printer(&output, '$'); + printer.Print("Hello $foo$!\n", "foo", "World"); + printer.Print("$foo$ $bar$\n", "foo", "one", "bar", "two"); + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "Hello World!\n" + "one two\n"); +} + +TEST(Printer, Indenting) { + char buffer[8192]; + + for (int block_size = 1; block_size < 512; block_size *= 2) { + ArrayOutputStream output(buffer, sizeof(buffer), block_size); + + { + Printer printer(&output, '$'); + map vars; + + vars["newline"] = "\n"; + + printer.Print("This is not indented.\n"); + printer.Indent(); + printer.Print("This is indented\nAnd so is this\n"); + printer.Outdent(); + printer.Print("But this is not."); + printer.Indent(); + printer.Print(" And this is still the same line.\n" + "But this is indented.\n"); + printer.Print(vars, "Note that a newline in a variable will break " + "indenting, as we see$newline$here.\n"); + printer.Indent(); + printer.Print("And this"); + printer.Outdent(); + printer.Outdent(); + printer.Print(" is double-indented\nBack to normal."); + + EXPECT_FALSE(printer.failed()); + } + + buffer[output.ByteCount()] = '\0'; + + EXPECT_STREQ(buffer, + "This is not indented.\n" + " This is indented\n" + " And so is this\n" + "But this is not. And this is still the same line.\n" + " But this is indented.\n" + " Note that a newline in a variable will break indenting, as we see\n" + "here.\n" + " And this is double-indented\n" + "Back to normal."); + } +} + +// Death tests do not work on Windows as of yet. +#ifdef GTEST_HAS_DEATH_TEST +TEST(Printer, Death) { + char buffer[8192]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + Printer printer(&output, '$'); + + EXPECT_DEBUG_DEATH(printer.Print("$nosuchvar$"), "Undefined variable"); + EXPECT_DEBUG_DEATH(printer.Print("$unclosed"), "Unclosed variable name"); + EXPECT_DEBUG_DEATH(printer.Outdent(), "without matching Indent"); +} +#endif // GTEST_HAS_DEATH_TEST + +TEST(Printer, WriteFailure) { + char buffer[16]; + + ArrayOutputStream output(buffer, sizeof(buffer)); + Printer printer(&output, '$'); + + // Print 16 bytes to fill the buffer exactly (should not fail). + printer.Print("0123456789abcdef"); + EXPECT_FALSE(printer.failed()); + + // Try to print one more byte (should fail). + printer.Print(" "); + EXPECT_TRUE(printer.failed()); + + // Should not crash + printer.Print("blah"); + EXPECT_TRUE(printer.failed()); + + // Buffer should contain the first 16 bytes written. + EXPECT_EQ("0123456789abcdef", string(buffer, sizeof(buffer))); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc new file mode 100644 index 00000000..3864fcfb --- /dev/null +++ b/src/google/protobuf/io/tokenizer.cc @@ -0,0 +1,679 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Here we have a hand-written lexer. At first you might ask yourself, +// "Hand-written text processing? Is Kenton crazy?!" Well, first of all, +// yes I am crazy, but that's beside the point. There are actually reasons +// why I ended up writing this this way. +// +// The traditional approach to lexing is to use lex to generate a lexer for +// you. Unfortunately, lex's output is ridiculously ugly and difficult to +// integrate cleanly with C++ code, especially abstract code or code meant +// as a library. Better parser-generators exist but would add dependencies +// which most users won't already have, which we'd like to avoid. (GNU flex +// has a C++ output option, but it's still ridiculously ugly, non-abstract, +// and not library-friendly.) +// +// The next approach that any good software engineer should look at is to +// use regular expressions. And, indeed, I did. I have code which +// implements this same class using regular expressions. It's about 200 +// lines shorter. However: +// - Rather than error messages telling you "This string has an invalid +// escape sequence at line 5, column 45", you get error messages like +// "Parse error on line 5". Giving more precise errors requires adding +// a lot of code that ends up basically as complex as the hand-coded +// version anyway. +// - The regular expression to match a string literal looks like this: +// kString = new RE("(\"([^\"\\\\]|" // non-escaped +// "\\\\[abfnrtv?\"'\\\\0-7]|" // normal escape +// "\\\\x[0-9a-fA-F])*\"|" // hex escape +// "\'([^\'\\\\]|" // Also support single-quotes. +// "\\\\[abfnrtv?\"'\\\\0-7]|" +// "\\\\x[0-9a-fA-F])*\')"); +// Verifying the correctness of this line noise is actually harder than +// verifying the correctness of ConsumeString(), defined below. I'm not +// even confident that the above is correct, after staring at it for some +// time. +// - PCRE is fast, but there's still more overhead involved than the code +// below. +// - Sadly, regular expressions are not part of the C standard library, so +// using them would require depending on some other library. For the +// open source release, this could be really annoying. Nobody likes +// downloading one piece of software just to find that they need to +// download something else to make it work, and in all likelihood +// people downloading Protocol Buffers will already be doing so just +// to make something else work. We could include a copy of PCRE with +// our code, but that obligates us to keep it up-to-date and just seems +// like a big waste just to save 200 lines of code. +// +// On a similar but unrelated note, I'm even scared to use ctype.h. +// Apparently functions like isalpha() are locale-dependent. So, if we used +// that, then if this code is being called from some program that doesn't +// have its locale set to "C", it would behave strangely. We can't just set +// the locale to "C" ourselves since we might break the calling program that +// way, particularly if it is multi-threaded. WTF? Someone please let me +// (Kenton) know if I'm missing something here... +// +// I'd love to hear about other alternatives, though, as this code isn't +// exactly pretty. + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// As mentioned above, I don't trust ctype.h due to the presence of "locales". +// So, I have written replacement functions here. Someone please smack me if +// this is a bad idea or if there is some way around this. +// +// These "character classes" are designed to be used in template methods. +// For instance, Tokenizer::ConsumeZeroOrMore() will eat +// whitespace. + +// Note: No class is allowed to contain '\0', since this is used to mark end- +// of-input and is handled specially. + +#define CHARACTER_CLASS(NAME, EXPRESSION) \ + class NAME { \ + public: \ + static inline bool InClass(char c) { \ + return EXPRESSION; \ + } \ + } + +CHARACTER_CLASS(Whitespace, c == ' ' || c == '\n' || c == '\t' || + c == '\r' || c == '\v'); + +CHARACTER_CLASS(Unprintable, c < ' ' && c != '\0'); + +CHARACTER_CLASS(Digit, '0' <= c && c <= '9'); +CHARACTER_CLASS(OctalDigit, '0' <= c && c <= '7'); +CHARACTER_CLASS(HexDigit, ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F')); + +CHARACTER_CLASS(Letter, ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + (c == '_')); + +CHARACTER_CLASS(Alphanumeric, ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + (c == '_')); + +CHARACTER_CLASS(Escape, c == 'a' || c == 'b' || c == 'f' || c == 'n' || + c == 'r' || c == 't' || c == 'v' || c == '\\' || + c == '?' || c == '\'' || c == '\"'); + +#undef CHARACTER_CLASS + +// Given a char, interpret it as a numeric digit and return its value. +// This supports any number base up to 36. +inline int DigitValue(char digit) { + if ('0' <= digit && digit <= '9') return digit - '0'; + if ('a' <= digit && digit <= 'z') return digit - 'a' + 10; + if ('A' <= digit && digit <= 'Z') return digit - 'A' + 10; + return -1; +} + +// Inline because it's only used in one place. +inline char TranslateEscape(char c) { + switch (c) { + case 'a': return '\a'; + case 'b': return '\b'; + case 'f': return '\f'; + case 'n': return '\n'; + case 'r': return '\r'; + case 't': return '\t'; + case 'v': return '\v'; + case '\\': return '\\'; + case '?': return '\?'; // Trigraphs = :( + case '\'': return '\''; + case '"': return '\"'; + + // We expect escape sequences to have been validated separately. + default: return '?'; + } +} + +} // anonymous namespace + +ErrorCollector::~ErrorCollector() {} + +// =================================================================== + +Tokenizer::Tokenizer(ZeroCopyInputStream* input, + ErrorCollector* error_collector) + : input_(input), + error_collector_(error_collector), + buffer_(NULL), + buffer_size_(0), + buffer_pos_(0), + read_error_(false), + line_(0), + column_(0), + token_start_(-1), + allow_f_after_float_(false), + comment_style_(CPP_COMMENT_STYLE) { + + current_.line = 0; + current_.column = 0; + current_.type = TYPE_START; + + Refresh(); +} + +Tokenizer::~Tokenizer() { + // If we had any buffer left unread, return it to the underlying stream + // so that someone else can read it. + if (buffer_size_ > buffer_pos_) { + input_->BackUp(buffer_size_ - buffer_pos_); + } +} + +// ------------------------------------------------------------------- +// Internal helpers. + +void Tokenizer::NextChar() { + // Update our line and column counters based on the character being + // consumed. + if (current_char_ == '\n') { + ++line_; + column_ = 0; + } else if (current_char_ == '\t') { + column_ += kTabWidth - column_ % kTabWidth; + } else { + ++column_; + } + + // Advance to the next character. + ++buffer_pos_; + if (buffer_pos_ < buffer_size_) { + current_char_ = buffer_[buffer_pos_]; + } else { + Refresh(); + } +} + +void Tokenizer::Refresh() { + if (read_error_) { + current_char_ = '\0'; + return; + } + + // If we're in a token, append the rest of the buffer to it. + if (token_start_ >= 0 && token_start_ < buffer_size_) { + current_.text.append(buffer_ + token_start_, buffer_size_ - token_start_); + token_start_ = 0; + } + + const void* data = NULL; + buffer_ = NULL; + buffer_pos_ = 0; + do { + if (!input_->Next(&data, &buffer_size_)) { + // end of stream (or read error) + buffer_size_ = 0; + read_error_ = true; + current_char_ = '\0'; + return; + } + } while (buffer_size_ == 0); + + buffer_ = static_cast(data); + + current_char_ = buffer_[0]; +} + +inline void Tokenizer::StartToken() { + token_start_ = buffer_pos_; + current_.type = TYPE_START; // Just for the sake of initializing it. + current_.text.clear(); + current_.line = line_; + current_.column = column_; +} + +inline void Tokenizer::EndToken() { + // Note: The if() is necessary because some STL implementations crash when + // you call string::append(NULL, 0), presumably because they are trying to + // be helpful by detecting the NULL pointer, even though there's nothing + // wrong with reading zero bytes from NULL. + if (buffer_pos_ != token_start_) { + current_.text.append(buffer_ + token_start_, buffer_pos_ - token_start_); + } + token_start_ = -1; +} + +// ------------------------------------------------------------------- +// Helper methods that consume characters. + +template +inline bool Tokenizer::LookingAt() { + return CharacterClass::InClass(current_char_); +} + +template +inline bool Tokenizer::TryConsumeOne() { + if (CharacterClass::InClass(current_char_)) { + NextChar(); + return true; + } else { + return false; + } +} + +inline bool Tokenizer::TryConsume(char c) { + if (current_char_ == c) { + NextChar(); + return true; + } else { + return false; + } +} + +template +inline void Tokenizer::ConsumeZeroOrMore() { + while (CharacterClass::InClass(current_char_)) { + NextChar(); + } +} + +template +inline void Tokenizer::ConsumeOneOrMore(const char* error) { + if (!CharacterClass::InClass(current_char_)) { + AddError(error); + } else { + do { + NextChar(); + } while (CharacterClass::InClass(current_char_)); + } +} + +// ------------------------------------------------------------------- +// Methods that read whole patterns matching certain kinds of tokens +// or comments. + +void Tokenizer::ConsumeString(char delimiter) { + while (true) { + switch (current_char_) { + case '\0': + case '\n': { + AddError("String literals cannot cross line boundaries."); + return; + } + + case '\\': { + // An escape sequence. + NextChar(); + if (TryConsumeOne()) { + // Valid escape sequence. + } else if (TryConsumeOne()) { + // Possibly followed by two more octal digits, but these will + // just be consumed by the main loop anyway so we don't need + // to do so explicitly here. + } else if (TryConsume('x') || TryConsume('X')) { + if (!TryConsumeOne()) { + AddError("Expected hex digits for escape sequence."); + } + // Possibly followed by another hex digit, but again we don't care. + } else { + AddError("Invalid escape sequence in string literal."); + } + break; + } + + default: { + if (current_char_ == delimiter) { + NextChar(); + return; + } + NextChar(); + break; + } + } + } +} + +Tokenizer::TokenType Tokenizer::ConsumeNumber(bool started_with_zero, + bool started_with_dot) { + bool is_float = false; + + if (started_with_zero && (TryConsume('x') || TryConsume('X'))) { + // A hex number (started with "0x"). + ConsumeOneOrMore("\"0x\" must be followed by hex digits."); + + } else if (started_with_zero && LookingAt()) { + // An octal number (had a leading zero). + ConsumeZeroOrMore(); + if (LookingAt()) { + AddError("Numbers starting with leading zero must be in octal."); + ConsumeZeroOrMore(); + } + + } else { + // A decimal number. + if (started_with_dot) { + is_float = true; + ConsumeZeroOrMore(); + } else { + ConsumeZeroOrMore(); + + if (TryConsume('.')) { + is_float = true; + ConsumeZeroOrMore(); + } + } + + if (TryConsume('e') || TryConsume('E')) { + is_float = true; + TryConsume('-') || TryConsume('+'); + ConsumeOneOrMore("\"e\" must be followed by exponent."); + } + + if (allow_f_after_float_ && (TryConsume('f') || TryConsume('F'))) { + is_float = true; + } + } + + if (LookingAt()) { + AddError("Need space between number and identifier."); + } else if (current_char_ == '.') { + if (is_float) { + AddError( + "Already saw decimal point or exponent; can't have another one."); + } else { + AddError("Hex and octal numbers must be integers."); + } + } + + return is_float ? TYPE_FLOAT : TYPE_INTEGER; +} + +void Tokenizer::ConsumeLineComment() { + while (current_char_ != '\0' && current_char_ != '\n') { + NextChar(); + } + TryConsume('\n'); +} + +void Tokenizer::ConsumeBlockComment() { + int start_line = line_; + int start_column = column_ - 2; + + while (true) { + while (current_char_ != '\0' && + current_char_ != '*' && + current_char_ != '/') { + NextChar(); + } + + if (TryConsume('*') && TryConsume('/')) { + // End of comment. + break; + } else if (TryConsume('/') && current_char_ == '*') { + // Note: We didn't consume the '*' because if there is a '/' after it + // we want to interpret that as the end of the comment. + AddError( + "\"/*\" inside block comment. Block comments cannot be nested."); + } else if (current_char_ == '\0') { + AddError("End-of-file inside block comment."); + error_collector_->AddError( + start_line, start_column, " Comment started here."); + break; + } + } +} + +// ------------------------------------------------------------------- + +bool Tokenizer::Next() { + TokenType last_token_type = current_.type; + + // Did we skip any characters after the last token? + bool skipped_stuff = false; + + while (!read_error_) { + if (TryConsumeOne()) { + ConsumeZeroOrMore(); + + } else if (comment_style_ == CPP_COMMENT_STYLE && TryConsume('/')) { + // Starting a comment? + if (TryConsume('/')) { + ConsumeLineComment(); + } else if (TryConsume('*')) { + ConsumeBlockComment(); + } else { + // Oops, it was just a slash. Return it. + current_.type = TYPE_SYMBOL; + current_.text = "/"; + current_.line = line_; + current_.column = column_ - 1; + return true; + } + + } else if (comment_style_ == SH_COMMENT_STYLE && TryConsume('#')) { + ConsumeLineComment(); + + } else if (LookingAt() || current_char_ == '\0') { + AddError("Invalid control characters encountered in text."); + NextChar(); + // Skip more unprintable characters, too. But, remember that '\0' is + // also what current_char_ is set to after EOF / read error. We have + // to be careful not to go into an infinite loop of trying to consume + // it, so make sure to check read_error_ explicitly before consuming + // '\0'. + while (TryConsumeOne() || + (!read_error_ && TryConsume('\0'))) { + // Ignore. + } + + } else { + // Reading some sort of token. + StartToken(); + + if (TryConsumeOne()) { + ConsumeZeroOrMore(); + current_.type = TYPE_IDENTIFIER; + } else if (TryConsume('0')) { + current_.type = ConsumeNumber(true, false); + } else if (TryConsume('.')) { + // This could be the beginning of a floating-point number, or it could + // just be a '.' symbol. + + if (TryConsumeOne()) { + // It's a floating-point number. + if (last_token_type == TYPE_IDENTIFIER && !skipped_stuff) { + // We don't accept syntax like "blah.123". + error_collector_->AddError(line_, column_ - 2, + "Need space between identifier and decimal point."); + } + current_.type = ConsumeNumber(false, true); + } else { + current_.type = TYPE_SYMBOL; + } + } else if (TryConsumeOne()) { + current_.type = ConsumeNumber(false, false); + } else if (TryConsume('\"')) { + ConsumeString('\"'); + current_.type = TYPE_STRING; + } else if (TryConsume('\'')) { + ConsumeString('\''); + current_.type = TYPE_STRING; + } else { + NextChar(); + current_.type = TYPE_SYMBOL; + } + + EndToken(); + return true; + } + + skipped_stuff = true; + } + + // EOF + current_.type = TYPE_END; + current_.text.clear(); + current_.line = line_; + current_.column = column_; + return false; +} + +// ------------------------------------------------------------------- +// Token-parsing helpers. Remember that these don't need to report +// errors since any errors should already have been reported while +// tokenizing. Also, these can assume that whatever text they +// are given is text that the tokenizer actually parsed as a token +// of the given type. + +bool Tokenizer::ParseInteger(const string& text, uint64 max_value, + uint64* output) { + // Sadly, we can't just use strtoul() since it is only 32-bit and strtoull() + // is non-standard. I hate the C standard library. :( + +// return strtoull(text.c_str(), NULL, 0); + + const char* ptr = text.c_str(); + int base = 10; + if (ptr[0] == '0') { + if (ptr[1] == 'x') { + // This is hex. + base = 16; + ptr += 2; + } else { + // This is octal. + base = 8; + } + } + + uint64 result = 0; + for (; *ptr != '\0'; ptr++) { + int digit = DigitValue(*ptr); + GOOGLE_LOG_IF(DFATAL, digit < 0 || digit >= base) + << " Tokenizer::ParseInteger() passed text that could not have been" + " tokenized as an integer: " << CEscape(text); + if (digit > max_value || result > (max_value - digit) / base) { + // Overflow. + return false; + } + result = result * base + digit; + } + + *output = result; + return true; +} + +double Tokenizer::ParseFloat(const string& text) { + const char* start = text.c_str(); + char* end; + double result = NoLocaleStrtod(start, &end); + + // "1e" is not a valid float, but if the tokenizer reads it, it will + // report an error but still return it as a valid token. We need to + // accept anything the tokenizer could possibly return, error or not. + if (*end == 'e' || *end == 'E') { + ++end; + if (*end == '-' || *end == '+') ++end; + } + + // If the Tokenizer had allow_f_after_float_ enabled, the float may be + // suffixed with the letter 'f'. + if (*end == 'f' || *end == 'F') { + ++end; + } + + GOOGLE_LOG_IF(DFATAL, end - start != text.size() || *start == '-') + << " Tokenizer::ParseFloat() passed text that could not have been" + " tokenized as a float: " << CEscape(text); + return result; +} + +void Tokenizer::ParseString(const string& text, string* output) { + output->clear(); + + // Reminder: text[0] is always the quote character. (If text is + // empty, it's invalid, so we'll just return.) + if (text.empty()) { + GOOGLE_LOG(DFATAL) + << " ParseString::ParseString() passed text that could not have been" + " tokenized as a string: " << CEscape(text); + return; + } + + output->reserve(text.size()); + + // Loop through the string copying characters to "output" and + // interpreting escape sequences. Note that any invalid escape + // sequences or other errors were already reported while tokenizing. + // In this case we do not need to produce valid results. + for (const char* ptr = text.c_str() + 1; *ptr != '\0'; ptr++) { + if (*ptr == '\\' && ptr[1] != '\0') { + // An escape sequence. + ++ptr; + + if (OctalDigit::InClass(*ptr)) { + // An octal escape. May one, two, or three digits. + int code = DigitValue(*ptr); + if (OctalDigit::InClass(ptr[1])) { + ++ptr; + code = code * 8 + DigitValue(*ptr); + } + if (OctalDigit::InClass(ptr[1])) { + ++ptr; + code = code * 8 + DigitValue(*ptr); + } + output->push_back(static_cast(code)); + + } else if (*ptr == 'x') { + // A hex escape. May zero, one, or two digits. (The zero case + // will have been caught as an error earlier.) + int code = 0; + if (HexDigit::InClass(ptr[1])) { + ++ptr; + code = DigitValue(*ptr); + } + if (HexDigit::InClass(ptr[1])) { + ++ptr; + code = code * 16 + DigitValue(*ptr); + } + output->push_back(static_cast(code)); + + } else { + // Some other escape code. + output->push_back(TranslateEscape(*ptr)); + } + + } else if (*ptr == text[0]) { + // Ignore quote matching the starting quote. + } else { + output->push_back(*ptr); + } + } + + return; +} + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/tokenizer.h b/src/google/protobuf/io/tokenizer.h new file mode 100644 index 00000000..841564ce --- /dev/null +++ b/src/google/protobuf/io/tokenizer.h @@ -0,0 +1,276 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Class for parsing tokenized text from a ZeroCopyInputStream. + +#ifndef GOOGLE_PROTOBUF_IO_TOKENIZER_H__ +#define GOOGLE_PROTOBUF_IO_TOKENIZER_H__ + +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +class ZeroCopyInputStream; // zero_copy_stream.h + +// Defined in this file. +class ErrorCollector; +class Tokenizer; + +// Abstract interface for an object which collects the errors that occur +// during parsing. A typical implementation might simply print the errors +// to stdout. +class LIBPROTOBUF_EXPORT ErrorCollector { + public: + inline ErrorCollector() {} + virtual ~ErrorCollector(); + + // Indicates that there was an error in the input at the given line and + // column numbers. The numbers are zero-based, so you may want to add + // 1 to each before printing them. + virtual void AddError(int line, int column, const string& message) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ErrorCollector); +}; + +// This class converts a stream of raw text into a stream of tokens for +// the protocol definition parser to parse. The tokens recognized are +// similar to those that make up the C language; see the TokenType enum for +// precise descriptions. Whitespace and comments are skipped. By default, +// C- and C++-style comments are recognized, but other styles can be used by +// calling set_comment_style(). +class LIBPROTOBUF_EXPORT Tokenizer { + public: + // Construct a Tokenizer that reads and tokenizes text from the given + // input stream and writes errors to the given error_collector. + // The caller keeps ownership of input and error_collector. + Tokenizer(ZeroCopyInputStream* input, ErrorCollector* error_collector); + ~Tokenizer(); + + enum TokenType { + TYPE_START, // Next() has not yet been called. + TYPE_END, // End of input reached. "text" is empty. + + TYPE_IDENTIFIER, // A sequence of letters, digits, and underscores, not + // starting with a digit. It is an error for a number + // to be followed by an identifier with no space in + // between. + TYPE_INTEGER, // A sequence of digits representing an integer. Normally + // the digits are decimal, but a prefix of "0x" indicates + // a hex number and a leading zero indicates octal, just + // like with C numeric literals. A leading negative sign + // is NOT included in the token; it's up to the parser to + // interpret the unary minus operator on its own. + TYPE_FLOAT, // A floating point literal, with a fractional part and/or + // an exponent. Always in decimal. Again, never + // negative. + TYPE_STRING, // A quoted sequence of escaped characters. Either single + // or double quotes can be used, but they must match. + // A string literal cannot cross a line break. + TYPE_SYMBOL, // Any other printable character, like '!' or '+'. + // Symbols are always a single character, so "!+$%" is + // four tokens. + }; + + // Structure representing a token read from the token stream. + struct Token { + TokenType type; + string text; // The exact text of the token as it appeared in + // the input. e.g. tokens of TYPE_STRING will still + // be escaped and in quotes. + + // "line" and "column" specify the position of the first character of + // the token within the input stream. They are zero-based. + int line; + int column; + }; + + // Get the current token. This is updated when Next() is called. Before + // the first call to Next(), current() has type TYPE_START and no contents. + const Token& current(); + + // Advance to the next token. Returns false if the end of the input is + // reached. + bool Next(); + + // Parse helpers --------------------------------------------------- + + // Parses a TYPE_FLOAT token. This never fails, so long as the text actually + // comes from a TYPE_FLOAT token parsed by Tokenizer. If it doesn't, the + // result is undefined (possibly an assert failure). + static double ParseFloat(const string& text); + + // Parses a TYPE_STRING token. This never fails, so long as the text actually + // comes from a TYPE_STRING token parsed by Tokenizer. If it doesn't, the + // result is undefined (possibly an assert failure). + static void ParseString(const string& text, string* output); + + // Parses a TYPE_INTEGER token. Returns false if the result would be + // greater than max_value. Otherwise, returns true and sets *output to the + // result. If the text is not from a Token of type TYPE_INTEGER originally + // parsed by a Tokenizer, the result is undefined (possibly an assert + // failure). + static bool ParseInteger(const string& text, uint64 max_value, + uint64* output); + + // Options --------------------------------------------------------- + + // Set true to allow floats to be suffixed with the letter 'f'. Tokens + // which would otherwise be integers but which have the 'f' suffix will be + // forced to be interpreted as floats. For all other purposes, the 'f' is + // ignored. + void set_allow_f_after_float(bool value) { allow_f_after_float_ = value; } + + // Valid values for set_comment_style(). + enum CommentStyle { + // Line comments begin with "//", block comments are delimited by "/*" and + // "*/". + CPP_COMMENT_STYLE, + // Line comments begin with "#". No way to write block comments. + SH_COMMENT_STYLE + }; + + // Sets the comment style. + void set_comment_style(CommentStyle style) { comment_style_ = style; } + + // ----------------------------------------------------------------- + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Tokenizer); + + Token current_; // Returned by current(). + + ZeroCopyInputStream* input_; + ErrorCollector* error_collector_; + + char current_char_; // == buffer_[buffer_pos_], updated by NextChar(). + const char* buffer_; // Current buffer returned from input_. + int buffer_size_; // Size of buffer_. + int buffer_pos_; // Current position within the buffer. + bool read_error_; // Did we previously encounter a read error? + + // Line and column number of current_char_ within the whole input stream. + int line_; + int column_; + + // Position in buffer_ where StartToken() was called. If the token + // started in the previous buffer, this is zero, and current_.text already + // contains the part of the token from the previous buffer. If not + // currently parsing a token, this is -1. + int token_start_; + + // Options. + bool allow_f_after_float_; + CommentStyle comment_style_; + + // Since we count columns we need to interpret tabs somehow. We'll take + // the standard 8-character definition for lack of any way to do better. + static const int kTabWidth = 8; + + // ----------------------------------------------------------------- + // Helper methods. + + // Consume this character and advance to the next one. + void NextChar(); + + // Read a new buffer from the input. + void Refresh(); + + // Called when the current character is the first character of a new + // token (not including whitespace or comments). + inline void StartToken(); + // Called when the current character is the first character after the + // end of the last token. After this returns, current_.text will + // contain all text consumed since StartToken() was called. + inline void EndToken(); + + // Convenience method to add an error at the current line and column. + void AddError(const string& message) { + error_collector_->AddError(line_, column_, message); + } + + // ----------------------------------------------------------------- + // The following four methods are used to consume tokens of specific + // types. They are actually used to consume all characters *after* + // the first, since the calling function consumes the first character + // in order to decide what kind of token is being read. + + // Read and consume a string, ending when the given delimiter is + // consumed. + void ConsumeString(char delimiter); + + // Read and consume a number, returning TYPE_FLOAT or TYPE_INTEGER + // depending on what was read. This needs to know if the first + // character was a zero in order to correctly recognize hex and octal + // numbers. + // It also needs to know if the first characted was a . to parse floating + // point correctly. + TokenType ConsumeNumber(bool started_with_zero, bool started_with_dot); + + // Consume the rest of a line. + void ConsumeLineComment(); + // Consume until "*/". + void ConsumeBlockComment(); + + // ----------------------------------------------------------------- + // These helper methods make the parsing code more readable. The + // "character classes" refered to are defined at the top of the .cc file. + // Basically it is a C++ class with one method: + // static bool InClass(char c); + // The method returns true if c is a member of this "class", like "Letter" + // or "Digit". + + // Returns true if the current character is of the given character + // class, but does not consume anything. + template + inline bool LookingAt(); + + // If the current character is in the given class, consume it and return + // true. Otherwise return false. + // e.g. TryConsumeOne() + template + inline bool TryConsumeOne(); + + // Like above, but try to consume the specific character indicated. + inline bool TryConsume(char c); + + // Consume zero or more of the given character class. + template + inline void ConsumeZeroOrMore(); + + // Consume one or more of the given character class or log the given + // error message. + // e.g. ConsumeOneOrMore("Expected digits."); + template + inline void ConsumeOneOrMore(const char* error); +}; + +// inline methods ==================================================== +inline const Tokenizer::Token& Tokenizer::current() { + return current_; +} + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_TOKENIZER_H__ diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc new file mode 100644 index 00000000..2171fcc3 --- /dev/null +++ b/src/google/protobuf/io/tokenizer_unittest.cc @@ -0,0 +1,706 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +// =================================================================== +// Data-Driven Test Infrastructure + +// TODO(kenton): This is copied from coded_stream_unittest. This is +// temporary until these fetaures are integrated into gTest itself. + +// TEST_1D and TEST_2D are macros I'd eventually like to see added to +// gTest. These macros can be used to declare tests which should be +// run multiple times, once for each item in some input array. TEST_1D +// tests all cases in a single input array. TEST_2D tests all +// combinations of cases from two arrays. The arrays must be statically +// defined such that the GOOGLE_ARRAYSIZE() macro works on them. Example: +// +// int kCases[] = {1, 2, 3, 4} +// TEST_1D(MyFixture, MyTest, kCases) { +// EXPECT_GT(kCases_case, 0); +// } +// +// This test iterates through the numbers 1, 2, 3, and 4 and tests that +// they are all grater than zero. In case of failure, the exact case +// which failed will be printed. The case type must be printable using +// ostream::operator<<. + +#define TEST_1D(FIXTURE, NAME, CASES) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType& CASES##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES); i++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES " case #" << i << ": " << CASES[i]); \ + DoSingleCase(CASES[i]); \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType& CASES##_case) + +#define TEST_2D(FIXTURE, NAME, CASES1, CASES2) \ + class FIXTURE##_##NAME##_DD : public FIXTURE { \ + protected: \ + template \ + void DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case); \ + }; \ + \ + TEST_F(FIXTURE##_##NAME##_DD, NAME) { \ + for (int i = 0; i < GOOGLE_ARRAYSIZE(CASES1); i++) { \ + for (int j = 0; j < GOOGLE_ARRAYSIZE(CASES2); j++) { \ + SCOPED_TRACE(testing::Message() \ + << #CASES1 " case #" << i << ": " << CASES1[i] << ", " \ + << #CASES2 " case #" << j << ": " << CASES2[j]); \ + DoSingleCase(CASES1[i], CASES2[j]); \ + } \ + } \ + } \ + \ + template \ + void FIXTURE##_##NAME##_DD::DoSingleCase(const CaseType1& CASES1##_case, \ + const CaseType2& CASES2##_case) + +// ------------------------------------------------------------------- + +// An input stream that is basically like an ArrayInputStream but sometimes +// returns empty buffers, just to throw us off. +class TestInputStream : public ZeroCopyInputStream { + public: + TestInputStream(const void* data, int size, int block_size) + : array_stream_(data, size, block_size), counter_(0) {} + ~TestInputStream() {} + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size) { + // We'll return empty buffers starting with the first buffer, and every + // 3 and 5 buffers after that. + if (counter_ % 3 == 0 || counter_ % 5 == 0) { + *data = NULL; + *size = 0; + ++counter_; + return true; + } else { + ++counter_; + return array_stream_.Next(data, size); + } + } + + void BackUp(int count) { return array_stream_.BackUp(count); } + bool Skip(int count) { return array_stream_.Skip(count); } + int64 ByteCount() const { return array_stream_.ByteCount(); } + + private: + ArrayInputStream array_stream_; + int counter_; +}; + +// ------------------------------------------------------------------- + +// An error collector which simply concatenates all its errors into a big +// block of text which can be checked. +class TestErrorCollector : public ErrorCollector { + public: + TestErrorCollector() {} + ~TestErrorCollector() {} + + string text_; + + // implements ErrorCollector --------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line, column, message); + } +}; + +// ------------------------------------------------------------------- + +// We test each operation over a variety of block sizes to insure that +// we test cases where reads cross buffer boundaries as well as cases +// where they don't. This is sort of a brute-force approach to this, +// but it's easy to write and easy to understand. +const int kBlockSizes[] = {1, 2, 3, 5, 7, 13, 32, 1024}; + +class TokenizerTest : public testing::Test { + protected: + // For easy testing. + uint64 ParseInteger(const string& text) { + uint64 result; + EXPECT_TRUE(Tokenizer::ParseInteger(text, kuint64max, &result)); + return result; + } +}; + +// =================================================================== + +// These tests causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +// In each test case, the entire input text should parse as a single token +// of the given type. +struct SimpleTokenCase { + string input; + Tokenizer::TokenType type; +}; + +inline ostream& operator<<(ostream& out, + const SimpleTokenCase& test_case) { + return out << CEscape(test_case.input); +} + +SimpleTokenCase kSimpleTokenCases[] = { + // Test identifiers. + { "hello", Tokenizer::TYPE_IDENTIFIER }, + + // Test integers. + { "123", Tokenizer::TYPE_INTEGER }, + { "0xab6", Tokenizer::TYPE_INTEGER }, + { "0XAB6", Tokenizer::TYPE_INTEGER }, + { "0X1234567", Tokenizer::TYPE_INTEGER }, + { "0x89abcdef", Tokenizer::TYPE_INTEGER }, + { "0x89ABCDEF", Tokenizer::TYPE_INTEGER }, + { "01234567", Tokenizer::TYPE_INTEGER }, + + // Test floats. + { "123.45", Tokenizer::TYPE_FLOAT }, + { "1.", Tokenizer::TYPE_FLOAT }, + { "1e3", Tokenizer::TYPE_FLOAT }, + { "1E3", Tokenizer::TYPE_FLOAT }, + { "1e-3", Tokenizer::TYPE_FLOAT }, + { "1e+3", Tokenizer::TYPE_FLOAT }, + { "1.e3", Tokenizer::TYPE_FLOAT }, + { "1.2e3", Tokenizer::TYPE_FLOAT }, + { ".1", Tokenizer::TYPE_FLOAT }, + { ".1e3", Tokenizer::TYPE_FLOAT }, + { ".1e-3", Tokenizer::TYPE_FLOAT }, + { ".1e+3", Tokenizer::TYPE_FLOAT }, + + // Test strings. + { "'hello'", Tokenizer::TYPE_STRING }, + { "\"foo\"", Tokenizer::TYPE_STRING }, + { "'a\"b'", Tokenizer::TYPE_STRING }, + { "\"a'b\"", Tokenizer::TYPE_STRING }, + { "'a\\'b'", Tokenizer::TYPE_STRING }, + { "\"a\\\"b\"", Tokenizer::TYPE_STRING }, + { "'\\xf'", Tokenizer::TYPE_STRING }, + { "'\\0'", Tokenizer::TYPE_STRING }, + + // Test symbols. + { "+", Tokenizer::TYPE_SYMBOL }, + { ".", Tokenizer::TYPE_SYMBOL }, +}; + +TEST_2D(TokenizerTest, SimpleTokens, kSimpleTokenCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kSimpleTokenCases_case.input.data(), + kSimpleTokenCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Before Next() is called, the initial token should always be TYPE_START. + EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // Parse the token. + ASSERT_TRUE(tokenizer.Next()); + + // Check that it has the right type. + EXPECT_EQ(kSimpleTokenCases_case.type, tokenizer.current().type); + // Check that it contains the complete input text. + EXPECT_EQ(kSimpleTokenCases_case.input, tokenizer.current().text); + // Check that it is located at the beginning of the input + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + + // After Next() returns false, the token should have type TYPE_END. + EXPECT_EQ(Tokenizer::TYPE_END, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(kSimpleTokenCases_case.input.size(), tokenizer.current().column); + + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +TEST_1D(TokenizerTest, FloatSuffix, kBlockSizes) { + // Test the "allow_f_after_float" option. + + // Set up the tokenizer. + const char* text = "1f 2.5f 6e3f 7F"; + TestInputStream input(text, strlen(text), kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + tokenizer.set_allow_f_after_float(true); + + // Advance through tokens and check that they are parsed as expected. + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "1f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "2.5f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "6e3f"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + ASSERT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, "7F"); + EXPECT_EQ(tokenizer.current().type, Tokenizer::TYPE_FLOAT); + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +#endif + +// ------------------------------------------------------------------- + +// In each case, the input is parsed to produce a list of tokens. The +// last token in "output" must have type TYPE_END. +struct MultiTokenCase { + string input; + Tokenizer::Token output[10]; // The compiler wants a constant array + // size for initialization to work. There + // is no reason this can't be increased if + // needed. +}; + +inline ostream& operator<<(ostream& out, + const MultiTokenCase& test_case) { + return out << CEscape(test_case.input); +} + +MultiTokenCase kMultiTokenCases[] = { + // Test empty input. + { "", { + { Tokenizer::TYPE_END , "" , 0, 0 }, + }}, + + // Test all token types at the same time. + { "foo 1 1.2 + 'bar'", { + { Tokenizer::TYPE_IDENTIFIER, "foo" , 0, 0 }, + { Tokenizer::TYPE_INTEGER , "1" , 0, 4 }, + { Tokenizer::TYPE_FLOAT , "1.2" , 0, 6 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 10 }, + { Tokenizer::TYPE_STRING , "'bar'", 0, 12 }, + { Tokenizer::TYPE_END , "" , 0, 17 }, + }}, + + // Test that consecutive symbols are parsed as separate tokens. + { "!@+%", { + { Tokenizer::TYPE_SYMBOL , "!" , 0, 0 }, + { Tokenizer::TYPE_SYMBOL , "@" , 0, 1 }, + { Tokenizer::TYPE_SYMBOL , "+" , 0, 2 }, + { Tokenizer::TYPE_SYMBOL , "%" , 0, 3 }, + { Tokenizer::TYPE_END , "" , 0, 4 }, + }}, + + // Test that newlines affect line numbers correctly. + { "foo bar\nrab oof", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 4 }, + { Tokenizer::TYPE_IDENTIFIER, "rab", 1, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "oof", 1, 4 }, + { Tokenizer::TYPE_END , "" , 1, 7 }, + }}, + + // Test that tabs affect column numbers correctly. + { "foo\tbar \tbaz", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 8 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 0, 16 }, + { Tokenizer::TYPE_END , "" , 0, 19 }, + }}, + + // Test that line comments are ignored. + { "foo // This is a comment\n" + "bar // This is another comment", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 1, 0 }, + { Tokenizer::TYPE_END , "" , 1, 30 }, + }}, + + // Test that block comments are ignored. + { "foo /* This is a block comment */ bar", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 34 }, + { Tokenizer::TYPE_END , "" , 0, 37 }, + }}, + + // Test that sh-style comments are not ignored by default. + { "foo # bar\n" + "baz", { + { Tokenizer::TYPE_IDENTIFIER, "foo", 0, 0 }, + { Tokenizer::TYPE_SYMBOL , "#" , 0, 4 }, + { Tokenizer::TYPE_IDENTIFIER, "bar", 0, 6 }, + { Tokenizer::TYPE_IDENTIFIER, "baz", 1, 0 }, + { Tokenizer::TYPE_END , "" , 1, 3 }, + }}, +}; + +TEST_2D(TokenizerTest, MultipleTokens, kMultiTokenCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kMultiTokenCases_case.input.data(), + kMultiTokenCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Before Next() is called, the initial token should always be TYPE_START. + EXPECT_EQ(Tokenizer::TYPE_START, tokenizer.current().type); + EXPECT_EQ("", tokenizer.current().text); + EXPECT_EQ(0, tokenizer.current().line); + EXPECT_EQ(0, tokenizer.current().column); + + // Loop through all expected tokens. + int i = 0; + Tokenizer::Token token; + do { + token = kMultiTokenCases_case.output[i++]; + + SCOPED_TRACE(testing::Message() << "Token #" << i << ": " << token.text); + + // Next() should only return false when it hits the end token. + if (token.type != Tokenizer::TYPE_END) { + ASSERT_TRUE(tokenizer.Next()); + } else { + ASSERT_FALSE(tokenizer.Next()); + } + + // Check that the token matches the expected one. + EXPECT_EQ(token.type, tokenizer.current().type); + EXPECT_EQ(token.text, tokenizer.current().text); + EXPECT_EQ(token.line, tokenizer.current().line); + EXPECT_EQ(token.column, tokenizer.current().column); + + } while (token.type != Tokenizer::TYPE_END); + + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +// This test causes gcc 3.3.5 (and earlier?) to give the cryptic error: +// "sorry, unimplemented: `method_call_expr' not supported by dump_expr" +#if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3) + +TEST_1D(TokenizerTest, ShCommentStyle, kBlockSizes) { + // Test the "comment_style" option. + + const char* text = "foo # bar\n" + "baz // qux\n" + "corge /* grault */\n" + "garply"; + const char* const kTokens[] = {"foo", // "# bar" is ignored + "baz", "/", "/", "qux", + "corge", "/", "*", "grault", "*", "/", + "garply"}; + + // Set up the tokenizer. + TestInputStream input(text, strlen(text), kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + tokenizer.set_comment_style(Tokenizer::SH_COMMENT_STYLE); + + // Advance through tokens and check that they are parsed as expected. + for (int i = 0; i < GOOGLE_ARRAYSIZE(kTokens); i++) { + EXPECT_TRUE(tokenizer.Next()); + EXPECT_EQ(tokenizer.current().text, kTokens[i]); + } + + // There should be no more input. + EXPECT_FALSE(tokenizer.Next()); + // There should be no errors. + EXPECT_TRUE(error_collector.text_.empty()); +} + +#endif + +// ------------------------------------------------------------------- + +// Test parse helpers. It's not really worth setting up a full data-driven +// test here. +TEST_F(TokenizerTest, ParseInteger) { + EXPECT_EQ(0, ParseInteger("0")); + EXPECT_EQ(123, ParseInteger("123")); + EXPECT_EQ(0xabcdef12u, ParseInteger("0xabcdef12")); + EXPECT_EQ(0xabcdef12u, ParseInteger("0xABCDEF12")); + EXPECT_EQ(kuint64max, ParseInteger("0xFFFFFFFFFFFFFFFF")); + EXPECT_EQ(01234567, ParseInteger("01234567")); + + // Test invalid integers that may still be tokenized as integers. + EXPECT_EQ(0, ParseInteger("0x")); + + uint64 i; +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + // Test invalid integers that will never be tokenized as integers. + EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("zxy", kuint64max, &i), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("1.2", kuint64max, &i), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("08", kuint64max, &i), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("0xg", kuint64max, &i), + "passed text that could not have been tokenized as an integer"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseInteger("-1", kuint64max, &i), + "passed text that could not have been tokenized as an integer"); +#endif // GTEST_HAS_DEATH_TEST + + // Test overflows. + EXPECT_TRUE (Tokenizer::ParseInteger("0", 0, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("1", 0, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("1", 1, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("12345", 12345, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("12346", 12345, &i)); + EXPECT_TRUE (Tokenizer::ParseInteger("0xFFFFFFFFFFFFFFFF" , kuint64max, &i)); + EXPECT_FALSE(Tokenizer::ParseInteger("0x10000000000000000", kuint64max, &i)); +} + +TEST_F(TokenizerTest, ParseFloat) { + EXPECT_DOUBLE_EQ(1 , Tokenizer::ParseFloat("1.")); + EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1e3")); + EXPECT_DOUBLE_EQ(1e3 , Tokenizer::ParseFloat("1E3")); + EXPECT_DOUBLE_EQ(1.5e3, Tokenizer::ParseFloat("1.5e3")); + EXPECT_DOUBLE_EQ(.1 , Tokenizer::ParseFloat(".1")); + EXPECT_DOUBLE_EQ(.25 , Tokenizer::ParseFloat(".25")); + EXPECT_DOUBLE_EQ(.1e3 , Tokenizer::ParseFloat(".1e3")); + EXPECT_DOUBLE_EQ(.25e3, Tokenizer::ParseFloat(".25e3")); + EXPECT_DOUBLE_EQ(.1e+3, Tokenizer::ParseFloat(".1e+3")); + EXPECT_DOUBLE_EQ(.1e-3, Tokenizer::ParseFloat(".1e-3")); + EXPECT_DOUBLE_EQ(5 , Tokenizer::ParseFloat("5")); + EXPECT_DOUBLE_EQ(6e-12, Tokenizer::ParseFloat("6e-12")); + EXPECT_DOUBLE_EQ(1.2 , Tokenizer::ParseFloat("1.2")); + EXPECT_DOUBLE_EQ(1.e2 , Tokenizer::ParseFloat("1.e2")); + + // Test invalid integers that may still be tokenized as integers. + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1e-")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.e")); + + // Test 'f' suffix. + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1f")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1.0f")); + EXPECT_DOUBLE_EQ(1, Tokenizer::ParseFloat("1F")); + + // These should parse successfully even though they are out of range. + // Overflows become infinity and underflows become zero. + EXPECT_EQ( 0.0, Tokenizer::ParseFloat("1e-9999999999999999999999999999")); + EXPECT_EQ(HUGE_VAL, Tokenizer::ParseFloat("1e+9999999999999999999999999999")); + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + // Test invalid integers that will never be tokenized as integers. + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("zxy"), + "passed text that could not have been tokenized as a float"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("1-e0"), + "passed text that could not have been tokenized as a float"); + EXPECT_DEBUG_DEATH(Tokenizer::ParseFloat("-1.0"), + "passed text that could not have been tokenized as a float"); +#endif // GTEST_HAS_DEATH_TEST +} + +TEST_F(TokenizerTest, ParseString) { + string output; + Tokenizer::ParseString("'hello'", &output); + EXPECT_EQ("hello", output); + Tokenizer::ParseString("\"blah\\nblah2\"", &output); + EXPECT_EQ("blah\nblah2", output); + Tokenizer::ParseString("'\\1x\\1\\123\\739\\52\\334n\\3'", &output); + EXPECT_EQ("\1x\1\123\739\52\334n\3", output); + Tokenizer::ParseString("'\\x20\\x4'", &output); + EXPECT_EQ("\x20\x4", output); + + // Test invalid strings that may still be tokenized as strings. + Tokenizer::ParseString("\"\\a\\l\\v\\t", &output); // \l is invalid + EXPECT_EQ("\a?\v\t", output); + Tokenizer::ParseString("'", &output); + EXPECT_EQ("", output); + Tokenizer::ParseString("'\\", &output); + EXPECT_EQ("\\", output); + + // Test invalid strings that will never be tokenized as strings. +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet + EXPECT_DEBUG_DEATH(Tokenizer::ParseString("", &output), + "passed text that could not have been tokenized as a string"); +#endif // GTEST_HAS_DEATH_TEST +} + +// ------------------------------------------------------------------- + +// Each case parses some input text, ignoring the tokens produced, and +// checks that the error output matches what is expected. +struct ErrorCase { + string input; + bool recoverable; // True if the tokenizer should be able to recover and + // parse more tokens after seeing this error. Cases + // for which this is true must end with "foo" as + // the last token, which the test will check for. + const char* errors; +}; + +inline ostream& operator<<(ostream& out, + const ErrorCase& test_case) { + return out << CEscape(test_case.input); +} + +ErrorCase kErrorCases[] = { + // String errors. + { "'\\l' foo", true, + "0:2: Invalid escape sequence in string literal.\n" }, + { "'\\x' foo", true, + "0:3: Expected hex digits for escape sequence.\n" }, + { "'foo", false, + "0:4: String literals cannot cross line boundaries.\n" }, + { "'bar\nfoo", true, + "0:4: String literals cannot cross line boundaries.\n" }, + + // Integer errors. + { "123foo", true, + "0:3: Need space between number and identifier.\n" }, + + // Hex/octal errors. + { "0x foo", true, + "0:2: \"0x\" must be followed by hex digits.\n" }, + { "0541823 foo", true, + "0:4: Numbers starting with leading zero must be in octal.\n" }, + { "0x123z foo", true, + "0:5: Need space between number and identifier.\n" }, + { "0x123.4 foo", true, + "0:5: Hex and octal numbers must be integers.\n" }, + { "0123.4 foo", true, + "0:4: Hex and octal numbers must be integers.\n" }, + + // Float errors. + { "1e foo", true, + "0:2: \"e\" must be followed by exponent.\n" }, + { "1e- foo", true, + "0:3: \"e\" must be followed by exponent.\n" }, + { "1.2.3 foo", true, + "0:3: Already saw decimal point or exponent; can't have another one.\n" }, + { "1e2.3 foo", true, + "0:3: Already saw decimal point or exponent; can't have another one.\n" }, + { "a.1 foo", true, + "0:1: Need space between identifier and decimal point.\n" }, + // allow_f_after_float not enabled, so this should be an error. + { "1.0f foo", true, + "0:3: Need space between number and identifier.\n" }, + + // Block comment errors. + { "/*", false, + "0:2: End-of-file inside block comment.\n" + "0:0: Comment started here.\n"}, + { "/*/*/ foo", true, + "0:3: \"/*\" inside block comment. Block comments cannot be nested.\n"}, + + // Control characters. Multiple consecutive control characters should only + // produce one error. + { "\b foo", true, + "0:0: Invalid control characters encountered in text.\n" }, + { "\b\b foo", true, + "0:0: Invalid control characters encountered in text.\n" }, + + // Check that control characters at end of input don't result in an + // infinite loop. + { "\b", false, + "0:0: Invalid control characters encountered in text.\n" }, + + // Check recovery from '\0'. We have to explicitly specify the length of + // these strings because otherwise the string constructor will just call + // strlen() which will see the first '\0' and think that is the end of the + // string. + { string("\0foo", 4), true, + "0:0: Invalid control characters encountered in text.\n" }, + { string("\0\0foo", 5), true, + "0:0: Invalid control characters encountered in text.\n" }, +}; + +TEST_2D(TokenizerTest, Errors, kErrorCases, kBlockSizes) { + // Set up the tokenizer. + TestInputStream input(kErrorCases_case.input.data(), + kErrorCases_case.input.size(), + kBlockSizes_case); + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + // Ignore all input, except remember if the last token was "foo". + bool last_was_foo = false; + while (tokenizer.Next()) { + last_was_foo = tokenizer.current().text == "foo"; + } + + // Check that the errors match what was expected. + EXPECT_EQ(error_collector.text_, kErrorCases_case.errors); + + // If the error was recoverable, make sure we saw "foo" after it. + if (kErrorCases_case.recoverable) { + EXPECT_TRUE(last_was_foo); + } +} + +// ------------------------------------------------------------------- + +TEST_1D(TokenizerTest, BackUpOnDestruction, kBlockSizes) { + string text = "foo bar"; + TestInputStream input(text.data(), text.size(), kBlockSizes_case); + + // Create a tokenizer, read one token, then destroy it. + { + TestErrorCollector error_collector; + Tokenizer tokenizer(&input, &error_collector); + + tokenizer.Next(); + } + + // Only "foo" should have been read. + EXPECT_EQ(strlen("foo"), input.ByteCount()); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream.cc b/src/google/protobuf/io/zero_copy_stream.cc new file mode 100644 index 00000000..80559c4a --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream.cc @@ -0,0 +1,34 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + + +namespace google { +namespace protobuf { +namespace io { + +ZeroCopyInputStream::~ZeroCopyInputStream() {} +ZeroCopyOutputStream::~ZeroCopyOutputStream() {} + + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream.h b/src/google/protobuf/io/zero_copy_stream.h new file mode 100644 index 00000000..bce5f2d3 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream.h @@ -0,0 +1,224 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the ZeroCopyInputStream and ZeroCopyOutputStream +// interfaces, which represent abstract I/O streams to and from which +// protocol buffers can be read and written. For a few simple +// implementations of these interfaces, see zero_copy_stream_impl.h. +// +// These interfaces are different from classic I/O streams in that they +// try to minimize the amount of data copying that needs to be done. +// To accomplish this, responsibility for allocating buffers is moved to +// the stream object, rather than being the responsibility of the caller. +// So, the stream can return a buffer which actually points directly into +// the final data structure where the bytes are to be stored, and the caller +// can interact directly with that buffer, eliminating an intermediate copy +// operation. +// +// As an example, consider the common case in which you are reading bytes +// from an array that is already in memory (or perhaps an mmap()ed file). +// With classic I/O streams, you would do something like: +// char buffer[BUFFER_SIZE]; +// input->Read(buffer, BUFFER_SIZE); +// DoSomething(buffer, BUFFER_SIZE); +// Then, the stream basically just calls memcpy() to copy the data from +// the array into your buffer. With a ZeroCopyInputStream, you would do +// this instead: +// const void* buffer; +// int size; +// input->Next(&buffer, &size); +// DoSomething(buffer, size); +// Here, no copy is performed. The input stream returns a pointer directly +// into the backing array, and the caller ends up reading directly from it. +// +// If you want to be able to read the old-fashion way, you can create +// a CodedInputStream or CodedOutputStream wrapping these objects and use +// their ReadRaw()/WriteRaw() methods. These will, of course, add a copy +// step, but Coded*Stream will handle buffering so at least it will be +// reasonably efficient. +// +// ZeroCopyInputStream example: +// // Read in a file and print its contents to stdout. +// int fd = open("myfile", O_RDONLY); +// ZeroCopyInputStream* input = new FileInputStream(fd); +// +// const void* buffer; +// int size; +// while (input->Next(&buffer, &size)) { +// cout.write(buffer, size); +// } +// +// delete input; +// close(fd); +// +// ZeroCopyOutputStream example: +// // Copy the contents of "infile" to "outfile", using plain read() for +// // "infile" but a ZeroCopyOutputStream for "outfile". +// int infd = open("infile", O_RDONLY); +// int outfd = open("outfile", O_WRONLY); +// ZeroCopyInputStream* output = new FileOutputStream(outfd); +// +// void* buffer; +// int size; +// while (output->Next(&buffer, &size)) { +// int bytes = read(infd, buffer, size); +// if (bytes < size) { +// // Reached EOF. +// output->BackUp(size - bytes); +// break; +// } +// } +// +// delete output; +// close(infd); +// close(outfd); + +#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ +#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ + +#include +#include + +namespace google { + +namespace protobuf { +namespace io { + +// Defined in this file. +class ZeroCopyInputStream; +class ZeroCopyOutputStream; + +// Abstract interface similar to an input stream but designed to minimize +// copying. +class LIBPROTOBUF_EXPORT ZeroCopyInputStream { + public: + inline ZeroCopyInputStream() {} + virtual ~ZeroCopyInputStream(); + + // Obtains a chunk of data from the stream. + // + // Preconditions: + // * "size" and "data" are not NULL. + // + // Postconditions: + // * If the returned value is false, there is no more data to return or + // an error occurred. All errors are permanent. + // * Otherwise, "size" points to the actual number of bytes read and "data" + // points to a pointer to a buffer containing these bytes. + // * Ownership of this buffer remains with the stream, and the buffer + // remains valid only until some other method of the stream is called + // or the stream is destroyed. + // * It is legal for the returned buffer to have zero size, as long + // as repeatedly calling Next() eventually yields a buffer with non-zero + // size. + virtual bool Next(const void** data, int* size) = 0; + + // Backs up a number of bytes, so that the next call to Next() returns + // data again that was already returned by the last call to Next(). This + // is useful when writing procedures that are only supposed to read up + // to a certain point in the input, then return. If Next() returns a + // buffer that goes beyond what you wanted to read, you can use BackUp() + // to return to the point where you intended to finish. + // + // Preconditions: + // * The last method called must have been Next(). + // * count must be less than or equal to the size of the last buffer + // returned by Next(). + // + // Postconditions: + // * The last "count" bytes of the last buffer returned by Next() will be + // pushed back into the stream. Subsequent calls to Next() will return + // the same data again before producing new data. + virtual void BackUp(int count) = 0; + + // Skips a number of bytes. Returns false if the end of the stream is + // reached or some input error occurred. In the end-of-stream case, the + // stream is advanced to the end of the stream (so ByteCount() will return + // the total size of the stream). + virtual bool Skip(int count) = 0; + + // Returns the total number of bytes read since this object was created. + virtual int64 ByteCount() const = 0; + + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyInputStream); +}; + +// Abstract interface similar to an output stream but designed to minimize +// copying. +class LIBPROTOBUF_EXPORT ZeroCopyOutputStream { + public: + inline ZeroCopyOutputStream() {} + virtual ~ZeroCopyOutputStream(); + + // Obtains a buffer into which data can be written. Any data written + // into this buffer will eventually (maybe instantly, maybe later on) + // be written to the output. + // + // Preconditions: + // * "size" and "data" are not NULL. + // + // Postconditions: + // * If the returned value is false, an error occurred. All errors are + // permanent. + // * Otherwise, "size" points to the actual number of bytes in the buffer + // and "data" points to the buffer. + // * Ownership of this buffer remains with the stream, and the buffer + // remains valid only until some other method of the stream is called + // or the stream is destroyed. + // * Any data which the caller stores in this buffer will eventually be + // written to the output (unless BackUp() is called). + // * It is legal for the returned buffer to have zero size, as long + // as repeatedly calling Next() eventually yields a buffer with non-zero + // size. + virtual bool Next(void** data, int* size) = 0; + + // Backs up a number of bytes, so that the end of the last buffer returned + // by Next() is not actually written. This is needed when you finish + // writing all the data you want to write, but the last buffer was bigger + // than you needed. You don't want to write a bunch of garbage after the + // end of your data, so you use BackUp() to back up. + // + // Preconditions: + // * The last method called must have been Next(). + // * count must be less than or equal to the size of the last buffer + // returned by Next(). + // * The caller must not have written anything to the last "count" bytes + // of that buffer. + // + // Postconditions: + // * The last "count" bytes of the last buffer returned by Next() will be + // ignored. + virtual void BackUp(int count) = 0; + + // Returns the total number of bytes written since this object was created. + virtual int64 ByteCount() const = 0; + + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ZeroCopyOutputStream); +}; + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_H__ diff --git a/src/google/protobuf/io/zero_copy_stream_impl.cc b/src/google/protobuf/io/zero_copy_stream_impl.cc new file mode 100644 index 00000000..7ff84460 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_impl.cc @@ -0,0 +1,793 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifdef _MSC_VER +#include +#else +#include +#include +#include +#include +#endif +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { + +#ifdef _WIN32 +// Win32 lseek is broken: If invoked on a non-seekable file descriptor, its +// return value is undefined. We re-define it to always produce an error. +#define lseek(fd, offset, origin) ((off_t)-1) +#endif + +namespace { + + +// EINTR sucks. +int close_no_eintr(int fd) { + int result; + do { + result = close(fd); + } while (result < 0 && errno == EINTR); + return result; +} + +// Default block size for Copying{In,Out}putStreamAdaptor. +static const int kDefaultBlockSize = 8192; + +} // namespace + +// =================================================================== + +ArrayInputStream::ArrayInputStream(const void* data, int size, + int block_size) + : data_(reinterpret_cast(data)), + size_(size), + block_size_(block_size > 0 ? block_size : size), + position_(0), + last_returned_size_(0) { +} + +ArrayInputStream::~ArrayInputStream() { +} + +bool ArrayInputStream::Next(const void** data, int* size) { + if (position_ < size_) { + last_returned_size_ = min(block_size_, size_ - position_); + *data = data_ + position_; + *size = last_returned_size_; + position_ += last_returned_size_; + return true; + } else { + // We're at the end of the array. + last_returned_size_ = 0; // Don't let caller back up. + return false; + } +} + +void ArrayInputStream::BackUp(int count) { + GOOGLE_CHECK_GT(last_returned_size_, 0) + << "BackUp() can only be called after a successful Next()."; + GOOGLE_CHECK_LE(count, last_returned_size_); + GOOGLE_CHECK_GE(count, 0); + position_ -= count; + last_returned_size_ = 0; // Don't let caller back up further. +} + +bool ArrayInputStream::Skip(int count) { + GOOGLE_CHECK_GE(count, 0); + last_returned_size_ = 0; // Don't let caller back up. + if (count > size_ - position_) { + position_ = size_; + return false; + } else { + position_ += count; + return true; + } +} + +int64 ArrayInputStream::ByteCount() const { + return position_; +} + + +// =================================================================== + +ArrayOutputStream::ArrayOutputStream(void* data, int size, int block_size) + : data_(reinterpret_cast(data)), + size_(size), + block_size_(block_size > 0 ? block_size : size), + position_(0), + last_returned_size_(0) { +} + +ArrayOutputStream::~ArrayOutputStream() { +} + +bool ArrayOutputStream::Next(void** data, int* size) { + if (position_ < size_) { + last_returned_size_ = min(block_size_, size_ - position_); + *data = data_ + position_; + *size = last_returned_size_; + position_ += last_returned_size_; + return true; + } else { + // We're at the end of the array. + last_returned_size_ = 0; // Don't let caller back up. + return false; + } +} + +void ArrayOutputStream::BackUp(int count) { + GOOGLE_CHECK_GT(last_returned_size_, 0) + << "BackUp() can only be called after a successful Next()."; + GOOGLE_CHECK_LE(count, last_returned_size_); + GOOGLE_CHECK_GE(count, 0); + position_ -= count; + last_returned_size_ = 0; // Don't let caller back up further. +} + +int64 ArrayOutputStream::ByteCount() const { + return position_; +} + +// =================================================================== + +StringOutputStream::StringOutputStream(string* target) + : target_(target) { +} + +StringOutputStream::~StringOutputStream() { +} + +bool StringOutputStream::Next(void** data, int* size) { + int old_size = target_->size(); + + // Grow the string. + if (old_size < target_->capacity()) { + // Resize the string to match its capacity, since we can get away + // without a memory allocation this way. + target_->resize(target_->capacity()); + } else { + // Size has reached capacity, so double the size. Also make sure + // that the new size is at least kMinimumSize. + target_->resize( + max(old_size * 2, + kMinimumSize + 0)); // "+ 0" works around GCC4 weirdness. + } + + *data = string_as_array(target_) + old_size; + *size = target_->size() - old_size; + return true; +} + +void StringOutputStream::BackUp(int count) { + GOOGLE_CHECK_GE(count, 0); + GOOGLE_CHECK_LE(count, target_->size()); + target_->resize(target_->size() - count); +} + +int64 StringOutputStream::ByteCount() const { + return target_->size(); +} + +// =================================================================== + + +// =================================================================== + +CopyingInputStream::~CopyingInputStream() {} + +int CopyingInputStream::Skip(int count) { + char junk[4096]; + int skipped = 0; + while (skipped < count) { + int bytes = Read(junk, min(count, implicit_cast(sizeof(junk)))); + if (bytes <= 0) { + // EOF or read error. + return skipped; + } + skipped += bytes; + } + return skipped; +} + +CopyingInputStreamAdaptor::CopyingInputStreamAdaptor( + CopyingInputStream* copying_stream, int block_size) + : copying_stream_(copying_stream), + owns_copying_stream_(false), + failed_(false), + position_(0), + buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize), + buffer_used_(0), + backup_bytes_(0) { +} + +CopyingInputStreamAdaptor::~CopyingInputStreamAdaptor() { + if (owns_copying_stream_) { + delete copying_stream_; + } +} + +bool CopyingInputStreamAdaptor::Next(const void** data, int* size) { + if (failed_) { + // Already failed on a previous read. + return false; + } + + AllocateBufferIfNeeded(); + + if (backup_bytes_ > 0) { + // We have data left over from a previous BackUp(), so just return that. + *data = buffer_.get() + buffer_used_ - backup_bytes_; + *size = backup_bytes_; + backup_bytes_ = 0; + return true; + } + + // Read new data into the buffer. + buffer_used_ = copying_stream_->Read(buffer_.get(), buffer_size_); + if (buffer_used_ <= 0) { + // EOF or read error. We don't need the buffer anymore. + if (buffer_used_ < 0) { + // Read error (not EOF). + failed_ = true; + } + FreeBuffer(); + return false; + } + position_ += buffer_used_; + + *size = buffer_used_; + *data = buffer_.get(); + return true; +} + +void CopyingInputStreamAdaptor::BackUp(int count) { + GOOGLE_CHECK(backup_bytes_ == 0 && buffer_.get() != NULL) + << " BackUp() can only be called after Next()."; + GOOGLE_CHECK_LE(count, buffer_used_) + << " Can't back up over more bytes than were returned by the last call" + " to Next()."; + GOOGLE_CHECK_GE(count, 0) + << " Parameter to BackUp() can't be negative."; + + backup_bytes_ = count; +} + +bool CopyingInputStreamAdaptor::Skip(int count) { + GOOGLE_CHECK_GE(count, 0); + + if (failed_) { + // Already failed on a previous read. + return false; + } + + // First skip any bytes left over from a previous BackUp(). + if (backup_bytes_ >= count) { + // We have more data left over than we're trying to skip. Just chop it. + backup_bytes_ -= count; + return true; + } + + count -= backup_bytes_; + backup_bytes_ = 0; + + int skipped = copying_stream_->Skip(count); + position_ += skipped; + return skipped == count; +} + +int64 CopyingInputStreamAdaptor::ByteCount() const { + return position_ - backup_bytes_; +} + +void CopyingInputStreamAdaptor::AllocateBufferIfNeeded() { + if (buffer_.get() == NULL) { + buffer_.reset(new uint8[buffer_size_]); + } +} + +void CopyingInputStreamAdaptor::FreeBuffer() { + GOOGLE_CHECK_EQ(backup_bytes_, 0); + buffer_used_ = 0; + buffer_.reset(); +} + +// =================================================================== + +CopyingOutputStream::~CopyingOutputStream() {} + +CopyingOutputStreamAdaptor::CopyingOutputStreamAdaptor( + CopyingOutputStream* copying_stream, int block_size) + : copying_stream_(copying_stream), + owns_copying_stream_(false), + failed_(false), + position_(0), + buffer_size_(block_size > 0 ? block_size : kDefaultBlockSize), + buffer_used_(0) { +} + +CopyingOutputStreamAdaptor::~CopyingOutputStreamAdaptor() { + WriteBuffer(); + if (owns_copying_stream_) { + delete copying_stream_; + } +} + +bool CopyingOutputStreamAdaptor::Flush() { + return WriteBuffer(); +} + +bool CopyingOutputStreamAdaptor::Next(void** data, int* size) { + if (buffer_used_ == buffer_size_) { + if (!WriteBuffer()) return false; + } + + AllocateBufferIfNeeded(); + + *data = buffer_.get() + buffer_used_; + *size = buffer_size_ - buffer_used_; + buffer_used_ = buffer_size_; + return true; +} + +void CopyingOutputStreamAdaptor::BackUp(int count) { + GOOGLE_CHECK_GE(count, 0); + GOOGLE_CHECK_EQ(buffer_used_, buffer_size_) + << " BackUp() can only be called after Next()."; + GOOGLE_CHECK_LE(count, buffer_used_) + << " Can't back up over more bytes than were returned by the last call" + " to Next()."; + + buffer_used_ -= count; +} + +int64 CopyingOutputStreamAdaptor::ByteCount() const { + return position_ + buffer_used_; +} + +bool CopyingOutputStreamAdaptor::WriteBuffer() { + if (failed_) { + // Already failed on a previous write. + return false; + } + + if (buffer_used_ == 0) return true; + + if (copying_stream_->Write(buffer_.get(), buffer_used_)) { + position_ += buffer_used_; + buffer_used_ = 0; + return true; + } else { + failed_ = true; + FreeBuffer(); + return false; + } +} + +void CopyingOutputStreamAdaptor::AllocateBufferIfNeeded() { + if (buffer_ == NULL) { + buffer_.reset(new uint8[buffer_size_]); + } +} + +void CopyingOutputStreamAdaptor::FreeBuffer() { + buffer_used_ = 0; + buffer_.reset(); +} + +// =================================================================== + +FileInputStream::FileInputStream(int file_descriptor, int block_size) + : copying_input_(file_descriptor), + impl_(©ing_input_, block_size) { +} + +FileInputStream::~FileInputStream() {} + +bool FileInputStream::Close() { + return copying_input_.Close(); +} + +bool FileInputStream::Next(const void** data, int* size) { + return impl_.Next(data, size); +} + +void FileInputStream::BackUp(int count) { + impl_.BackUp(count); +} + +bool FileInputStream::Skip(int count) { + return impl_.Skip(count); +} + +int64 FileInputStream::ByteCount() const { + return impl_.ByteCount(); +} + +FileInputStream::CopyingFileInputStream::CopyingFileInputStream( + int file_descriptor) + : file_(file_descriptor), + close_on_delete_(false), + is_closed_(false), + errno_(0), + previous_seek_failed_(false) { +} + +FileInputStream::CopyingFileInputStream::~CopyingFileInputStream() { + if (close_on_delete_) { + if (!Close()) { + GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_); + } + } +} + +bool FileInputStream::CopyingFileInputStream::Close() { + GOOGLE_CHECK(!is_closed_); + + is_closed_ = true; + if (close_no_eintr(file_) != 0) { + // The docs on close() do not specify whether a file descriptor is still + // open after close() fails with EIO. However, the glibc source code + // seems to indicate that it is not. + errno_ = errno; + return false; + } + + return true; +} + +int FileInputStream::CopyingFileInputStream::Read(void* buffer, int size) { + GOOGLE_CHECK(!is_closed_); + + int result; + do { + result = read(file_, buffer, size); + } while (result < 0 && errno == EINTR); + + if (result < 0) { + // Read error (not EOF). + errno_ = errno; + } + + return result; +} + +int FileInputStream::CopyingFileInputStream::Skip(int count) { + GOOGLE_CHECK(!is_closed_); + + if (!previous_seek_failed_ && + lseek(file_, count, SEEK_CUR) != (off_t)-1) { + // Seek succeeded. + return count; + } else { + // Failed to seek. + + // Note to self: Don't seek again. This file descriptor doesn't + // support it. + previous_seek_failed_ = true; + + // Use the default implementation. + return CopyingInputStream::Skip(count); + } +} + +// =================================================================== + +FileOutputStream::FileOutputStream(int file_descriptor, int block_size) + : copying_output_(file_descriptor), + impl_(©ing_output_, block_size) { +} + +FileOutputStream::~FileOutputStream() { + impl_.Flush(); +} + +bool FileOutputStream::Close() { + bool flush_succeeded = impl_.Flush(); + return copying_output_.Close() && flush_succeeded; +} + +bool FileOutputStream::Next(void** data, int* size) { + return impl_.Next(data, size); +} + +void FileOutputStream::BackUp(int count) { + impl_.BackUp(count); +} + +int64 FileOutputStream::ByteCount() const { + return impl_.ByteCount(); +} + +FileOutputStream::CopyingFileOutputStream::CopyingFileOutputStream( + int file_descriptor) + : file_(file_descriptor), + close_on_delete_(false), + is_closed_(false), + errno_(0) { +} + +FileOutputStream::CopyingFileOutputStream::~CopyingFileOutputStream() { + if (close_on_delete_) { + if (!Close()) { + GOOGLE_LOG(ERROR) << "close() failed: " << strerror(errno_); + } + } +} + +bool FileOutputStream::CopyingFileOutputStream::Close() { + GOOGLE_CHECK(!is_closed_); + + is_closed_ = true; + if (close_no_eintr(file_) != 0) { + // The docs on close() do not specify whether a file descriptor is still + // open after close() fails with EIO. However, the glibc source code + // seems to indicate that it is not. + errno_ = errno; + return false; + } + + return true; +} + +bool FileOutputStream::CopyingFileOutputStream::Write( + const void* buffer, int size) { + GOOGLE_CHECK(!is_closed_); + int total_written = 0; + + const uint8* buffer_base = reinterpret_cast(buffer); + + while (total_written < size) { + int bytes; + do { + bytes = write(file_, buffer_base + total_written, size - total_written); + } while (bytes < 0 && errno == EINTR); + + if (bytes <= 0) { + // Write error. + + // FIXME(kenton): According to the man page, if write() returns zero, + // there was no error; write() simply did not write anything. It's + // unclear under what circumstances this might happen, but presumably + // errno won't be set in this case. I am confused as to how such an + // event should be handled. For now I'm treating it as an error, since + // retrying seems like it could lead to an infinite loop. I suspect + // this never actually happens anyway. + + if (bytes < 0) { + errno_ = errno; + } + return false; + } + total_written += bytes; + } + + return true; +} + +// =================================================================== + +IstreamInputStream::IstreamInputStream(istream* input, int block_size) + : copying_input_(input), + impl_(©ing_input_, block_size) { +} + +IstreamInputStream::~IstreamInputStream() {} + +bool IstreamInputStream::Next(const void** data, int* size) { + return impl_.Next(data, size); +} + +void IstreamInputStream::BackUp(int count) { + impl_.BackUp(count); +} + +bool IstreamInputStream::Skip(int count) { + return impl_.Skip(count); +} + +int64 IstreamInputStream::ByteCount() const { + return impl_.ByteCount(); +} + +IstreamInputStream::CopyingIstreamInputStream::CopyingIstreamInputStream( + istream* input) + : input_(input) { +} + +IstreamInputStream::CopyingIstreamInputStream::~CopyingIstreamInputStream() {} + +int IstreamInputStream::CopyingIstreamInputStream::Read( + void* buffer, int size) { + input_->read(reinterpret_cast(buffer), size); + int result = input_->gcount(); + if (result == 0 && input_->fail() && !input_->eof()) { + return -1; + } + return result; +} + +// =================================================================== + +OstreamOutputStream::OstreamOutputStream(ostream* output, int block_size) + : copying_output_(output), + impl_(©ing_output_, block_size) { +} + +OstreamOutputStream::~OstreamOutputStream() { + impl_.Flush(); +} + +bool OstreamOutputStream::Next(void** data, int* size) { + return impl_.Next(data, size); +} + +void OstreamOutputStream::BackUp(int count) { + impl_.BackUp(count); +} + +int64 OstreamOutputStream::ByteCount() const { + return impl_.ByteCount(); +} + +OstreamOutputStream::CopyingOstreamOutputStream::CopyingOstreamOutputStream( + ostream* output) + : output_(output) { +} + +OstreamOutputStream::CopyingOstreamOutputStream::~CopyingOstreamOutputStream() { +} + +bool OstreamOutputStream::CopyingOstreamOutputStream::Write( + const void* buffer, int size) { + output_->write(reinterpret_cast(buffer), size); + return output_->good(); +} + +// =================================================================== + +ConcatenatingInputStream::ConcatenatingInputStream( + ZeroCopyInputStream* const streams[], int count) + : streams_(streams), stream_count_(count), bytes_retired_(0) { +} + +ConcatenatingInputStream::~ConcatenatingInputStream() { +} + +bool ConcatenatingInputStream::Next(const void** data, int* size) { + while (stream_count_ > 0) { + if (streams_[0]->Next(data, size)) return true; + + // That stream is done. Advance to the next one. + bytes_retired_ += streams_[0]->ByteCount(); + ++streams_; + --stream_count_; + } + + // No more streams. + return false; +} + +void ConcatenatingInputStream::BackUp(int count) { + if (stream_count_ > 0) { + streams_[0]->BackUp(count); + } else { + GOOGLE_LOG(DFATAL) << "Can't BackUp() after failed Next()."; + } +} + +bool ConcatenatingInputStream::Skip(int count) { + while (stream_count_ > 0) { + // Assume that ByteCount() can be used to find out how much we actually + // skipped when Skip() fails. + int64 target_byte_count = streams_[0]->ByteCount() + count; + if (streams_[0]->Skip(count)) return true; + + // Hit the end of the stream. Figure out how many more bytes we still have + // to skip. + int64 final_byte_count = streams_[0]->ByteCount(); + GOOGLE_DCHECK_LT(final_byte_count, target_byte_count); + count = target_byte_count - final_byte_count; + + // That stream is done. Advance to the next one. + bytes_retired_ += final_byte_count; + ++streams_; + --stream_count_; + } + + return false; +} + +int64 ConcatenatingInputStream::ByteCount() const { + if (stream_count_ == 0) { + return bytes_retired_; + } else { + return bytes_retired_ + streams_[0]->ByteCount(); + } +} + + +// =================================================================== + +LimitingInputStream::LimitingInputStream(ZeroCopyInputStream* input, + int64 limit) + : input_(input), limit_(limit) {} + +LimitingInputStream::~LimitingInputStream() { + // If we overshot the limit, back up. + if (limit_ < 0) input_->BackUp(-limit_); +} + +bool LimitingInputStream::Next(const void** data, int* size) { + if (limit_ < 0) return false; + if (!input_->Next(data, size)) return false; + + limit_ -= *size; + if (limit_ < 0) { + // We overshot the limit. Reduce *size to hide the rest of the buffer. + *size += limit_; + } + return true; +} + +void LimitingInputStream::BackUp(int count) { + if (limit_ < 0) { + input_->BackUp(count - limit_); + limit_ = count; + } else { + input_->BackUp(count); + limit_ += count; + } +} + +bool LimitingInputStream::Skip(int count) { + if (count > limit_) { + if (limit_ < 0) return false; + input_->Skip(limit_); + limit_ = 0; + return false; + } else { + if (!input_->Skip(count)) return false; + limit_ -= count; + return true; + } +} + +int64 LimitingInputStream::ByteCount() const { + if (limit_ < 0) { + return input_->ByteCount() + limit_; + } else { + return input_->ByteCount(); + } +} + + +// =================================================================== + +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/io/zero_copy_stream_impl.h b/src/google/protobuf/io/zero_copy_stream_impl.h new file mode 100644 index 00000000..bd73afb7 --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_impl.h @@ -0,0 +1,617 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains common implementations of the interfaces defined in +// zero_copy_stream.h. These implementations cover I/O on raw arrays, +// strings, and file descriptors. Of course, many users will probably +// want to write their own implementations of these interfaces specific +// to the particular I/O abstractions they prefer to use, but these +// should cover the most common cases. + +#ifndef GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ +#define GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ + +#include +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace io { + +// =================================================================== + +// A ZeroCopyInputStream backed by an in-memory array of bytes. +class LIBPROTOBUF_EXPORT ArrayInputStream : public ZeroCopyInputStream { + public: + // Create an InputStream that returns the bytes pointed to by "data". + // "data" remains the property of the caller but must remain valid until + // the stream is destroyed. If a block_size is given, calls to Next() + // will return data blocks no larger than the given size. Otherwise, the + // first call to Next() returns the entire array. block_size is mainly + // useful for testing; in production you would probably never want to set + // it. + ArrayInputStream(const void* data, int size, int block_size = -1); + ~ArrayInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + const uint8* const data_; // The byte array. + const int size_; // Total size of the array. + const int block_size_; // How many bytes to return at a time. + + int position_; + int last_returned_size_; // How many bytes we returned last time Next() + // was called (used for error checking only). + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream backed by an in-memory array of bytes. +class LIBPROTOBUF_EXPORT ArrayOutputStream : public ZeroCopyOutputStream { + public: + // Create an OutputStream that writes to the bytes pointed to by "data". + // "data" remains the property of the caller but must remain valid until + // the stream is destroyed. If a block_size is given, calls to Next() + // will return data blocks no larger than the given size. Otherwise, the + // first call to Next() returns the entire array. block_size is mainly + // useful for testing; in production you would probably never want to set + // it. + ArrayOutputStream(void* data, int size, int block_size = -1); + ~ArrayOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + uint8* const data_; // The byte array. + const int size_; // Total size of the array. + const int block_size_; // How many bytes to return at a time. + + int position_; + int last_returned_size_; // How many bytes we returned last time Next() + // was called (used for error checking only). + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ArrayOutputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which appends bytes to a string. +class LIBPROTOBUF_EXPORT StringOutputStream : public ZeroCopyOutputStream { + public: + // Create a StringOutputStream which appends bytes to the given string. + // The string remains property of the caller, but it MUST NOT be accessed + // in any way until the stream is destroyed. + // + // Hint: If you call target->reserve(n) before creating the stream, + // the first call to Next() will return at least n bytes of buffer + // space. + explicit StringOutputStream(string* target); + ~StringOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + static const int kMinimumSize = 16; + + string* target_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringOutputStream); +}; + +// Note: There is no StringInputStream. Instead, just create an +// ArrayInputStream as follows: +// ArrayInputStream input(str.data(), str.size()); + +// =================================================================== + + +// =================================================================== + +// A generic traditional input stream interface. +// +// Lots of traditional input streams (e.g. file descriptors, C stdio +// streams, and C++ iostreams) expose an interface where every read +// involves copying bytes into a buffer. If you want to take such an +// interface and make a ZeroCopyInputStream based on it, simply implement +// CopyingInputStream and then use CopyingInputStreamAdaptor. +// +// CopyingInputStream implementations should avoid buffering if possible. +// CopyingInputStreamAdaptor does its own buffering and will read data +// in large blocks. +class LIBPROTOBUF_EXPORT CopyingInputStream { + public: + virtual ~CopyingInputStream(); + + // Reads up to "size" bytes into the given buffer. Returns the number of + // bytes read. Read() waits until at least one byte is available, or + // returns zero if no bytes will ever become available (EOF), or -1 if a + // permanent read error occurred. + virtual int Read(void* buffer, int size) = 0; + + // Skips the next "count" bytes of input. Returns the number of bytes + // actually skipped. This will always be exactly equal to "count" unless + // EOF was reached or a permanent read error occurred. + // + // The default implementation just repeatedly calls Read() into a scratch + // buffer. + virtual int Skip(int count); +}; + +// A ZeroCopyInputStream which reads from a CopyingInputStream. This is +// useful for implementing ZeroCopyInputStreams that read from traditional +// streams. Note that this class is not really zero-copy. +// +// If you want to read from file descriptors or C++ istreams, this is +// already implemented for you: use FileInputStream or IstreamInputStream +// respectively. +class LIBPROTOBUF_EXPORT CopyingInputStreamAdaptor : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given CopyingInputStream. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. The caller retains ownership of + // copying_stream unless SetOwnsCopyingStream(true) is called. + explicit CopyingInputStreamAdaptor(CopyingInputStream* copying_stream, + int block_size = -1); + ~CopyingInputStreamAdaptor(); + + // Call SetOwnsCopyingStream(true) to tell the CopyingInputStreamAdaptor to + // delete the underlying CopyingInputStream when it is destroyed. + void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; } + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + // Insures that buffer_ is not NULL. + void AllocateBufferIfNeeded(); + // Frees the buffer and resets buffer_used_. + void FreeBuffer(); + + // The underlying copying stream. + CopyingInputStream* copying_stream_; + bool owns_copying_stream_; + + // True if we have seen a permenant error from the underlying stream. + bool failed_; + + // The current position of copying_stream_, relative to the point where + // we started reading. + int64 position_; + + // Data is read into this buffer. It may be NULL if no buffer is currently + // in use. Otherwise, it points to an array of size buffer_size_. + scoped_array buffer_; + const int buffer_size_; + + // Number of valid bytes currently in the buffer (i.e. the size last + // returned by Next()). 0 <= buffer_used_ <= buffer_size_. + int buffer_used_; + + // Number of bytes in the buffer which were backed up over by a call to + // BackUp(). These need to be returned again. + // 0 <= backup_bytes_ <= buffer_used_ + int backup_bytes_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingInputStreamAdaptor); +}; + +// =================================================================== + +// A generic traditional output stream interface. +// +// Lots of traditional output streams (e.g. file descriptors, C stdio +// streams, and C++ iostreams) expose an interface where every write +// involves copying bytes from a buffer. If you want to take such an +// interface and make a ZeroCopyOutputStream based on it, simply implement +// CopyingOutputStream and then use CopyingOutputStreamAdaptor. +// +// CopyingOutputStream implementations should avoid buffering if possible. +// CopyingOutputStreamAdaptor does its own buffering and will write data +// in large blocks. +class LIBPROTOBUF_EXPORT CopyingOutputStream { + public: + virtual ~CopyingOutputStream(); + + // Writes "size" bytes from the given buffer to the output. Returns true + // if successful, false on a write error. + virtual bool Write(const void* buffer, int size) = 0; +}; + +// A ZeroCopyOutputStream which writes to a CopyingOutputStream. This is +// useful for implementing ZeroCopyOutputStreams that write to traditional +// streams. Note that this class is not really zero-copy. +// +// If you want to write to file descriptors or C++ ostreams, this is +// already implemented for you: use FileOutputStream or OstreamOutputStream +// respectively. +class LIBPROTOBUF_EXPORT CopyingOutputStreamAdaptor : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given Unix file descriptor. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit CopyingOutputStreamAdaptor(CopyingOutputStream* copying_stream, + int block_size = -1); + ~CopyingOutputStreamAdaptor(); + + // Writes all pending data to the underlying stream. Returns false if a + // write error occurred on the underlying stream. (The underlying + // stream itself is not necessarily flushed.) + bool Flush(); + + // Call SetOwnsCopyingStream(true) to tell the CopyingOutputStreamAdaptor to + // delete the underlying CopyingOutputStream when it is destroyed. + void SetOwnsCopyingStream(bool value) { owns_copying_stream_ = value; } + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + // Write the current buffer, if it is present. + bool WriteBuffer(); + // Insures that buffer_ is not NULL. + void AllocateBufferIfNeeded(); + // Frees the buffer. + void FreeBuffer(); + + // The underlying copying stream. + CopyingOutputStream* copying_stream_; + bool owns_copying_stream_; + + // True if we have seen a permenant error from the underlying stream. + bool failed_; + + // The current position of copying_stream_, relative to the point where + // we started writing. + int64 position_; + + // Data is written from this buffer. It may be NULL if no buffer is + // currently in use. Otherwise, it points to an array of size buffer_size_. + scoped_array buffer_; + const int buffer_size_; + + // Number of valid bytes currently in the buffer (i.e. the size last + // returned by Next()). When BackUp() is called, we just reduce this. + // 0 <= buffer_used_ <= buffer_size_. + int buffer_used_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOutputStreamAdaptor); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from a file descriptor. +// +// FileInputStream is preferred over using an ifstream with IstreamInputStream. +// The latter will introduce an extra layer of buffering, harming performance. +// Also, it's conceivable that FileInputStream could someday be enhanced +// to use zero-copy file descriptors on OSs which support them. +class LIBPROTOBUF_EXPORT FileInputStream : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given Unix file descriptor. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. + explicit FileInputStream(int file_descriptor, int block_size = -1); + ~FileInputStream(); + + // Flushes any buffers and closes the underlying file. Returns false if + // an error occurs during the process; use GetErrno() to examine the error. + // Even if an error occurs, the file descriptor is closed when this returns. + bool Close(); + + // By default, the file descriptor is not closed when the stream is + // destroyed. Call SetCloseOnDelete(true) to change that. WARNING: + // This leaves no way for the caller to detect if close() fails. If + // detecting close() errors is important to you, you should arrange + // to close the descriptor yourself. + void SetCloseOnDelete(bool value) { copying_input_.SetCloseOnDelete(value); } + + // If an I/O error has occurred on this file descriptor, this is the + // errno from that error. Otherwise, this is zero. Once an error + // occurs, the stream is broken and all subsequent operations will + // fail. + int GetErrno() { return copying_input_.GetErrno(); } + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingFileInputStream : public CopyingInputStream { + public: + CopyingFileInputStream(int file_descriptor); + ~CopyingFileInputStream(); + + bool Close(); + void SetCloseOnDelete(bool value) { close_on_delete_ = value; } + int GetErrno() { return errno_; } + + // implements CopyingInputStream --------------------------------- + int Read(void* buffer, int size); + int Skip(int count); + + private: + // The file descriptor. + const int file_; + bool close_on_delete_; + bool is_closed_; + + // The errno of the I/O error, if one has occurred. Otherwise, zero. + int errno_; + + // Did we try to seek once and fail? If so, we assume this file descriptor + // doesn't support seeking and won't try again. + bool previous_seek_failed_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileInputStream); + }; + + CopyingFileInputStream copying_input_; + CopyingInputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which writes to a file descriptor. +// +// FileInputStream is preferred over using an ofstream with OstreamOutputStream. +// The latter will introduce an extra layer of buffering, harming performance. +// Also, it's conceivable that FileInputStream could someday be enhanced +// to use zero-copy file descriptors on OSs which support them. +class LIBPROTOBUF_EXPORT FileOutputStream : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given Unix file descriptor. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit FileOutputStream(int file_descriptor, int block_size = -1); + ~FileOutputStream(); + + // Flushes any buffers and closes the underlying file. Returns false if + // an error occurs during the process; use GetErrno() to examine the error. + // Even if an error occurs, the file descriptor is closed when this returns. + bool Close(); + + // By default, the file descriptor is not closed when the stream is + // destroyed. Call SetCloseOnDelete(true) to change that. WARNING: + // This leaves no way for the caller to detect if close() fails. If + // detecting close() errors is important to you, you should arrange + // to close the descriptor yourself. + void SetCloseOnDelete(bool value) { copying_output_.SetCloseOnDelete(value); } + + // If an I/O error has occurred on this file descriptor, this is the + // errno from that error. Otherwise, this is zero. Once an error + // occurs, the stream is broken and all subsequent operations will + // fail. + int GetErrno() { return copying_output_.GetErrno(); } + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingFileOutputStream : public CopyingOutputStream { + public: + CopyingFileOutputStream(int file_descriptor); + ~CopyingFileOutputStream(); + + bool Close(); + void SetCloseOnDelete(bool value) { close_on_delete_ = value; } + int GetErrno() { return errno_; } + + // implements CopyingOutputStream -------------------------------- + bool Write(const void* buffer, int size); + + private: + // The file descriptor. + const int file_; + bool close_on_delete_; + bool is_closed_; + + // The errno of the I/O error, if one has occurred. Otherwise, zero. + int errno_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingFileOutputStream); + }; + + CopyingFileOutputStream copying_output_; + CopyingOutputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FileOutputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from a C++ istream. +// +// Note that for reading files (or anything represented by a file descriptor), +// FileInputStream is more efficient. +class LIBPROTOBUF_EXPORT IstreamInputStream : public ZeroCopyInputStream { + public: + // Creates a stream that reads from the given C++ istream. + // If a block_size is given, it specifies the number of bytes that + // should be read and returned with each call to Next(). Otherwise, + // a reasonable default is used. + explicit IstreamInputStream(istream* stream, int block_size = -1); + ~IstreamInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingIstreamInputStream : public CopyingInputStream { + public: + CopyingIstreamInputStream(istream* input); + ~CopyingIstreamInputStream(); + + // implements CopyingInputStream --------------------------------- + int Read(void* buffer, int size); + // (We use the default implementation of Skip().) + + private: + // The stream. + istream* input_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingIstreamInputStream); + }; + + CopyingIstreamInputStream copying_input_; + CopyingInputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(IstreamInputStream); +}; + +// =================================================================== + +// A ZeroCopyOutputStream which writes to a C++ ostream. +// +// Note that for writing files (or anything represented by a file descriptor), +// FileOutputStream is more efficient. +class LIBPROTOBUF_EXPORT OstreamOutputStream : public ZeroCopyOutputStream { + public: + // Creates a stream that writes to the given C++ ostream. + // If a block_size is given, it specifies the size of the buffers + // that should be returned by Next(). Otherwise, a reasonable default + // is used. + explicit OstreamOutputStream(ostream* stream, int block_size = -1); + ~OstreamOutputStream(); + + // implements ZeroCopyOutputStream --------------------------------- + bool Next(void** data, int* size); + void BackUp(int count); + int64 ByteCount() const; + + private: + class LIBPROTOBUF_EXPORT CopyingOstreamOutputStream : public CopyingOutputStream { + public: + CopyingOstreamOutputStream(ostream* output); + ~CopyingOstreamOutputStream(); + + // implements CopyingOutputStream -------------------------------- + bool Write(const void* buffer, int size); + + private: + // The stream. + ostream* output_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(CopyingOstreamOutputStream); + }; + + CopyingOstreamOutputStream copying_output_; + CopyingOutputStreamAdaptor impl_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(OstreamOutputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which reads from several other streams in sequence. +// ConcatenatingInputStream is unable to distinguish between end-of-stream +// and read errors in the underlying streams, so it assumes any errors mean +// end-of-stream. So, if the underlying streams fail for any other reason, +// ConcatenatingInputStream may do odd things. It is suggested that you do +// not use ConcatenatingInputStream on streams that might produce read errors +// other than end-of-stream. +class LIBPROTOBUF_EXPORT ConcatenatingInputStream : public ZeroCopyInputStream { + public: + // All streams passed in as well as the array itself must remain valid + // until the ConcatenatingInputStream is destroyed. + ConcatenatingInputStream(ZeroCopyInputStream* const streams[], int count); + ~ConcatenatingInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + // As streams are retired, streams_ is incremented and count_ is + // decremented. + ZeroCopyInputStream* const* streams_; + int stream_count_; + int64 bytes_retired_; // Bytes read from previous streams. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ConcatenatingInputStream); +}; + +// =================================================================== + +// A ZeroCopyInputStream which wraps some other stream and limits it to +// a particular byte count. +class LIBPROTOBUF_EXPORT LimitingInputStream : public ZeroCopyInputStream { + public: + LimitingInputStream(ZeroCopyInputStream* input, int64 limit); + ~LimitingInputStream(); + + // implements ZeroCopyInputStream ---------------------------------- + bool Next(const void** data, int* size); + void BackUp(int count); + bool Skip(int count); + int64 ByteCount() const; + + + private: + ZeroCopyInputStream* input_; + int64 limit_; // Decreases as we go, becomes negative if we overshoot. + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(LimitingInputStream); +}; + +// =================================================================== + +} // namespace io +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_IO_ZERO_COPY_STREAM_IMPL_H__ diff --git a/src/google/protobuf/io/zero_copy_stream_unittest.cc b/src/google/protobuf/io/zero_copy_stream_unittest.cc new file mode 100644 index 00000000..c618041f --- /dev/null +++ b/src/google/protobuf/io/zero_copy_stream_unittest.cc @@ -0,0 +1,443 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Testing strategy: For each type of I/O (array, string, file, etc.) we +// create an output stream and write some data to it, then create a +// corresponding input stream to read the same data back and expect it to +// match. When the data is written, it is written in several small chunks +// of varying sizes, with a BackUp() after each chunk. It is read back +// similarly, but with chunks separated at different points. The whole +// process is run with a variety of block sizes for both the input and +// the output. +// +// TODO(kenton): Rewrite this test to bring it up to the standards of all +// the other proto2 tests. May want to wait for gTest to implement +// "parametized tests" so that one set of tests can be used on all the +// implementations. + +#ifdef _MSC_VER +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace io { +namespace { + +#ifdef _WIN32 +#define pipe(fds) _pipe(fds, 4096, O_BINARY) +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +class IoTest : public testing::Test { + protected: + // Test helpers. + + // Helper to write an array of data to an output stream. + bool WriteToOutput(ZeroCopyOutputStream* output, const void* data, int size); + // Helper to read a fixed-length array of data from an input stream. + int ReadFromInput(ZeroCopyInputStream* input, void* data, int size); + // Write a string to the output stream. + void WriteString(ZeroCopyOutputStream* output, const char* str); + // Read a number of bytes equal to the size of the given string and checks + // that it matches the string. + void ReadString(ZeroCopyInputStream* input, const char* str); + // Writes some text to the output stream in a particular order. Returns + // the number of bytes written, incase the caller needs that to set up an + // input stream. + int WriteStuff(ZeroCopyOutputStream* output); + // Reads text from an input stream and expects it to match what + // WriteStuff() writes. + void ReadStuff(ZeroCopyInputStream* input); + + static const int kBlockSizes[]; + static const int kBlockSizeCount; +}; + +const int IoTest::kBlockSizes[] = {-1, 1, 2, 5, 7, 10, 23, 64}; +const int IoTest::kBlockSizeCount = GOOGLE_ARRAYSIZE(IoTest::kBlockSizes); + +bool IoTest::WriteToOutput(ZeroCopyOutputStream* output, + const void* data, int size) { + const uint8* in = reinterpret_cast(data); + int in_size = size; + + void* out; + int out_size; + + while (true) { + if (!output->Next(&out, &out_size)) { + return false; + } + + if (in_size <= out_size) { + memcpy(out, in, in_size); + output->BackUp(out_size - in_size); + return true; + } + + memcpy(out, in, out_size); + in += out_size; + in_size -= out_size; + } +} + +int IoTest::ReadFromInput(ZeroCopyInputStream* input, void* data, int size) { + uint8* out = reinterpret_cast(data); + int out_size = size; + + const void* in; + int in_size = 0; + + while (true) { + if (!input->Next(&in, &in_size)) { + return size - out_size; + } + + if (out_size <= in_size) { + memcpy(out, in, out_size); + input->BackUp(in_size - out_size); + return size; // Copied all of it. + } + + memcpy(out, in, in_size); + out += in_size; + out_size -= in_size; + } +} + +void IoTest::WriteString(ZeroCopyOutputStream* output, const char* str) { + EXPECT_TRUE(WriteToOutput(output, str, strlen(str))); +} + +void IoTest::ReadString(ZeroCopyInputStream* input, const char* str) { + int length = strlen(str); + scoped_array buffer(new char[length + 1]); + buffer[length] = '\0'; + EXPECT_EQ(ReadFromInput(input, buffer.get(), length), length); + EXPECT_STREQ(str, buffer.get()); +} + +int IoTest::WriteStuff(ZeroCopyOutputStream* output) { + WriteString(output, "Hello world!\n"); + WriteString(output, "Some te"); + WriteString(output, "xt. Blah blah."); + WriteString(output, "abcdefg"); + WriteString(output, "01234567890123456789"); + WriteString(output, "foobar"); + + EXPECT_EQ(output->ByteCount(), 68); + + int result = output->ByteCount(); + return result; +} + +// Reads text from an input stream and expects it to match what WriteStuff() +// writes. +void IoTest::ReadStuff(ZeroCopyInputStream* input) { + ReadString(input, "Hello world!\n"); + ReadString(input, "Some text. "); + ReadString(input, "Blah "); + ReadString(input, "blah."); + ReadString(input, "abcdefg"); + EXPECT_TRUE(input->Skip(20)); + ReadString(input, "foo"); + ReadString(input, "bar"); + + EXPECT_EQ(input->ByteCount(), 68); + + uint8 byte; + EXPECT_EQ(ReadFromInput(input, &byte, 1), 0); +} + +// =================================================================== + +TEST_F(IoTest, ArrayIo) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + int size; + { + ArrayOutputStream output(buffer, kBufferSize, kBlockSizes[i]); + size = WriteStuff(&output); + } + { + ArrayInputStream input(buffer, size, kBlockSizes[j]); + ReadStuff(&input); + } + } + } +} + +// There is no string input, only string output. Also, it doesn't support +// explicit block sizes. So, we'll only run one test and we'll use +// ArrayInput to read back the results. +TEST_F(IoTest, StringIo) { + string str; + { + StringOutputStream output(&str); + WriteStuff(&output); + } + { + ArrayInputStream input(str.data(), str.size()); + ReadStuff(&input); + } +} + + +// To test files, we create a temporary file, write, read, truncate, repeat. +TEST_F(IoTest, FileIo) { + string filename = TestTempDir() + "/zero_copy_stream_test_file"; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + // Make a temporary file. + int file = + open(filename.c_str(), O_RDWR | O_CREAT | O_TRUNC | O_BINARY, 0777); + ASSERT_GE(file, 0); + + { + FileOutputStream output(file, kBlockSizes[i]); + WriteStuff(&output); + EXPECT_EQ(0, output.GetErrno()); + } + + // Rewind. + ASSERT_NE(lseek(file, 0, SEEK_SET), (off_t)-1); + + { + FileInputStream input(file, kBlockSizes[j]); + ReadStuff(&input); + EXPECT_EQ(0, input.GetErrno()); + } + + close(file); + } + } +} + +// MSVC raises various debugging exceptions if we try to use a file +// descriptor of -1, defeating our tests below. This class will disable +// these debug assertions while in scope. +class MsvcDebugDisabler { + public: +#ifdef _MSC_VER + MsvcDebugDisabler() { + old_handler_ = _set_invalid_parameter_handler(MyHandler); + old_mode_ = _CrtSetReportMode(_CRT_ASSERT, 0); + } + ~MsvcDebugDisabler() { + old_handler_ = _set_invalid_parameter_handler(old_handler_); + old_mode_ = _CrtSetReportMode(_CRT_ASSERT, old_mode_); + } + + static void MyHandler(const wchar_t *expr, + const wchar_t *func, + const wchar_t *file, + unsigned int line, + uintptr_t pReserved) { + // do nothing + } + + _invalid_parameter_handler old_handler_; + int old_mode_; +#else + // Dummy constructor and destructor to ensure that GCC doesn't complain + // that debug_disabler is an unused variable. + MsvcDebugDisabler() {} + ~MsvcDebugDisabler() {} +#endif +}; + +// Test that FileInputStreams report errors correctly. +TEST_F(IoTest, FileReadError) { + MsvcDebugDisabler debug_disabler; + + // -1 = invalid file descriptor. + FileInputStream input(-1); + + const void* buffer; + int size; + EXPECT_FALSE(input.Next(&buffer, &size)); + EXPECT_EQ(EBADF, input.GetErrno()); +} + +// Test that FileOutputStreams report errors correctly. +TEST_F(IoTest, FileWriteError) { + MsvcDebugDisabler debug_disabler; + + // -1 = invalid file descriptor. + FileOutputStream input(-1); + + void* buffer; + int size; + + // The first call to Next() succeeds because it doesn't have anything to + // write yet. + EXPECT_TRUE(input.Next(&buffer, &size)); + + // Second call fails. + EXPECT_FALSE(input.Next(&buffer, &size)); + + EXPECT_EQ(EBADF, input.GetErrno()); +} + +// Pipes are not seekable, so File{Input,Output}Stream ends up doing some +// different things to handle them. We'll test by writing to a pipe and +// reading back from it. +TEST_F(IoTest, PipeIo) { + int files[2]; + + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + // Need to create a new pipe each time because ReadStuff() expects + // to see EOF at the end. + ASSERT_EQ(pipe(files), 0); + + { + FileOutputStream output(files[1], kBlockSizes[i]); + WriteStuff(&output); + EXPECT_EQ(0, output.GetErrno()); + } + close(files[1]); // Send EOF. + + { + FileInputStream input(files[0], kBlockSizes[j]); + ReadStuff(&input); + EXPECT_EQ(0, input.GetErrno()); + } + close(files[0]); + } + } +} + +// Test using C++ iostreams. +TEST_F(IoTest, IostreamIo) { + for (int i = 0; i < kBlockSizeCount; i++) { + for (int j = 0; j < kBlockSizeCount; j++) { + stringstream stream; + + { + OstreamOutputStream output(&stream, kBlockSizes[i]); + WriteStuff(&output); + EXPECT_FALSE(stream.fail()); + } + + { + IstreamInputStream input(&stream, kBlockSizes[j]); + ReadStuff(&input); + EXPECT_TRUE(stream.eof()); + } + } + } +} + +// To test ConcatenatingInputStream, we create several ArrayInputStreams +// covering a buffer and then concatenate them. +TEST_F(IoTest, ConcatenatingInputStream) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + // Fill the buffer. + ArrayOutputStream output(buffer, kBufferSize); + WriteStuff(&output); + + // Now split it up into multiple streams of varying sizes. + ASSERT_EQ(68, output.ByteCount()); // Test depends on this. + ArrayInputStream input1(buffer , 12); + ArrayInputStream input2(buffer + 12, 7); + ArrayInputStream input3(buffer + 19, 6); + ArrayInputStream input4(buffer + 25, 15); + ArrayInputStream input5(buffer + 40, 0); + // Note: We want to make sure we have a stream boundary somewhere between + // bytes 42 and 62, which is the range that it Skip()ed by ReadStuff(). This + // tests that a bug that existed in the original code for Skip() is fixed. + ArrayInputStream input6(buffer + 40, 10); + ArrayInputStream input7(buffer + 50, 18); // Total = 68 bytes. + + ZeroCopyInputStream* streams[] = + {&input1, &input2, &input3, &input4, &input5, &input6, &input7}; + + // Create the concatenating stream and read. + ConcatenatingInputStream input(streams, GOOGLE_ARRAYSIZE(streams)); + ReadStuff(&input); +} + +// To test LimitingInputStream, we write our golden text to a buffer, then +// create an ArrayInputStream that contains the whole buffer (not just the +// bytes written), then use a LimitingInputStream to limit it just to the +// bytes written. +TEST_F(IoTest, LimitingInputStream) { + const int kBufferSize = 256; + uint8 buffer[kBufferSize]; + + // Fill the buffer. + ArrayOutputStream output(buffer, kBufferSize); + WriteStuff(&output); + + // Set up input. + ArrayInputStream array_input(buffer, kBufferSize); + LimitingInputStream input(&array_input, output.ByteCount()); + + ReadStuff(&input); +} + +// Check that a zero-size array doesn't confuse the code. +TEST(ZeroSizeArray, Input) { + ArrayInputStream input(NULL, 0); + const void* data; + int size; + EXPECT_FALSE(input.Next(&data, &size)); +} + +TEST(ZeroSizeArray, Output) { + ArrayOutputStream output(NULL, 0); + void* data; + int size; + EXPECT_FALSE(output.Next(&data, &size)); +} + +} // namespace +} // namespace io +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc new file mode 100644 index 00000000..b9b1ac6c --- /dev/null +++ b/src/google/protobuf/message.cc @@ -0,0 +1,343 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; +using internal::ReflectionOps; + +static string InitializationErrorMessage(const char* action, + const Message& message) { + return strings::Substitute( + "Can't $0 message of type \"$1\" because it is missing required " + "fields: $2", + action, message.GetDescriptor()->full_name(), + message.InitializationErrorString()); +} + +Message::~Message() {} + +void Message::MergeFrom(const Message& from) { + const Descriptor* descriptor = GetDescriptor(); + GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) + << ": Tried to merge from a message with a different type. " + "to: " << descriptor->full_name() << ", " + "from:" << from.GetDescriptor()->full_name(); + ReflectionOps::Merge(from, this); +} + +void Message::CopyFrom(const Message& from) { + const Descriptor* descriptor = GetDescriptor(); + GOOGLE_CHECK_EQ(from.GetDescriptor(), descriptor) + << ": Tried to copy from a message with a different type." + "to: " << descriptor->full_name() << ", " + "from:" << from.GetDescriptor()->full_name(); + ReflectionOps::Copy(from, this); +} + +void Message::Clear() { + ReflectionOps::Clear(this); +} + +bool Message::IsInitialized() const { + return ReflectionOps::IsInitialized(*this); +} + +void Message::FindInitializationErrors(vector* errors) const { + return ReflectionOps::FindInitializationErrors(*this, "", errors); +} + +string Message::InitializationErrorString() const { + vector errors; + FindInitializationErrors(&errors); + return JoinStrings(errors, ", "); +} + +void Message::CheckInitialized() const { + GOOGLE_CHECK(IsInitialized()) + << "Message of type \"" << GetDescriptor()->full_name() + << "\" is missing required fields: " << InitializationErrorString(); +} + +void Message::DiscardUnknownFields() { + return ReflectionOps::DiscardUnknownFields(this); +} + +bool Message::MergePartialFromCodedStream(io::CodedInputStream* input) { + return WireFormat::ParseAndMergePartial(input, this); +} + +bool Message::MergeFromCodedStream(io::CodedInputStream* input) { + if (!MergePartialFromCodedStream(input)) return false; + if (!IsInitialized()) { + GOOGLE_LOG(ERROR) << InitializationErrorMessage("parse", *this); + return false; + } + return true; +} + +bool Message::ParseFromCodedStream(io::CodedInputStream* input) { + Clear(); + return MergeFromCodedStream(input); +} + +bool Message::ParsePartialFromCodedStream(io::CodedInputStream* input) { + Clear(); + return MergePartialFromCodedStream(input); +} + +bool Message::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) { + io::CodedInputStream decoder(input); + return ParseFromCodedStream(&decoder) && decoder.ConsumedEntireMessage(); +} + +bool Message::ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input) { + io::CodedInputStream decoder(input); + return ParsePartialFromCodedStream(&decoder) && + decoder.ConsumedEntireMessage(); +} + +bool Message::ParseFromString(const string& data) { + io::ArrayInputStream input(data.data(), data.size()); + return ParseFromZeroCopyStream(&input); +} + +bool Message::ParsePartialFromString(const string& data) { + io::ArrayInputStream input(data.data(), data.size()); + return ParsePartialFromZeroCopyStream(&input); +} + +bool Message::ParseFromArray(const void* data, int size) { + io::ArrayInputStream input(data, size); + return ParseFromZeroCopyStream(&input); +} + +bool Message::ParsePartialFromArray(const void* data, int size) { + io::ArrayInputStream input(data, size); + return ParsePartialFromZeroCopyStream(&input); +} + +bool Message::ParseFromFileDescriptor(int file_descriptor) { + io::FileInputStream input(file_descriptor); + return ParseFromZeroCopyStream(&input) && input.GetErrno() == 0; +} + +bool Message::ParsePartialFromFileDescriptor(int file_descriptor) { + io::FileInputStream input(file_descriptor); + return ParsePartialFromZeroCopyStream(&input) && input.GetErrno() == 0; +} + +bool Message::ParseFromIstream(istream* input) { + io::IstreamInputStream zero_copy_input(input); + return ParseFromZeroCopyStream(&zero_copy_input) && input->eof(); +} + +bool Message::ParsePartialFromIstream(istream* input) { + io::IstreamInputStream zero_copy_input(input); + return ParsePartialFromZeroCopyStream(&zero_copy_input) && input->eof(); +} + + + +bool Message::SerializeWithCachedSizes( + io::CodedOutputStream* output) const { + return WireFormat::SerializeWithCachedSizes(*this, GetCachedSize(), output); +} + +int Message::ByteSize() const { + int size = WireFormat::ByteSize(*this); + SetCachedSize(size); + return size; +} + +void Message::SetCachedSize(int size) const { + GOOGLE_LOG(FATAL) << "Message class \"" << GetDescriptor()->full_name() + << "\" implements neither SetCachedSize() nor ByteSize(). " + "Must implement one or the other."; +} + +bool Message::SerializeToCodedStream(io::CodedOutputStream* output) const { + GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this); + return SerializePartialToCodedStream(output); +} + +bool Message::SerializePartialToCodedStream( + io::CodedOutputStream* output) const { + ByteSize(); // Force size to be cached. + if (!SerializeWithCachedSizes(output)) return false; + return true; +} + +bool Message::SerializeToZeroCopyStream( + io::ZeroCopyOutputStream* output) const { + io::CodedOutputStream encoder(output); + return SerializeToCodedStream(&encoder); +} + +bool Message::SerializePartialToZeroCopyStream( + io::ZeroCopyOutputStream* output) const { + io::CodedOutputStream encoder(output); + return SerializePartialToCodedStream(&encoder); +} + +bool Message::AppendToString(string* output) const { + GOOGLE_DCHECK(IsInitialized()) << InitializationErrorMessage("serialize", *this); + return AppendPartialToString(output); +} + +bool Message::AppendPartialToString(string* output) const { + // For efficiency, we'd like to reserve the exact amount of space we need + // in the string. + int total_size = output->size() + ByteSize(); + output->reserve(total_size); + + io::StringOutputStream output_stream(output); + + { + io::CodedOutputStream encoder(&output_stream); + if (!SerializeWithCachedSizes(&encoder)) return false; + } + + GOOGLE_CHECK_EQ(output_stream.ByteCount(), total_size); + return true; +} + +bool Message::SerializeToString(string* output) const { + output->clear(); + return AppendToString(output); +} + +bool Message::SerializePartialToString(string* output) const { + output->clear(); + return AppendPartialToString(output); +} + +bool Message::SerializeToArray(void* data, int size) const { + io::ArrayOutputStream output_stream(data, size); + return SerializeToZeroCopyStream(&output_stream); +} + +bool Message::SerializePartialToArray(void* data, int size) const { + io::ArrayOutputStream output_stream(data, size); + return SerializePartialToZeroCopyStream(&output_stream); +} + +bool Message::SerializeToFileDescriptor(int file_descriptor) const { + io::FileOutputStream output(file_descriptor); + return SerializeToZeroCopyStream(&output); +} + +bool Message::SerializePartialToFileDescriptor(int file_descriptor) const { + io::FileOutputStream output(file_descriptor); + return SerializePartialToZeroCopyStream(&output); +} + +bool Message::SerializeToOstream(ostream* output) const { + io::OstreamOutputStream zero_copy_output(output); + return SerializeToZeroCopyStream(&zero_copy_output); +} + +bool Message::SerializePartialToOstream(ostream* output) const { + io::OstreamOutputStream zero_copy_output(output); + return SerializePartialToZeroCopyStream(&zero_copy_output); +} + + +Reflection::~Reflection() {} + +// =================================================================== +// MessageFactory + +MessageFactory::~MessageFactory() {} + +namespace { + +class GeneratedMessageFactory : public MessageFactory { + public: + GeneratedMessageFactory(); + ~GeneratedMessageFactory(); + + static GeneratedMessageFactory* singleton(); + + void RegisterType(const Descriptor* descriptor, const Message* prototype); + + // implements MessageFactory --------------------------------------- + const Message* GetPrototype(const Descriptor* type); + + private: + hash_map type_map_; +}; + +GeneratedMessageFactory::GeneratedMessageFactory() {} +GeneratedMessageFactory::~GeneratedMessageFactory() {} + +GeneratedMessageFactory* GeneratedMessageFactory::singleton() { + // No need for thread-safety here because this will be called at static + // initialization time. (And GCC4 makes this thread-safe anyway.) + static GeneratedMessageFactory singleton; + return &singleton; +} + +void GeneratedMessageFactory::RegisterType(const Descriptor* descriptor, + const Message* prototype) { + GOOGLE_DCHECK_EQ(descriptor->file()->pool(), DescriptorPool::generated_pool()) + << "Tried to register a non-generated type with the generated " + "type registry."; + + if (!InsertIfNotPresent(&type_map_, descriptor, prototype)) { + GOOGLE_LOG(DFATAL) << "Type is already registered: " << descriptor->full_name(); + } +} + +const Message* GeneratedMessageFactory::GetPrototype(const Descriptor* type) { + return FindPtrOrNull(type_map_, type); +} + +} // namespace + +MessageFactory* MessageFactory::generated_factory() { + return GeneratedMessageFactory::singleton(); +} + +void MessageFactory::InternalRegisterGeneratedMessage( + const Descriptor* descriptor, const Message* prototype) { + GeneratedMessageFactory::singleton()->RegisterType(descriptor, prototype); +} + + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h new file mode 100644 index 00000000..1a297b12 --- /dev/null +++ b/src/google/protobuf/message.h @@ -0,0 +1,687 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains the abstract interface for all protocol messages. +// Although it's possible to implement this interface manually, most users +// will use the protocol compiler to generate implementations. +// +// Example usage: +// +// Say you have a message defined as: +// +// message Foo { +// optional string text = 1; +// repeated int32 numbers = 2; +// } +// +// Then, if you used the protocol compiler to generate a class from the above +// definition, you could use it like so: +// +// string data; // Will store a serialized version of the message. +// +// { +// // Create a message and serialize it. +// Foo foo; +// foo.set_text("Hello World!"); +// foo.add_numbers(1); +// foo.add_numbers(5); +// foo.add_numbers(42); +// +// foo.SerializeToString(&data); +// } +// +// { +// // Parse the serialized message and check that it contains the +// // correct data. +// Foo foo; +// foo.ParseFromString(data); +// +// assert(foo.text() == "Hello World!"); +// assert(foo.numbers_size() == 3); +// assert(foo.numbers(0) == 1); +// assert(foo.numbers(1) == 5); +// assert(foo.numbers(2) == 42); +// } +// +// { +// // Same as the last block, but do it dynamically via the Message +// // reflection interface. +// Message* foo = new Foo; +// Descriptor* descriptor = foo->GetDescriptor(); +// +// // Get the descriptors for the fields we're interested in and verify +// // their types. +// FieldDescriptor* text_field = descriptor->FindFieldByName("text"); +// assert(text_field != NULL); +// assert(text_field->type() == FieldDescriptor::TYPE_STRING); +// assert(text_field->label() == FieldDescriptor::TYPE_OPTIONAL); +// FieldDescriptor* numbers_field = descriptor->FindFieldByName("numbers"); +// assert(numbers_field != NULL); +// assert(numbers_field->type() == FieldDescriptor::TYPE_INT32); +// assert(numbers_field->label() == FieldDescriptor::TYPE_REPEATED); +// +// // Parse the message. +// foo->ParseFromString(data); +// +// // Use the reflection interface to examine the contents. +// Reflection* reflection = foo->GetReflection(); +// assert(reflection->GetString(foo, text_field) == "Hello World!"); +// assert(reflection->CountField(foo, numbers_field) == 3); +// assert(reflection->GetInt32(foo, numbers_field, 0) == 1); +// assert(reflection->GetInt32(foo, numbers_field, 1) == 5); +// assert(reflection->GetInt32(foo, numbers_field, 2) == 42); +// +// delete foo; +// } + +#ifndef GOOGLE_PROTOBUF_MESSAGE_H__ +#define GOOGLE_PROTOBUF_MESSAGE_H__ + +#include +#include +#include +#include + +namespace google { + +namespace protobuf { + +// Defined in this file. +class Message; +class Reflection; + +// Defined in other files. +class Descriptor; // descriptor.h +class FieldDescriptor; // descriptor.h +class EnumValueDescriptor; // descriptor.h +namespace io { + class ZeroCopyInputStream; // zero_copy_stream.h + class ZeroCopyOutputStream; // zero_copy_stream.h + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h +} +class UnknownFieldSet; // unknown_field_set.h + +// Abstract interface for protocol messages. +// +// The methods of this class that are virtual but not pure-virtual have +// default implementations based on reflection. Message classes which are +// optimized for speed will want to override these with faster implementations, +// but classes optimized for code size may be happy with keeping them. See +// the optimize_for option in descriptor.proto. +class LIBPROTOBUF_EXPORT Message { + public: + inline Message() {} + virtual ~Message(); + + // Basic Operations ------------------------------------------------ + + // Construct a new instance of the same type. Ownership is passed to the + // caller. + virtual Message* New() const = 0; + + // Make this message into a copy of the given message. The given message + // must have the same descriptor, but need not necessarily be the same class. + // By default this is just implemented as "Clear(); MergeFrom(from);". + virtual void CopyFrom(const Message& from); + + // Merge the fields from the given message into this message. Singular + // fields will be overwritten, except for embedded messages which will + // be merged. Repeated fields will be concatenated. The given message + // must be of the same type as this message (i.e. the exact same class). + virtual void MergeFrom(const Message& from); + + // Clear all fields of the message and set them to their default values. + // Clear() avoids freeing memory, assuming that any memory allocated + // to hold parts of the message will be needed again to hold the next + // message. If you actually want to free the memory used by a Message, + // you must delete it. + virtual void Clear(); + + // Quickly check if all required fields have values set. + virtual bool IsInitialized() const; + + // Verifies that IsInitialized() returns true. GOOGLE_CHECK-fails otherwise, with + // a nice error message. + void CheckInitialized() const; + + // Slowly build a list of all required fields that are not set. + // This is much, much slower than IsInitialized() as it is implemented + // purely via reflection. Generally, you should not call this unless you + // have already determined that an error exists by calling IsInitialized(). + void FindInitializationErrors(vector* errors) const; + + // Like FindInitializationErrors, but joins all the strings, delimited by + // commas, and returns them. + string InitializationErrorString() const; + + // Clears all unknown fields from this message and all embedded messages. + // Normally, if unknown tag numbers are encountered when parsing a message, + // the tag and value are stored in the message's UnknownFieldSet and + // then written back out when the message is serialized. This allows servers + // which simply route messages to other servers to pass through messages + // that have new field definitions which they don't yet know about. However, + // this behavior can have security implications. To avoid it, call this + // method after parsing. + // + // See Reflection::GetUnknownFields() for more on unknown fields. + virtual void DiscardUnknownFields(); + + // Debugging ------------------------------------------------------- + + // Generates a human readable form of this message, useful for debugging + // and other purposes. + string DebugString() const; + // Like DebugString(), but with less whitespace. + string ShortDebugString() const; + // Convenience function useful in GDB. Prints DebugString() to stdout. + void PrintDebugString() const; + + // Parsing --------------------------------------------------------- + // Methods for parsing in protocol buffer format. Most of these are + // just simple wrappers around MergeFromCodedStream(). + + // Fill the message with a protocol buffer parsed from the given input + // stream. Returns false on a read error or if the input is in the + // wrong format. + bool ParseFromCodedStream(io::CodedInputStream* input); + // Like ParseFromCodedStream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromCodedStream(io::CodedInputStream* input); + // Read a protocol buffer from the given zero-copy input stream. If + // successful, the entire input will be consumed. + bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input); + // Like ParseFromZeroCopyStream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromZeroCopyStream(io::ZeroCopyInputStream* input); + // Parse a protocol buffer contained in a string. + bool ParseFromString(const string& data); + // Like ParseFromString(), but accepts messages that are missing + // required fields. + bool ParsePartialFromString(const string& data); + // Parse a protocol buffer contained in an array of bytes. + bool ParseFromArray(const void* data, int size); + // Like ParseFromArray(), but accepts messages that are missing + // required fields. + bool ParsePartialFromArray(const void* data, int size); + + // Parse a protocol buffer from a file descriptor. If successful, the entire + // input will be consumed. + bool ParseFromFileDescriptor(int file_descriptor); + // Like ParseFromFileDescriptor(), but accepts messages that are missing + // required fields. + bool ParsePartialFromFileDescriptor(int file_descriptor); + // Parse a protocol buffer from a C++ istream. If successful, the entire + // input will be consumed. + bool ParseFromIstream(istream* input); + // Like ParseFromIstream(), but accepts messages that are missing + // required fields. + bool ParsePartialFromIstream(istream* input); + + + // Reads a protocol buffer from the stream and merges it into this + // Message. Singular fields read from the input overwrite what is + // already in the Message and repeated fields are appended to those + // already present. + // + // It is the responsibility of the caller to call input->LastTagWas() + // (for groups) or input->ConsumedEntireMessage() (for non-groups) after + // this returns to verify that the message's end was delimited correctly. + // + // ParsefromCodedStream() is implemented as Clear() followed by + // MergeFromCodedStream(). + bool MergeFromCodedStream(io::CodedInputStream* input); + + // Like MergeFromCodedStream(), but succeeds even if required fields are + // missing in the input. + // + // MergeFromCodedStream() is just implemented as MergePartialFromCodedStream() + // followed by IsInitialized(). + virtual bool MergePartialFromCodedStream(io::CodedInputStream* input); + + // Serialization --------------------------------------------------- + // Methods for serializing in protocol buffer format. Most of these + // are just simple wrappers around ByteSize() and SerializeWithCachedSizes(). + + // Write a protocol buffer of this message to the given output. Returns + // false on a write error. If the message is missing required fields, + // this may GOOGLE_CHECK-fail. + bool SerializeToCodedStream(io::CodedOutputStream* output) const; + // Like SerializeToCodedStream(), but allows missing required fields. + bool SerializePartialToCodedStream(io::CodedOutputStream* output) const; + // Write the message to the given zero-copy output stream. All required + // fields must be set. + bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const; + // Like SerializeToZeroCopyStream(), but allows missing required fields. + bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const; + // Serialize the message and store it in the given string. All required + // fields must be set. + bool SerializeToString(string* output) const; + // Like SerializeToString(), but allows missing required fields. + bool SerializePartialToString(string* output) const; + // Serialize the message and store it in the given byte array. All required + // fields must be set. + bool SerializeToArray(void* data, int size) const; + // Like SerializeToArray(), but allows missing required fields. + bool SerializePartialToArray(void* data, int size) const; + + // Serialize the message and write it to the given file descriptor. All + // required fields must be set. + bool SerializeToFileDescriptor(int file_descriptor) const; + // Like SerializeToFileDescriptor(), but allows missing required fields. + bool SerializePartialToFileDescriptor(int file_descriptor) const; + // Serialize the message and write it to the given C++ ostream. All + // required fields must be set. + bool SerializeToOstream(ostream* output) const; + // Like SerializeToOstream(), but allows missing required fields. + bool SerializePartialToOstream(ostream* output) const; + + + // Like SerializeToString(), but appends to the data to the string's existing + // contents. All required fields must be set. + bool AppendToString(string* output) const; + // Like AppendToString(), but allows missing required fields. + bool AppendPartialToString(string* output) const; + + // Computes the serialized size of the message. This recursively calls + // ByteSize() on all embedded messages. If a subclass does not override + // this, it MUST override SetCachedSize(). + virtual int ByteSize() const; + + // Serializes the message without recomputing the size. The message must + // not have changed since the last call to ByteSize(); if it has, the results + // are undefined. + virtual bool SerializeWithCachedSizes(io::CodedOutputStream* output) const; + + // Returns the result of the last call to ByteSize(). An embedded message's + // size is needed both to serialize it (because embedded messages are + // length-delimited) and to compute the outer message's size. Caching + // the size avoids computing it multiple times. + // + // ByteSize() does not automatically use the cached size when available + // because this would require invalidating it every time the message was + // modified, which would be too hard and expensive. (E.g. if a deeply-nested + // sub-message is changed, all of its parents' cached sizes would need to be + // invalidated, which is too much work for an otherwise inlined setter + // method.) + virtual int GetCachedSize() const = 0; + + private: + // This is called only by the default implementation of ByteSize(), to + // update the cached size. If you override ByteSize(), you do not need + // to override this. If you do not override ByteSize(), you MUST override + // this; the default implementation will crash. + // + // The method is private because subclasses should never call it; only + // override it. Yes, C++ lets you do that. Crazy, huh? + virtual void SetCachedSize(int size) const; + + public: + + // Introspection --------------------------------------------------- + + // Typedef for backwards-compatibility. + typedef google::protobuf::Reflection Reflection; + + // Get a Descriptor for this message's type. This describes what + // fields the message contains, the types of those fields, etc. + virtual const Descriptor* GetDescriptor() const = 0; + + // Get the Reflection interface for this Message, which can be used to + // read and modify the fields of the Message dynamically (in other words, + // without knowing the message type at compile time). This object remains + // property of the Message. + virtual const Reflection* GetReflection() const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Message); +}; + +// This interface contains methods that can be used to dynamically access +// and modify the fields of a protocol message. Their semantics are +// similar to the accessors the protocol compiler generates. +// +// To get the Reflection for a given Message, call Message::GetReflection(). +// +// This interface is separate from Message only for efficiency reasons; +// the vast majority of implementations of Message will share the same +// implementation of Reflection (GeneratedMessageReflection, +// defined in generated_message.h), and all Messages of a particular class +// should share the same Reflection object (though you should not rely on +// the latter fact). +// +// There are several ways that these methods can be used incorrectly. For +// example, any of the following conditions will lead to undefined +// results (probably assertion failures): +// - The FieldDescriptor is not a field of this message type. +// - The method called is not appropriate for the field's type. For +// each field type in FieldDescriptor::TYPE_*, there is only one +// Get*() method, one Set*() method, and one Add*() method that is +// valid for that type. It should be obvious which (except maybe +// for TYPE_BYTES, which are represented using strings in C++). +// - A Get*() or Set*() method for singular fields is called on a repeated +// field. +// - GetRepeated*(), SetRepeated*(), or Add*() is called on a non-repeated +// field. +// - The Message object passed to any method is not of the right type for +// this Reflection object (i.e. message.GetReflection() != reflection). +// +// You might wonder why there is not any abstract representation for a field +// of arbitrary type. E.g., why isn't there just a "GetField()" method that +// returns "const Field&", where "Field" is some class with accessors like +// "GetInt32Value()". The problem is that someone would have to deal with +// allocating these Field objects. For generated message classes, having to +// allocate space for an additional object to wrap every field would at least +// double the message's memory footprint, probably worse. Allocating the +// objects on-demand, on the other hand, would be expensive and prone to +// memory leaks. So, instead we ended up with this flat interface. +// +// WARNING: This class is currently in the process of being converted from +// a per-instance object to a per-class object. You'll notice that there +// are two sets of methods below: ones that take a Message pointer or +// reference as a parameter, and ones that don't. The former ones are the +// new interface; the latter ones will go away soon. +// +// TODO(kenton): Create a utility class which callers can use to read and +// write fields from a Reflection without paying attention to the type. +class LIBPROTOBUF_EXPORT Reflection { + public: + // TODO(kenton): Remove parameter. + inline Reflection() {} + virtual ~Reflection(); + + // Get the UnknownFieldSet for the message. This contains fields which + // were seen when the Message was parsed but were not recognized according + // to the Message's definition. + virtual const UnknownFieldSet& GetUnknownFields( + const Message& message) const = 0; + // Get a mutable pointer to the UnknownFieldSet for the message. This + // contains fields which were seen when the Message was parsed but were not + // recognized according to the Message's definition. + virtual UnknownFieldSet* MutableUnknownFields(Message* message) const = 0; + + // Check if the given non-repeated field is set. + virtual bool HasField(const Message& message, + const FieldDescriptor* field) const = 0; + + // Get the number of elements of a repeated field. + virtual int FieldSize(const Message& message, + const FieldDescriptor* field) const = 0; + + // Clear the value of a field, so that HasField() returns false or + // FieldSize() returns zero. + virtual void ClearField(Message* message, + const FieldDescriptor* field) const = 0; + + // List all fields of the message which are currently set. This includes + // extensions. Singular fields will only be listed if HasField(field) would + // return true and repeated fields will only be listed if FieldSize(field) + // would return non-zero. Fields (both normal fields and extension fields) + // will be listed ordered by field number. + virtual void ListFields(const Message& message, + vector* output) const = 0; + + // Singular field getters ------------------------------------------ + // These get the value of a non-repeated field. They return the default + // value for fields that aren't set. + + virtual int32 GetInt32 (const Message& message, + const FieldDescriptor* field) const = 0; + virtual int64 GetInt64 (const Message& message, + const FieldDescriptor* field) const = 0; + virtual uint32 GetUInt32(const Message& message, + const FieldDescriptor* field) const = 0; + virtual uint64 GetUInt64(const Message& message, + const FieldDescriptor* field) const = 0; + virtual float GetFloat (const Message& message, + const FieldDescriptor* field) const = 0; + virtual double GetDouble(const Message& message, + const FieldDescriptor* field) const = 0; + virtual bool GetBool (const Message& message, + const FieldDescriptor* field) const = 0; + virtual string GetString(const Message& message, + const FieldDescriptor* field) const = 0; + virtual const EnumValueDescriptor* GetEnum( + const Message& message, const FieldDescriptor* field) const = 0; + virtual const Message& GetMessage(const Message& message, + const FieldDescriptor* field) const = 0; + + // Get a string value without copying, if possible. + // + // GetString() necessarily returns a copy of the string. This can be + // inefficient when the string is already stored in a string object in the + // underlying message. GetStringReference() will return a reference to the + // underlying string in this case. Otherwise, it will copy the string into + // *scratch and return that. + // + // Note: It is perfectly reasonable and useful to write code like: + // str = reflection->GetStringReference(field, &str); + // This line would ensure that only one copy of the string is made + // regardless of the field's underlying representation. When initializing + // a newly-constructed string, though, it's just as fast and more readable + // to use code like: + // string str = reflection->GetString(field); + virtual const string& GetStringReference(const Message& message, + const FieldDescriptor* field, + string* scratch) const = 0; + + + // Singular field mutators ----------------------------------------- + // These mutate the value of a non-repeated field. + + virtual void SetInt32 (Message* message, + const FieldDescriptor* field, int32 value) const = 0; + virtual void SetInt64 (Message* message, + const FieldDescriptor* field, int64 value) const = 0; + virtual void SetUInt32(Message* message, + const FieldDescriptor* field, uint32 value) const = 0; + virtual void SetUInt64(Message* message, + const FieldDescriptor* field, uint64 value) const = 0; + virtual void SetFloat (Message* message, + const FieldDescriptor* field, float value) const = 0; + virtual void SetDouble(Message* message, + const FieldDescriptor* field, double value) const = 0; + virtual void SetBool (Message* message, + const FieldDescriptor* field, bool value) const = 0; + virtual void SetString(Message* message, + const FieldDescriptor* field, + const string& value) const = 0; + virtual void SetEnum (Message* message, + const FieldDescriptor* field, + const EnumValueDescriptor* value) const = 0; + // Get a mutable pointer to a field with a message type. + virtual Message* MutableMessage(Message* message, + const FieldDescriptor* field) const = 0; + + + // Repeated field getters ------------------------------------------ + // These get the value of one element of a repeated field. + + virtual int32 GetRepeatedInt32 (const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual int64 GetRepeatedInt64 (const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual uint32 GetRepeatedUInt32(const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual uint64 GetRepeatedUInt64(const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual float GetRepeatedFloat (const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual double GetRepeatedDouble(const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual bool GetRepeatedBool (const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual string GetRepeatedString(const Message& message, + const FieldDescriptor* field, + int index) const = 0; + virtual const EnumValueDescriptor* GetRepeatedEnum( + const Message& message, + const FieldDescriptor* field, int index) const = 0; + virtual const Message& GetRepeatedMessage( + const Message& message, + const FieldDescriptor* field, int index) const = 0; + + // See GetStringReference(), above. + virtual const string& GetRepeatedStringReference( + const Message& message, const FieldDescriptor* field, + int index, string* scratch) const = 0; + + + // Repeated field mutators ----------------------------------------- + // These mutate the value of one element of a repeated field. + + virtual void SetRepeatedInt32 (Message* message, + const FieldDescriptor* field, + int index, int32 value) const = 0; + virtual void SetRepeatedInt64 (Message* message, + const FieldDescriptor* field, + int index, int64 value) const = 0; + virtual void SetRepeatedUInt32(Message* message, + const FieldDescriptor* field, + int index, uint32 value) const = 0; + virtual void SetRepeatedUInt64(Message* message, + const FieldDescriptor* field, + int index, uint64 value) const = 0; + virtual void SetRepeatedFloat (Message* message, + const FieldDescriptor* field, + int index, float value) const = 0; + virtual void SetRepeatedDouble(Message* message, + const FieldDescriptor* field, + int index, double value) const = 0; + virtual void SetRepeatedBool (Message* message, + const FieldDescriptor* field, + int index, bool value) const = 0; + virtual void SetRepeatedString(Message* message, + const FieldDescriptor* field, + int index, const string& value) const = 0; + virtual void SetRepeatedEnum(Message* message, + const FieldDescriptor* field, int index, + const EnumValueDescriptor* value) const = 0; + // Get a mutable pointer to an element of a repeated field with a message + // type. + virtual Message* MutableRepeatedMessage( + Message* message, const FieldDescriptor* field, int index) const = 0; + + + // Repeated field adders ------------------------------------------- + // These add an element to a repeated field. + + virtual void AddInt32 (Message* message, + const FieldDescriptor* field, int32 value) const = 0; + virtual void AddInt64 (Message* message, + const FieldDescriptor* field, int64 value) const = 0; + virtual void AddUInt32(Message* message, + const FieldDescriptor* field, uint32 value) const = 0; + virtual void AddUInt64(Message* message, + const FieldDescriptor* field, uint64 value) const = 0; + virtual void AddFloat (Message* message, + const FieldDescriptor* field, float value) const = 0; + virtual void AddDouble(Message* message, + const FieldDescriptor* field, double value) const = 0; + virtual void AddBool (Message* message, + const FieldDescriptor* field, bool value) const = 0; + virtual void AddString(Message* message, + const FieldDescriptor* field, + const string& value) const = 0; + virtual void AddEnum (Message* message, + const FieldDescriptor* field, + const EnumValueDescriptor* value) const = 0; + virtual Message* AddMessage(Message* message, + const FieldDescriptor* field) const = 0; + + + // Extensions ------------------------------------------------------ + + // Try to find an extension of this message type by fully-qualified field + // name. Returns NULL if no extension is known for this name or number. + virtual const FieldDescriptor* FindKnownExtensionByName( + const string& name) const = 0; + + // Try to find an extension of this message type by field number. + // Returns NULL if no extension is known for this name or number. + virtual const FieldDescriptor* FindKnownExtensionByNumber( + int number) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Reflection); +}; + +// Abstract interface for a factory for message objects. +class LIBPROTOBUF_EXPORT MessageFactory { + public: + inline MessageFactory() {} + virtual ~MessageFactory(); + + // Given a Descriptor, gets or constructs the default (prototype) Message + // of that type. You can then call that message's New() method to construct + // a mutable message of that type. + // + // Calling this method twice with the same Descriptor returns the same + // object. The returned object remains property of the factory. Also, any + // objects created by calling the prototype's New() method share some data + // with the prototype, so these must be destoyed before the MessageFactory + // is destroyed. + // + // The given descriptor must outlive the returned message, and hence must + // outlive the MessageFactory. + // + // Some implementations do not support all types. GetPrototype() will + // return NULL if the descriptor passed in is not supported. + // + // This method may or may not be thread-safe depending on the implementation. + // Each implementation should document its own degree thread-safety. + virtual const Message* GetPrototype(const Descriptor* type) = 0; + + // Gets a MessageFactory which supports all generated, compiled-in messages. + // In other words, for any compiled-in type FooMessage, the following is true: + // MessageFactory::generated_factory()->GetPrototype( + // FooMessage::descriptor()) == FooMessage::default_instance() + // This factory supports all types which are found in + // DescriptorPool::generated_pool(). If given a descriptor from any other + // pool, GetPrototype() will return NULL. (You can also check if a + // descriptor is for a generated message by checking if + // descriptor->file()->pool() == DescriptorPool::generated_pool().) + // + // This factory is 100% thread-safe; calling GetPrototype() does not modify + // any shared data. + // + // This factory is a singleton. The caller must not delete the object. + static MessageFactory* generated_factory(); + + // For internal use only: Registers a message type at static initialization + // time, to be placed in generated_factory(). + static void InternalRegisterGeneratedMessage(const Descriptor* descriptor, + const Message* prototype); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_MESSAGE_H__ diff --git a/src/google/protobuf/message_unittest.cc b/src/google/protobuf/message_unittest.cc new file mode 100644 index 00000000..491d3799 --- /dev/null +++ b/src/google/protobuf/message_unittest.cc @@ -0,0 +1,224 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +#include +#include +#include +#ifdef _MSC_VER +#include +#else +#include +#endif +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace google { +namespace protobuf { + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +TEST(MessageTest, SerializeHelpers) { + // TODO(kenton): Test more helpers? They're all two-liners so it seems + // like a waste of time. + + protobuf_unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + stringstream stream; + + string str1("foo"); + string str2("bar"); + + message.SerializeToString(&str1); + message.AppendToString(&str2); + message.SerializeToOstream(&stream); + + EXPECT_EQ(str1.size() + 3, str2.size()); + EXPECT_EQ("bar", str2.substr(0, 3)); + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(str2.substr(3) == str1); + + // GCC gives some sort of error if we try to just do stream.str() == str1. + string temp = stream.str(); + EXPECT_TRUE(temp == str1); + +} + +TEST(MessageTest, ParseFromFileDescriptor) { + string filename = TestSourceDir() + + "/google/protobuf/testdata/golden_message"; + int file = open(filename.c_str(), O_RDONLY | O_BINARY); + + unittest::TestAllTypes message; + EXPECT_TRUE(message.ParseFromFileDescriptor(file)); + TestUtil::ExpectAllFieldsSet(message); + + EXPECT_GE(close(file), 0); +} + +TEST(MessageTest, ParseHelpers) { + // TODO(kenton): Test more helpers? They're all two-liners so it seems + // like a waste of time. + string data; + + { + // Set up. + protobuf_unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + message.SerializeToString(&data); + } + + { + // Test ParseFromString. + protobuf_unittest::TestAllTypes message; + EXPECT_TRUE(message.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(message); + } + + { + // Test ParseFromIstream. + protobuf_unittest::TestAllTypes message; + stringstream stream(data); + EXPECT_TRUE(message.ParseFromIstream(&stream)); + EXPECT_TRUE(stream.eof()); + TestUtil::ExpectAllFieldsSet(message); + } +} + +TEST(MessageTest, ParseFailsIfNotInitialized) { + unittest::TestRequired message; + vector errors; + + { + ScopedMemoryLog log; + EXPECT_FALSE(message.ParseFromString("")); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_EQ("Can't parse message of type \"protobuf_unittest.TestRequired\" " + "because it is missing required fields: a, b, c", + errors[0]); +} + +TEST(MessageTest, BypassInitializationCheckOnParse) { + unittest::TestRequired message; + io::ArrayInputStream raw_input(NULL, 0); + io::CodedInputStream input(&raw_input); + EXPECT_TRUE(message.MergePartialFromCodedStream(&input)); +} + +TEST(MessageTest, InitializationErrorString) { + unittest::TestRequired message; + EXPECT_EQ("a, b, c", message.InitializationErrorString()); +} + +#ifdef GTEST_HAS_DEATH_TEST // death tests do not work on Windows yet. + +TEST(MessageTest, SerializeFailsIfNotInitialized) { + unittest::TestRequired message; + string data; + EXPECT_DEBUG_DEATH(EXPECT_TRUE(message.SerializeToString(&data)), + "Can't serialize message of type \"protobuf_unittest.TestRequired\" because " + "it is missing required fields: a, b, c"); +} + +TEST(MessageTest, CheckInitialized) { + unittest::TestRequired message; + EXPECT_DEATH(message.CheckInitialized(), + "Message of type \"protobuf_unittest.TestRequired\" is missing required " + "fields: a, b, c"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(MessageTest, BypassInitializationCheckOnSerialize) { + unittest::TestRequired message; + io::ArrayOutputStream raw_output(NULL, 0); + io::CodedOutputStream output(&raw_output); + EXPECT_TRUE(message.SerializePartialToCodedStream(&output)); +} + +TEST(MessageTest, FindInitializationErrors) { + unittest::TestRequired message; + vector errors; + message.FindInitializationErrors(&errors); + ASSERT_EQ(3, errors.size()); + EXPECT_EQ("a", errors[0]); + EXPECT_EQ("b", errors[1]); + EXPECT_EQ("c", errors[2]); +} + +TEST(MessageTest, ParseFailsOnInvalidMessageEnd) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromArray("", 0)); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromArray("\0", 1)); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromArray("\200", 1)); + + // The byte is an endgroup tag, but we aren't parsing a group. + EXPECT_FALSE(message.ParseFromArray("\014", 1)); +} + +TEST(MessageFactoryTest, GeneratedFactoryLookup) { + EXPECT_EQ( + MessageFactory::generated_factory()->GetPrototype( + protobuf_unittest::TestAllTypes::descriptor()), + &protobuf_unittest::TestAllTypes::default_instance()); +} + +TEST(MessageFactoryTest, GeneratedFactoryUnknownType) { + // Construct a new descriptor. + DescriptorPool pool; + FileDescriptorProto file; + file.set_name("foo.proto"); + file.add_message_type()->set_name("Foo"); + const Descriptor* descriptor = pool.BuildFile(file)->message_type(0); + + // Trying to construct it should return NULL. + EXPECT_TRUE( + MessageFactory::generated_factory()->GetPrototype(descriptor) == NULL); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/package_info.h b/src/google/protobuf/package_info.h new file mode 100644 index 00000000..0ba6e791 --- /dev/null +++ b/src/google/protobuf/package_info.h @@ -0,0 +1,50 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file exists solely to document the google::protobuf namespace. +// It is not compiled into anything, but it may be read by an automated +// documentation generator. + +namespace google { + +// Core components of the Protocol Buffers runtime library. +// +// The files in this package represent the core of the Protocol Buffer +// system. All of them are part of the libprotobuf library. +// +// A note on thread-safety: +// +// Thread-safety in the Protocol Buffer library follows a simple rule: +// unless explicitly noted otherwise, it is always safe to use an object +// from multiple threads simultaneously as long as the object is declared +// const in all threads (or, it is only used in ways that would be allowed +// if it were declared const). However, if an object is accessed in one +// thread in a way that would not be allowed if it were const, then it is +// not safe to access that object in any other thread simultaneously. +// +// Put simply, read-only access to an object can happen in multiple threads +// simultaneously, but write access can only happen in a single thread at +// a time. +// +// The implementation does contain some "const" methods which actually modify +// the object behind the scenes -- e.g., to cache results -- but in these cases +// mutex locking is used to make the access thread-safe. +namespace protobuf {} +} // namespace google diff --git a/src/google/protobuf/reflection_ops.cc b/src/google/protobuf/reflection_ops.cc new file mode 100644 index 00000000..9e12658d --- /dev/null +++ b/src/google/protobuf/reflection_ops.cc @@ -0,0 +1,248 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +void ReflectionOps::Copy(const Message& from, Message* to) { + if (&from == to) return; + Clear(to); + Merge(from, to); +} + +void ReflectionOps::Merge(const Message& from, Message* to) { + GOOGLE_CHECK_NE(&from, to); + + const Descriptor* descriptor = from.GetDescriptor(); + GOOGLE_CHECK_EQ(to->GetDescriptor(), descriptor) + << "Tried to merge messages of different types."; + + const Reflection* from_reflection = from.GetReflection(); + const Reflection* to_reflection = to->GetReflection(); + + vector fields; + from_reflection->ListFields(from, &fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + + if (field->is_repeated()) { + int count = from_reflection->FieldSize(from, field); + for (int j = 0; j < count; j++) { + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, METHOD) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + to_reflection->Add##METHOD(to, field, \ + from_reflection->GetRepeated##METHOD(from, field, j)); \ + break; + + HANDLE_TYPE(INT32 , Int32 ); + HANDLE_TYPE(INT64 , Int64 ); + HANDLE_TYPE(UINT32, UInt32); + HANDLE_TYPE(UINT64, UInt64); + HANDLE_TYPE(FLOAT , Float ); + HANDLE_TYPE(DOUBLE, Double); + HANDLE_TYPE(BOOL , Bool ); + HANDLE_TYPE(STRING, String); + HANDLE_TYPE(ENUM , Enum ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + to_reflection->AddMessage(to, field)->MergeFrom( + from_reflection->GetRepeatedMessage(from, field, j)); + break; + } + } + } else { + switch (field->cpp_type()) { +#define HANDLE_TYPE(CPPTYPE, METHOD) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + to_reflection->Set##METHOD(to, field, \ + from_reflection->Get##METHOD(from, field)); \ + break; + + HANDLE_TYPE(INT32 , Int32 ); + HANDLE_TYPE(INT64 , Int64 ); + HANDLE_TYPE(UINT32, UInt32); + HANDLE_TYPE(UINT64, UInt64); + HANDLE_TYPE(FLOAT , Float ); + HANDLE_TYPE(DOUBLE, Double); + HANDLE_TYPE(BOOL , Bool ); + HANDLE_TYPE(STRING, String); + HANDLE_TYPE(ENUM , Enum ); +#undef HANDLE_TYPE + + case FieldDescriptor::CPPTYPE_MESSAGE: + to_reflection->MutableMessage(to, field)->MergeFrom( + from_reflection->GetMessage(from, field)); + break; + } + } + } + + to_reflection->MutableUnknownFields(to)->MergeFrom( + from_reflection->GetUnknownFields(from)); +} + +void ReflectionOps::Clear(Message* message) { + const Reflection* reflection = message->GetReflection(); + + vector fields; + reflection->ListFields(*message, &fields); + for (int i = 0; i < fields.size(); i++) { + reflection->ClearField(message, fields[i]); + } + + reflection->MutableUnknownFields(message)->Clear(); +} + +bool ReflectionOps::IsInitialized(const Message& message) { + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* reflection = message.GetReflection(); + + // Check required fields of this message. + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + if (!reflection->HasField(message, descriptor->field(i))) { + return false; + } + } + } + + // Check that sub-messages are initialized. + vector fields; + reflection->ListFields(message, &fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->is_repeated()) { + int size = reflection->FieldSize(message, field); + + for (int i = 0; i < size; i++) { + if (!reflection->GetRepeatedMessage(message, field, i) + .IsInitialized()) { + return false; + } + } + } else { + if (!reflection->GetMessage(message, field).IsInitialized()) { + return false; + } + } + } + } + + return true; +} + +void ReflectionOps::DiscardUnknownFields(Message* message) { + const Reflection* reflection = message->GetReflection(); + + reflection->MutableUnknownFields(message)->Clear(); + + vector fields; + reflection->ListFields(*message, &fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + if (field->is_repeated()) { + int size = reflection->FieldSize(*message, field); + for (int i = 0; i < size; i++) { + reflection->MutableRepeatedMessage(message, field, i) + ->DiscardUnknownFields(); + } + } else { + reflection->MutableMessage(message, field)->DiscardUnknownFields(); + } + } + } +} + +static string SubMessagePrefix(const string& prefix, + const FieldDescriptor* field, + int index) { + string result(prefix); + if (field->is_extension()) { + result.append("("); + result.append(field->full_name()); + result.append(")"); + } else { + result.append(field->name()); + } + if (index != -1) { + result.append("["); + result.append(SimpleItoa(index)); + result.append("]"); + } + result.append("."); + return result; +} + +void ReflectionOps::FindInitializationErrors( + const Message& message, + const string& prefix, + vector* errors) { + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* reflection = message.GetReflection(); + + // Check required fields of this message. + for (int i = 0; i < descriptor->field_count(); i++) { + if (descriptor->field(i)->is_required()) { + if (!reflection->HasField(message, descriptor->field(i))) { + errors->push_back(prefix + descriptor->field(i)->name()); + } + } + } + + // Check sub-messages. + vector fields; + reflection->ListFields(message, &fields); + for (int i = 0; i < fields.size(); i++) { + const FieldDescriptor* field = fields[i]; + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + + if (field->is_repeated()) { + int size = reflection->FieldSize(message, field); + + for (int i = 0; i < size; i++) { + const Message& sub_message = + reflection->GetRepeatedMessage(message, field, i); + FindInitializationErrors(sub_message, + SubMessagePrefix(prefix, field, i), + errors); + } + } else { + const Message& sub_message = reflection->GetMessage(message, field); + FindInitializationErrors(sub_message, + SubMessagePrefix(prefix, field, -1), + errors); + } + } + } +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/reflection_ops.h b/src/google/protobuf/reflection_ops.h new file mode 100644 index 00000000..b6922a63 --- /dev/null +++ b/src/google/protobuf/reflection_ops.h @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_REFLECTION_OPS_H__ +#define GOOGLE_PROTOBUF_REFLECTION_OPS_H__ + +#include + +namespace google { +namespace protobuf { +namespace internal { + +// Basic operations that can be performed using reflection. +// These can be used as a cheap way to implement the corresponding +// methods of the Message interface, though they are likely to be +// slower than implementations tailored for the specific message type. +// +// This class should stay limited to operations needed to implement +// the Message interface. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT ReflectionOps { + public: + static void Copy(const Message& from, Message* to); + static void Merge(const Message& from, Message* to); + static void Clear(Message* message); + static bool IsInitialized(const Message& message); + static void DiscardUnknownFields(Message* message); + + // Finds all unset required fields in the message and adds their full + // paths (e.g. "foo.bar[5].baz") to *names. "prefix" will be attached to + // the front of each name. + static void FindInitializationErrors(const Message& message, + const string& prefix, + vector* errors); + + private: + // All methods are static. No need to construct. + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionOps); +}; + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_REFLECTION_OPS_H__ diff --git a/src/google/protobuf/reflection_ops_unittest.cc b/src/google/protobuf/reflection_ops_unittest.cc new file mode 100644 index 00000000..ab587331 --- /dev/null +++ b/src/google/protobuf/reflection_ops_unittest.cc @@ -0,0 +1,395 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +TEST(ReflectionOpsTest, SanityCheck) { + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + TestUtil::ExpectAllFieldsSet(message); +} + +TEST(ReflectionOpsTest, Copy) { + unittest::TestAllTypes message, message2; + + TestUtil::SetAllFields(&message); + + ReflectionOps::Copy(message, &message2); + + TestUtil::ExpectAllFieldsSet(message2); + + // Copying from self should be a no-op. + ReflectionOps::Copy(message2, &message2); + TestUtil::ExpectAllFieldsSet(message2); +} + +TEST(ReflectionOpsTest, CopyExtensions) { + unittest::TestAllExtensions message, message2; + + TestUtil::SetAllExtensions(&message); + + ReflectionOps::Copy(message, &message2); + + TestUtil::ExpectAllExtensionsSet(message2); +} + +TEST(ReflectionOpsTest, Merge) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllTypes message, message2; + + TestUtil::SetAllFields(&message); + + // This field will test merging into an empty spot. + message2.set_optional_int32(message.optional_int32()); + message.clear_optional_int32(); + + // This tests overwriting. + message2.set_optional_string(message.optional_string()); + message.set_optional_string("something else"); + + // This tests concatenating. + message2.add_repeated_int32(message.repeated_int32(1)); + int32 i = message.repeated_int32(0); + message.clear_repeated_int32(); + message.add_repeated_int32(i); + + ReflectionOps::Merge(message2, &message); + + TestUtil::ExpectAllFieldsSet(message); +} + +TEST(ReflectionOpsTest, MergeExtensions) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllExtensions message, message2; + + TestUtil::SetAllExtensions(&message); + + // This field will test merging into an empty spot. + message2.SetExtension(unittest::optional_int32_extension, + message.GetExtension(unittest::optional_int32_extension)); + message.ClearExtension(unittest::optional_int32_extension); + + // This tests overwriting. + message2.SetExtension(unittest::optional_string_extension, + message.GetExtension(unittest::optional_string_extension)); + message.SetExtension(unittest::optional_string_extension, "something else"); + + // This tests concatenating. + message2.AddExtension(unittest::repeated_int32_extension, + message.GetExtension(unittest::repeated_int32_extension, 1)); + int32 i = message.GetExtension(unittest::repeated_int32_extension, 0); + message.ClearExtension(unittest::repeated_int32_extension); + message.AddExtension(unittest::repeated_int32_extension, i); + + ReflectionOps::Merge(message2, &message); + + TestUtil::ExpectAllExtensionsSet(message); +} + +TEST(ReflectionOpsTest, MergeUnknown) { + // Test that the messages' UnknownFieldSets are correctly merged. + unittest::TestEmptyMessage message1, message2; + message1.mutable_unknown_fields()->AddField(1234)->add_varint(1); + message2.mutable_unknown_fields()->AddField(1234)->add_varint(2); + + ReflectionOps::Merge(message2, &message1); + + ASSERT_EQ(1, message1.unknown_fields().field_count()); + const UnknownField& field = message1.unknown_fields().field(0); + ASSERT_EQ(2, field.varint_size()); + EXPECT_EQ(1, field.varint(0)); + EXPECT_EQ(2, field.varint(1)); +} + +#ifdef GTEST_HAS_DEATH_TEST + +TEST(ReflectionOpsTest, MergeFromSelf) { + // Note: Copy is implemented in terms of Merge() so technically the Copy + // test already tested most of this. + + unittest::TestAllTypes message; + + EXPECT_DEATH( + ReflectionOps::Merge(message, &message), + "&from"); +} + +#endif // GTEST_HAS_DEATH_TEST + +TEST(ReflectionOpsTest, Clear) { + unittest::TestAllTypes message; + + TestUtil::SetAllFields(&message); + + ReflectionOps::Clear(&message); + + TestUtil::ExpectClear(message); + + // Check that getting embedded messages returns the objects created during + // SetAllFields() rather than default instances. + EXPECT_NE(&unittest::TestAllTypes::OptionalGroup::default_instance(), + &message.optionalgroup()); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.optional_nested_message()); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.optional_foreign_message()); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.optional_import_message()); +} + +TEST(ReflectionOpsTest, ClearExtensions) { + unittest::TestAllExtensions message; + + TestUtil::SetAllExtensions(&message); + + ReflectionOps::Clear(&message); + + TestUtil::ExpectExtensionsClear(message); + + // Check that getting embedded messages returns the objects created during + // SetAllExtensions() rather than default instances. + EXPECT_NE(&unittest::OptionalGroup_extension::default_instance(), + &message.GetExtension(unittest::optionalgroup_extension)); + EXPECT_NE(&unittest::TestAllTypes::NestedMessage::default_instance(), + &message.GetExtension(unittest::optional_nested_message_extension)); + EXPECT_NE(&unittest::ForeignMessage::default_instance(), + &message.GetExtension( + unittest::optional_foreign_message_extension)); + EXPECT_NE(&unittest_import::ImportMessage::default_instance(), + &message.GetExtension(unittest::optional_import_message_extension)); +} + +TEST(ReflectionOpsTest, ClearUnknown) { + // Test that the message's UnknownFieldSet is correctly cleared. + unittest::TestEmptyMessage message; + message.mutable_unknown_fields()->AddField(1234)->add_varint(1); + + ReflectionOps::Clear(&message); + + EXPECT_EQ(0, message.unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, DiscardUnknownFields) { + unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + + // Set some unknown fields in message. + message.mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.mutable_optional_nested_message() + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.mutable_repeated_nested_message(0) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + + EXPECT_EQ(1, message.unknown_fields().field_count()); + EXPECT_EQ(1, message.optional_nested_message() + .unknown_fields().field_count()); + EXPECT_EQ(1, message.repeated_nested_message(0) + .unknown_fields().field_count()); + + // Discard them. + ReflectionOps::DiscardUnknownFields(&message); + TestUtil::ExpectAllFieldsSet(message); + + EXPECT_EQ(0, message.unknown_fields().field_count()); + EXPECT_EQ(0, message.optional_nested_message() + .unknown_fields().field_count()); + EXPECT_EQ(0, message.repeated_nested_message(0) + .unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, DiscardUnknownExtensions) { + unittest::TestAllExtensions message; + TestUtil::SetAllExtensions(&message); + + // Set some unknown fields. + message.mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.MutableExtension(unittest::optional_nested_message_extension) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + message.MutableExtension(unittest::repeated_nested_message_extension, 0) + ->mutable_unknown_fields() + ->AddField(123456) + ->add_varint(654321); + + EXPECT_EQ(1, message.unknown_fields().field_count()); + EXPECT_EQ(1, + message.GetExtension(unittest::optional_nested_message_extension) + .unknown_fields().field_count()); + EXPECT_EQ(1, + message.GetExtension(unittest::repeated_nested_message_extension, 0) + .unknown_fields().field_count()); + + // Discard them. + ReflectionOps::DiscardUnknownFields(&message); + TestUtil::ExpectAllExtensionsSet(message); + + EXPECT_EQ(0, message.unknown_fields().field_count()); + EXPECT_EQ(0, + message.GetExtension(unittest::optional_nested_message_extension) + .unknown_fields().field_count()); + EXPECT_EQ(0, + message.GetExtension(unittest::repeated_nested_message_extension, 0) + .unknown_fields().field_count()); +} + +TEST(ReflectionOpsTest, IsInitialized) { + unittest::TestRequired message; + + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + message.set_a(1); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + message.set_b(2); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + message.set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); +} + +TEST(ReflectionOpsTest, ForeignIsInitialized) { + unittest::TestRequiredForeign message; + + // Starts out initialized because the foreign message is itself an optional + // field. + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); + + // Once we create that field, the message is no longer initialized. + message.mutable_optional_message(); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + + // Initialize it. Now we're initialized. + message.mutable_optional_message()->set_a(1); + message.mutable_optional_message()->set_b(2); + message.mutable_optional_message()->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); + + // Add a repeated version of the message. No longer initialized. + unittest::TestRequired* sub_message = message.add_repeated_message(); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + + // Initialize that repeated version. + sub_message->set_a(1); + sub_message->set_b(2); + sub_message->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); +} + +TEST(ReflectionOpsTest, ExtensionIsInitialized) { + unittest::TestAllExtensions message; + + // Starts out initialized because the foreign message is itself an optional + // field. + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); + + // Once we create that field, the message is no longer initialized. + message.MutableExtension(unittest::TestRequired::single); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + + // Initialize it. Now we're initialized. + message.MutableExtension(unittest::TestRequired::single)->set_a(1); + message.MutableExtension(unittest::TestRequired::single)->set_b(2); + message.MutableExtension(unittest::TestRequired::single)->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); + + // Add a repeated version of the message. No longer initialized. + message.AddExtension(unittest::TestRequired::multi); + EXPECT_FALSE(ReflectionOps::IsInitialized(message)); + + // Initialize that repeated version. + message.MutableExtension(unittest::TestRequired::multi, 0)->set_a(1); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_b(2); + message.MutableExtension(unittest::TestRequired::multi, 0)->set_c(3); + EXPECT_TRUE(ReflectionOps::IsInitialized(message)); +} + +static string FindInitializationErrors(const Message& message) { + vector errors; + ReflectionOps::FindInitializationErrors(message, "", &errors); + return JoinStrings(errors, ","); +} + +TEST(ReflectionOpsTest, FindInitializationErrors) { + unittest::TestRequired message; + EXPECT_EQ("a,b,c", FindInitializationErrors(message)); +} + +TEST(ReflectionOpsTest, FindForeignInitializationErrors) { + unittest::TestRequiredForeign message; + message.mutable_optional_message(); + message.add_repeated_message(); + message.add_repeated_message(); + EXPECT_EQ("optional_message.a," + "optional_message.b," + "optional_message.c," + "repeated_message[0].a," + "repeated_message[0].b," + "repeated_message[0].c," + "repeated_message[1].a," + "repeated_message[1].b," + "repeated_message[1].c", + FindInitializationErrors(message)); +} + +TEST(ReflectionOpsTest, FindExtensionInitializationErrors) { + unittest::TestAllExtensions message; + message.MutableExtension(unittest::TestRequired::single); + message.AddExtension(unittest::TestRequired::multi); + message.AddExtension(unittest::TestRequired::multi); + EXPECT_EQ("(protobuf_unittest.TestRequired.single).a," + "(protobuf_unittest.TestRequired.single).b," + "(protobuf_unittest.TestRequired.single).c," + "(protobuf_unittest.TestRequired.multi)[0].a," + "(protobuf_unittest.TestRequired.multi)[0].b," + "(protobuf_unittest.TestRequired.multi)[0].c," + "(protobuf_unittest.TestRequired.multi)[1].a," + "(protobuf_unittest.TestRequired.multi)[1].b," + "(protobuf_unittest.TestRequired.multi)[1].c", + FindInitializationErrors(message)); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc new file mode 100644 index 00000000..53a3c958 --- /dev/null +++ b/src/google/protobuf/repeated_field.cc @@ -0,0 +1,41 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { +namespace internal { + +GenericRepeatedField::~GenericRepeatedField() {} + +} // namespace internal + +template <> +void RepeatedPtrField::Clear() { + for (int i = 0; i < current_size_; i++) { + elements_[i]->clear(); + } + current_size_ = 0; +} + +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h new file mode 100644 index 00000000..3368e8b7 --- /dev/null +++ b/src/google/protobuf/repeated_field.h @@ -0,0 +1,782 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// RepeatedField and RepeatedPtrField are used by generated protocol message +// classes to manipulate repeated fields. These classes are very similar to +// STL's vector, but include a number of optimizations found to be useful +// specifically in the case of Protocol Buffers. RepeatedPtrField is +// particularly different from STL vector as it manages ownership of the +// pointers that it contains. +// +// Typically, clients should not need to access RepeatedField objects directly, +// but should instead use the accessor functions generated automatically by the +// protocol compiler. + +#ifndef GOOGLE_PROTOBUF_REPEATED_FIELD_H__ +#define GOOGLE_PROTOBUF_REPEATED_FIELD_H__ + +#include +#include +#include +#include + + +namespace google { +namespace protobuf { + +namespace internal { + +// DO NOT USE GenericRepeatedField; it should be considered a private detail +// of RepeatedField/RepeatedPtrField that may be removed in the future. +// GeneratedMessageReflection needs to manipulate repeated fields in a +// generic way, so we have them implement this interface. This should ONLY +// be used by GeneratedMessageReflection. This would normally be very bad +// design but GeneratedMessageReflection is a big efficiency hack anyway. +// +// TODO(kenton): Implement something like Jeff's ProtoVoidPtrArray change. +// Then, get rid of GenericRepeatedField. +class LIBPROTOBUF_EXPORT GenericRepeatedField { + public: + inline GenericRepeatedField() {} + virtual ~GenericRepeatedField(); + + private: + // We only want GeneratedMessageReflection to see and use these, so we + // make them private. Yes, it is valid C++ for a subclass to implement + // a virtual method which is private in the superclass. Crazy, huh? + friend class GeneratedMessageReflection; + + virtual const void* GenericGet(int index) const = 0; + virtual void* GenericMutable(int index) = 0; + virtual void* GenericAdd() = 0; + virtual void GenericClear() = 0; + virtual int GenericSize() const = 0; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(GenericRepeatedField); +}; + +} // namespace internal + +// RepeatedField is used to represent repeated fields of a primitive type (in +// other words, everything except strings and nested Messages). Most users will +// not ever use a RepeatedField directly; they will use the get-by-index, +// set-by-index, and add accessors that are generated for all repeated fields. +template +class RepeatedField : public internal::GenericRepeatedField { + public: + RepeatedField(); + ~RepeatedField(); + + int size() const; + + Element Get(int index) const; + Element* Mutable(int index); + void Set(int index, Element value); + void Add(Element value); + // Remove the last element in the array. + // We don't provide a way to remove any element other than the last + // because it invites inefficient use, such as O(n^2) filtering loops + // that should have been O(n). If you want to remove an element other + // than the last, the best way to do it is to re-arrange the elements + // so that the one you want removed is at the end, then call RemoveLast(). + void RemoveLast(); + void Clear(); + void MergeFrom(const RepeatedField& other); + + // Reserve space to expand the field to at least the given size. If the + // array is grown, it will always be at least doubled in size. + void Reserve(int new_size); + + // Gets the underlying array. This pointer is possibly invalidated by + // any add or remove operation. + Element* mutable_data(); + const Element* data() const; + + // Swap entire contents with "other". + void Swap(RepeatedField* other); + + // STL-like iterator support + typedef Element* iterator; + typedef const Element* const_iterator; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + private: // See GenericRepeatedField for why this is private. + // implements GenericRepeatedField --------------------------------- + const void* GenericGet(int index) const; + void* GenericMutable(int index); + void* GenericAdd(); + void GenericClear(); + int GenericSize() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedField); + + static const int kInitialSize = 4; + + Element* elements_; + int current_size_; + int total_size_; + + Element initial_space_[kInitialSize]; +}; + +namespace internal { +template class RepeatedPtrIterator; +} // namespace internal + +// RepeatedPtrField is like RepeatedField, but used for repeated strings or +// Messages. +template +class RepeatedPtrField : public internal::GenericRepeatedField { + public: + RepeatedPtrField(); + + // This constructor is only defined for RepeatedPtrField. + // When a RepeatedPtrField is created using this constructor, + // prototype->New() will be called to allocate new elements, rather than + // just using the "new" operator. This is useful for the implementation + // of DynamicMessage, but is not used by normal generated messages. + explicit RepeatedPtrField(const Message* prototype); + + ~RepeatedPtrField(); + + // Returns the prototype if one was passed to the constructor. + const Message* prototype() const; + + int size() const; + + const Element& Get(int index) const; + Element* Mutable(int index); + Element* Add(); + void RemoveLast(); // Remove the last element in the array. + void Clear(); + void MergeFrom(const RepeatedPtrField& other); + + // Reserve space to expand the field to at least the given size. This only + // resizes the pointer array; it doesn't allocate any objects. If the + // array is grown, it will always be at least doubled in size. + void Reserve(int new_size); + + // Gets the underlying array. This pointer is possibly invalidated by + // any add or remove operation. + Element** mutable_data(); + const Element* const* data() const; + + // Swap entire contents with "other". + void Swap(RepeatedPtrField* other); + + // STL-like iterator support + typedef internal::RepeatedPtrIterator iterator; + typedef internal::RepeatedPtrIterator const_iterator; + + iterator begin(); + const_iterator begin() const; + iterator end(); + const_iterator end() const; + + // Advanced memory management -------------------------------------- + // When hardcore memory management becomes necessary -- as it often + // does here at Google -- the following methods may be useful. + + // Add an already-allocated object, passing ownership to the + // RepeatedPtrField. + void AddAllocated(Element* value); + // Remove the last element and return it, passing ownership to the + // caller. + // Requires: size() > 0 + Element* ReleaseLast(); + + // When elements are removed by calls to RemoveLast() or Clear(), they + // are not actually freed. Instead, they are cleared and kept so that + // they can be reused later. This can save lots of CPU time when + // repeatedly reusing a protocol message for similar purposes. + // + // Really, extremely hardcore programs may actually want to manipulate + // these objects to better-optimize memory management. These methods + // allow that. + + // Get the number of cleared objects that are currently being kept + // around for reuse. + int ClearedCount(); + // Add an element to the pool of cleared objects, passing ownership to + // the RepeatedPtrField. The element must be cleared prior to calling + // this method. + void AddCleared(Element* value); + // Remove a single element from the cleared pool and return it, passing + // ownership to the caller. The element is guaranteed to be cleared. + // Requires: ClearedCount() > 0 + Element* ReleaseCleared(); + + private: // See GenericRepeatedField for why this is private. + // implements GenericRepeatedField --------------------------------- + const void* GenericGet(int index) const; + void* GenericMutable(int index); + void* GenericAdd(); + void GenericClear(); + int GenericSize() const; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RepeatedPtrField); + + static const int kInitialSize = 4; + + // prototype_ is used for RepeatedPtrField only (see constructor). + const Message* prototype_; + + Element** elements_; + int current_size_; + int allocated_size_; + int total_size_; + + Element* initial_space_[kInitialSize]; + + Element* NewElement(); +}; + +// implementation ==================================================== + +template +inline RepeatedField::RepeatedField() + : elements_(initial_space_), + current_size_(0), + total_size_(kInitialSize) { +} + +template +RepeatedField::~RepeatedField() { + if (elements_ != initial_space_) { + delete [] elements_; + } +} + +template +inline int RepeatedField::size() const { + return current_size_; +} + + +template +inline Element RepeatedField::Get(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return elements_[index]; +} + +template +inline Element* RepeatedField::Mutable(int index) { + GOOGLE_DCHECK_LT(index, size()); + return elements_ + index; +} + +template +inline void RepeatedField::Set(int index, Element value) { + GOOGLE_DCHECK_LT(index, size()); + elements_[index] = value; +} + +template +inline void RepeatedField::Add(Element value) { + if (current_size_ == total_size_) Reserve(total_size_ + 1); + elements_[current_size_++] = value; +} + +template +inline void RepeatedField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + --current_size_; +} + +template +inline void RepeatedField::Clear() { + current_size_ = 0; +} + +template +void RepeatedField::MergeFrom(const RepeatedField& other) { + Reserve(current_size_ + other.current_size_); + memcpy(elements_ + current_size_, other.elements_, + sizeof(Element) * other.current_size_); + current_size_ += other.current_size_; +} + +template +inline Element* RepeatedField::mutable_data() { + return elements_; +} + +template +inline const Element* RepeatedField::data() const { + return elements_; +} + + +template +void RepeatedField::Swap(RepeatedField* other) { + Element* swap_elements = elements_; + int swap_current_size = current_size_; + int swap_total_size = total_size_; + // We may not be using initial_space_ but it's not worth checking. Just + // copy it anyway. + Element swap_initial_space[kInitialSize]; + memcpy(swap_initial_space, initial_space_, sizeof(initial_space_)); + + elements_ = other->elements_; + current_size_ = other->current_size_; + total_size_ = other->total_size_; + memcpy(initial_space_, other->initial_space_, sizeof(initial_space_)); + + other->elements_ = swap_elements; + other->current_size_ = swap_current_size; + other->total_size_ = swap_total_size; + memcpy(other->initial_space_, swap_initial_space, sizeof(swap_initial_space)); + + if (elements_ == other->initial_space_) { + elements_ = initial_space_; + } + if (other->elements_ == initial_space_) { + other->elements_ = other->initial_space_; + } +} + +template +inline typename RepeatedField::iterator +RepeatedField::begin() { + return elements_; +} +template +inline typename RepeatedField::const_iterator +RepeatedField::begin() const { + return elements_; +} +template +inline typename RepeatedField::iterator +RepeatedField::end() { + return elements_ + current_size_; +} +template +inline typename RepeatedField::const_iterator +RepeatedField::end() const { + return elements_ + current_size_; +} + + +template +const void* RepeatedField::GenericGet(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return elements_ + index; +} + +template +void* RepeatedField::GenericMutable(int index) { + return Mutable(index); +} + +template +void* RepeatedField::GenericAdd() { + Add(Element()); + return Mutable(current_size_ - 1); +} + +template +void RepeatedField::GenericClear() { + Clear(); +} + +template +int RepeatedField::GenericSize() const { + return size(); +} + +template +inline void RepeatedField::Reserve(int new_size) { + if (total_size_ >= new_size) return; + + Element* old_elements = elements_; + total_size_ = max(total_size_ * 2, new_size); + elements_ = new Element[total_size_]; + memcpy(elements_, old_elements, current_size_ * sizeof(elements_[0])); + if (old_elements != initial_space_) { + delete [] old_elements; + } +} + +// ------------------------------------------------------------------- + +template +inline RepeatedPtrField::RepeatedPtrField() + : prototype_(NULL), + elements_(initial_space_), + current_size_(0), + allocated_size_(0), + total_size_(kInitialSize) { +} + +template <> +inline RepeatedPtrField::RepeatedPtrField(const Message* prototype) + : prototype_(prototype), + elements_(initial_space_), + current_size_(0), + allocated_size_(0), + total_size_(kInitialSize) { +} + +template +RepeatedPtrField::~RepeatedPtrField() { + for (int i = 0; i < allocated_size_; i++) { + delete elements_[i]; + } + if (elements_ != initial_space_) { + delete [] elements_; + } +} + +template <> +inline const Message* RepeatedPtrField::prototype() const { + return prototype_; +} + + +template +inline int RepeatedPtrField::size() const { + return current_size_; +} + + +template +inline const Element& RepeatedPtrField::Get(int index) const { + GOOGLE_DCHECK_LT(index, size()); + return *elements_[index]; +} + +template +inline Element* RepeatedPtrField::Mutable(int index) { + GOOGLE_DCHECK_LT(index, size()); + return elements_[index]; +} + +template +inline Element* RepeatedPtrField::Add() { + if (current_size_ < allocated_size_) return elements_[current_size_++]; + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + ++allocated_size_; + return elements_[current_size_++] = NewElement(); +} + +template +inline void RepeatedPtrField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + elements_[--current_size_]->Clear(); +} + +template <> +inline void RepeatedPtrField::RemoveLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + elements_[--current_size_]->clear(); +} + +template +void RepeatedPtrField::Clear() { + for (int i = 0; i < current_size_; i++) { + elements_[i]->Clear(); + } + current_size_ = 0; +} + +// Specialization defined in repeated_field.cc. +template <> +void LIBPROTOBUF_EXPORT RepeatedPtrField::Clear(); + +template +void RepeatedPtrField::MergeFrom(const RepeatedPtrField& other) { + Reserve(current_size_ + other.current_size_); + for (int i = 0; i < other.current_size_; i++) { + Add()->MergeFrom(other.Get(i)); + } +} + +template <> +inline void RepeatedPtrField::MergeFrom(const RepeatedPtrField& other) { + Reserve(current_size_ + other.current_size_); + for (int i = 0; i < other.current_size_; i++) { + Add()->assign(other.Get(i)); + } +} + + +template +inline Element** RepeatedPtrField::mutable_data() { + return elements_; +} + +template +inline const Element* const* RepeatedPtrField::data() const { + return elements_; +} + + +template +void RepeatedPtrField::Swap(RepeatedPtrField* other) { + Element** swap_elements = elements_; + int swap_current_size = current_size_; + int swap_allocated_size = allocated_size_; + int swap_total_size = total_size_; + // We may not be using initial_space_ but it's not worth checking. Just + // copy it anyway. + Element* swap_initial_space[kInitialSize]; + memcpy(swap_initial_space, initial_space_, sizeof(initial_space_)); + + elements_ = other->elements_; + current_size_ = other->current_size_; + allocated_size_ = other->allocated_size_; + total_size_ = other->total_size_; + memcpy(initial_space_, other->initial_space_, sizeof(initial_space_)); + + other->elements_ = swap_elements; + other->current_size_ = swap_current_size; + other->allocated_size_ = swap_allocated_size; + other->total_size_ = swap_total_size; + memcpy(other->initial_space_, swap_initial_space, sizeof(swap_initial_space)); + + if (elements_ == other->initial_space_) { + elements_ = initial_space_; + } + if (other->elements_ == initial_space_) { + other->elements_ = other->initial_space_; + } +} + + +template +inline void RepeatedPtrField::AddAllocated(Element* value) { + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + // We don't care about the order of cleared elements, so if there's one + // in the way, just move it to the back of the array. + if (current_size_ < allocated_size_) { + elements_[allocated_size_] = elements_[current_size_]; + } + ++allocated_size_; + elements_[current_size_++] = value; +} + +template +inline Element* RepeatedPtrField::ReleaseLast() { + GOOGLE_DCHECK_GT(current_size_, 0); + Element* result = elements_[--current_size_]; + --allocated_size_; + if (current_size_ < allocated_size_) { + // There are cleared elements on the end; replace the removed element + // with the last allocated element. + elements_[current_size_] = elements_[allocated_size_]; + } + return result; +} + + +template +inline int RepeatedPtrField::ClearedCount() { + return allocated_size_ - current_size_; +} + +template +inline void RepeatedPtrField::AddCleared(Element* value) { + if (allocated_size_ == total_size_) Reserve(total_size_ + 1); + elements_[allocated_size_++] = value; +} + +template +inline Element* RepeatedPtrField::ReleaseCleared() { + GOOGLE_DCHECK_GT(allocated_size_, current_size_); + return elements_[--allocated_size_]; +} + + +template +const void* RepeatedPtrField::GenericGet(int index) const { + return &Get(index); +} + +template +void* RepeatedPtrField::GenericMutable(int index) { + return Mutable(index); +} + +template +void* RepeatedPtrField::GenericAdd() { + return Add(); +} + +template +void RepeatedPtrField::GenericClear() { + Clear(); +} + +template +int RepeatedPtrField::GenericSize() const { + return size(); +} + + +template +inline void RepeatedPtrField::Reserve(int new_size) { + if (total_size_ >= new_size) return; + + Element** old_elements = elements_; + total_size_ = max(total_size_ * 2, new_size); + elements_ = new Element*[total_size_]; + memcpy(elements_, old_elements, allocated_size_ * sizeof(elements_[0])); + if (old_elements != initial_space_) { + delete [] old_elements; + } +} + +template +inline Element* RepeatedPtrField::NewElement() { + return new Element; +} + +// RepeatedPtrField is alowed but requires a prototype since Message +// is abstract. +template <> +inline Message* RepeatedPtrField::NewElement() { + return prototype_->New(); +} + +// ------------------------------------------------------------------- + +namespace internal { + +// STL-like iterator implementation for RepeatedPtrField. You should not +// refer to this class directly; use RepeatedPtrField::iterator instead. +// +// The iterator for RepeatedPtrField, RepeatedPtrIterator, is +// very similar to iterator_ptr<> in util/gtl/iterator_adaptors-inl.h, +// but adds random-access operators and is slightly more specialized +// for using T** as its base type. I didn't re-use the other class to +// avoid an extra dependency. +// +// This code stolen from net/proto/proto-array-internal.h by Jeffrey Yasskin +// (jyasskin@google.com). +template +class RepeatedPtrIterator + : public std::iterator< + std::random_access_iterator_tag, + typename internal::remove_pointer< + typename internal::remove_pointer::type>::type> { + public: + typedef RepeatedPtrIterator iterator; + typedef typename iterator::reference reference; + typedef typename iterator::pointer pointer; + typedef typename iterator::difference_type difference_type; + + RepeatedPtrIterator() : it_(NULL) {} + explicit RepeatedPtrIterator(const It& it) : it_(it) {} + + // Allow "upcasting" from RepeatedPtrIterator to + // RepeatedPtrIterator. + template + RepeatedPtrIterator(const RepeatedPtrIterator& other) + : it_(other.base()) {} + + // Provide access to the wrapped iterator. + const It& base() const { return it_; } + + // dereferenceable + reference operator*() const { return **it_; } + pointer operator->() const { return &(operator*()); } + + // {inc,dec}rementable + iterator& operator++() { ++it_; return *this; } + iterator operator++(int) { return iterator(it_++); } + iterator& operator--() { --it_; return *this; } + iterator operator--(int) { return iterator(it_--); } + + // equality_comparable + bool operator==(const iterator& x) const { return it_ == x.it_; } + bool operator!=(const iterator& x) const { return it_ != x.it_; } + + // less_than_comparable + bool operator<(const iterator& x) const { return it_ < x.it_; } + bool operator<=(const iterator& x) const { return it_ <= x.it_; } + bool operator>(const iterator& x) const { return it_ > x.it_; } + bool operator>=(const iterator& x) const { return it_ >= x.it_; } + + // addable, subtractable + iterator& operator+=(difference_type d) { + it_ += d; + return *this; + } + friend iterator operator+(iterator it, difference_type d) { + it += d; + return it; + } + friend iterator operator+(difference_type d, iterator it) { + it += d; + return it; + } + iterator& operator-=(difference_type d) { + it_ -= d; + return *this; + } + friend iterator operator-(iterator it, difference_type d) { + it -= d; + return it; + } + + // indexable + reference operator[](difference_type d) const { return *(*this + d); } + + // random access iterator + difference_type operator-(const iterator& x) const { return it_ - x.it_; } + + private: + // The internal iterator. + It it_; +}; + +} // namespace internal + +template +inline typename RepeatedPtrField::iterator +RepeatedPtrField::begin() { + return iterator(elements_); +} +template +inline typename RepeatedPtrField::const_iterator +RepeatedPtrField::begin() const { + return iterator(elements_); +} +template +inline typename RepeatedPtrField::iterator +RepeatedPtrField::end() { + return iterator(elements_ + current_size_); +} +template +inline typename RepeatedPtrField::const_iterator +RepeatedPtrField::end() const { + return iterator(elements_ + current_size_); +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_REPEATED_FIELD_H__ diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc new file mode 100644 index 00000000..eb9b096f --- /dev/null +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -0,0 +1,603 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// TODO(kenton): Improve this unittest to bring it up to the standards of +// other proto2 unittests. + +#include + +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +// Test operations on a RepeatedField which is small enough that it does +// not allocate a separate array for storage. +TEST(RepeatedField, Small) { + RepeatedField field; + + EXPECT_EQ(field.size(), 0); + + field.Add(5); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), 5); + + field.Add(42); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), 5); + EXPECT_EQ(field.Get(1), 42); + + field.Set(1, 23); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), 5); + EXPECT_EQ(field.Get(1), 23); + + field.RemoveLast(); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), 5); + + field.Clear(); + + EXPECT_EQ(field.size(), 0); +} + +// Test operations on a RepeatedField which is large enough to allocate a +// separate array. +TEST(RepeatedField, Large) { + RepeatedField field; + + for (int i = 0; i < 16; i++) { + field.Add(i * i); + } + + EXPECT_EQ(field.size(), 16); + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field.Get(i), i * i); + } +} + +// Test swapping between various types of RepeatedFields. +TEST(RepeatedField, SwapSmallSmall) { + RepeatedField field1; + RepeatedField field2; + + field1.Add(5); + field1.Add(42); + + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 0); + EXPECT_EQ(field2.size(), 2); + EXPECT_EQ(field2.Get(0), 5); + EXPECT_EQ(field2.Get(1), 42); +} + +TEST(RepeatedField, SwapLargeSmall) { + RepeatedField field1; + RepeatedField field2; + + for (int i = 0; i < 16; i++) { + field1.Add(i * i); + } + field2.Add(5); + field2.Add(42); + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 2); + EXPECT_EQ(field1.Get(0), 5); + EXPECT_EQ(field1.Get(1), 42); + EXPECT_EQ(field2.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field2.Get(i), i * i); + } +} + +TEST(RepeatedField, SwapLargeLarge) { + RepeatedField field1; + RepeatedField field2; + + field1.Add(5); + field1.Add(42); + for (int i = 0; i < 16; i++) { + field1.Add(i); + field2.Add(i * i); + } + field2.Swap(&field1); + + EXPECT_EQ(field1.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field1.Get(i), i * i); + } + EXPECT_EQ(field2.size(), 18); + EXPECT_EQ(field2.Get(0), 5); + EXPECT_EQ(field2.Get(1), 42); + for (int i = 2; i < 18; i++) { + EXPECT_EQ(field2.Get(i), i - 2); + } +} + +// Determines how much space was reserved by the given field by adding elements +// to it until it re-allocates its space. +static int ReservedSpace(RepeatedField* field) { + const int* ptr = field->data(); + do { + field->Add(0); + } while (field->data() == ptr); + + return field->size() - 1; +} + +TEST(RepeatedField, ReserveMoreThanDouble) { + // Reserve more than double the previous space in the field and expect the + // field to reserve exactly the amount specified. + RepeatedField field; + field.Reserve(20); + + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedField, ReserveLessThanDouble) { + // Reserve less than double the previous space in the field and expect the + // field to grow by double instead. + RepeatedField field; + field.Reserve(20); + field.Reserve(30); + + EXPECT_EQ(40, ReservedSpace(&field)); +} + +TEST(RepeatedField, ReserveLessThanExisting) { + // Reserve less than the previous space in the field and expect the + // field to not re-allocate at all. + RepeatedField field; + field.Reserve(20); + const int* previous_ptr = field.data(); + field.Reserve(10); + + EXPECT_EQ(previous_ptr, field.data()); + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedField, MergeFrom) { + RepeatedField source, destination; + + source.Add(4); + source.Add(5); + + destination.Add(1); + destination.Add(2); + destination.Add(3); + + destination.MergeFrom(source); + + ASSERT_EQ(5, destination.size()); + + EXPECT_EQ(1, destination.Get(0)); + EXPECT_EQ(2, destination.Get(1)); + EXPECT_EQ(3, destination.Get(2)); + EXPECT_EQ(4, destination.Get(3)); + EXPECT_EQ(5, destination.Get(4)); +} + +TEST(RepeatedField, MutableDataIsMutable) { + RepeatedField field; + field.Add(1); + EXPECT_EQ(1, field.Get(0)); + // The fact that this line compiles would be enough, but we'll check the + // value anyway. + *field.mutable_data() = 2; + EXPECT_EQ(2, field.Get(0)); +} + +// =================================================================== +// RepeatedPtrField tests. These pretty much just mirror the RepeatedField +// tests above. + +TEST(RepeatedPtrField, Small) { + RepeatedPtrField field; + + EXPECT_EQ(field.size(), 0); + + field.Add()->assign("foo"); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), "foo"); + + field.Add()->assign("bar"); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), "foo"); + EXPECT_EQ(field.Get(1), "bar"); + + field.Mutable(1)->assign("baz"); + + EXPECT_EQ(field.size(), 2); + EXPECT_EQ(field.Get(0), "foo"); + EXPECT_EQ(field.Get(1), "baz"); + + field.RemoveLast(); + + EXPECT_EQ(field.size(), 1); + EXPECT_EQ(field.Get(0), "foo"); + + field.Clear(); + + EXPECT_EQ(field.size(), 0); +} + +TEST(RepeatedPtrField, Large) { + RepeatedPtrField field; + + for (int i = 0; i < 16; i++) { + *field.Add() += 'a' + i; + } + + EXPECT_EQ(field.size(), 16); + + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field.Get(i).size(), 1); + EXPECT_EQ(field.Get(i)[0], 'a' + i); + } +} + +TEST(RepeatedPtrField, SwapSmallSmall) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field1.Add()->assign("foo"); + field1.Add()->assign("bar"); + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 0); + EXPECT_EQ(field2.size(), 2); + EXPECT_EQ(field2.Get(0), "foo"); + EXPECT_EQ(field2.Get(1), "bar"); +} + +TEST(RepeatedPtrField, SwapLargeSmall) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field2.Add()->assign("foo"); + field2.Add()->assign("bar"); + for (int i = 0; i < 16; i++) { + *field1.Add() += 'a' + i; + } + field1.Swap(&field2); + + EXPECT_EQ(field1.size(), 2); + EXPECT_EQ(field1.Get(0), "foo"); + EXPECT_EQ(field1.Get(1), "bar"); + EXPECT_EQ(field2.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field2.Get(i).size(), 1); + EXPECT_EQ(field2.Get(i)[0], 'a' + i); + } +} + +TEST(RepeatedPtrField, SwapLargeLarge) { + RepeatedPtrField field1; + RepeatedPtrField field2; + + field1.Add()->assign("foo"); + field1.Add()->assign("bar"); + for (int i = 0; i < 16; i++) { + *field1.Add() += 'A' + i; + *field2.Add() += 'a' + i; + } + field2.Swap(&field1); + + EXPECT_EQ(field1.size(), 16); + for (int i = 0; i < 16; i++) { + EXPECT_EQ(field1.Get(i).size(), 1); + EXPECT_EQ(field1.Get(i)[0], 'a' + i); + } + EXPECT_EQ(field2.size(), 18); + EXPECT_EQ(field2.Get(0), "foo"); + EXPECT_EQ(field2.Get(1), "bar"); + for (int i = 2; i < 18; i++) { + EXPECT_EQ(field2.Get(i).size(), 1); + EXPECT_EQ(field2.Get(i)[0], 'A' + i - 2); + } +} + +static int ReservedSpace(RepeatedPtrField* field) { + const string* const* ptr = field->data(); + do { + field->Add(); + } while (field->data() == ptr); + + return field->size() - 1; +} + +TEST(RepeatedPtrField, ReserveMoreThanDouble) { + RepeatedPtrField field; + field.Reserve(20); + + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveLessThanDouble) { + RepeatedPtrField field; + field.Reserve(20); + field.Reserve(30); + + EXPECT_EQ(40, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveLessThanExisting) { + RepeatedPtrField field; + field.Reserve(20); + const string* const* previous_ptr = field.data(); + field.Reserve(10); + + EXPECT_EQ(previous_ptr, field.data()); + EXPECT_EQ(20, ReservedSpace(&field)); +} + +TEST(RepeatedPtrField, ReserveDoesntLoseAllocated) { + // Check that a bug is fixed: An earlier implementation of Reserve() + // failed to copy pointers to allocated-but-cleared objects, possibly + // leading to segfaults. + RepeatedPtrField field; + string* first = field.Add(); + field.RemoveLast(); + + field.Reserve(20); + EXPECT_EQ(first, field.Add()); +} + +// Clearing elements is tricky with RepeatedPtrFields since the memory for +// the elements is retained and reused. +TEST(RepeatedPtrField, ClearedElements) { + RepeatedPtrField field; + + string* original = field.Add(); + *original = "foo"; + + EXPECT_EQ(field.ClearedCount(), 0); + + field.RemoveLast(); + EXPECT_TRUE(original->empty()); + EXPECT_EQ(field.ClearedCount(), 1); + + EXPECT_EQ(field.Add(), original); // Should return same string for reuse. + + EXPECT_EQ(field.ReleaseLast(), original); // We take ownership. + EXPECT_EQ(field.ClearedCount(), 0); + + EXPECT_NE(field.Add(), original); // Should NOT return the same string. + EXPECT_EQ(field.ClearedCount(), 0); + + field.AddAllocated(original); // Give ownership back. + EXPECT_EQ(field.ClearedCount(), 0); + EXPECT_EQ(field.Mutable(1), original); + + field.Clear(); + EXPECT_EQ(field.ClearedCount(), 2); + EXPECT_EQ(field.ReleaseCleared(), original); // Take ownership again. + EXPECT_EQ(field.ClearedCount(), 1); + EXPECT_NE(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); + EXPECT_NE(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); + + field.AddCleared(original); // Give ownership back, but as a cleared object. + EXPECT_EQ(field.ClearedCount(), 1); + EXPECT_EQ(field.Add(), original); + EXPECT_EQ(field.ClearedCount(), 0); +} + +TEST(RepeatedPtrField, MergeFrom) { + RepeatedPtrField source, destination; + + source.Add()->assign("4"); + source.Add()->assign("5"); + + destination.Add()->assign("1"); + destination.Add()->assign("2"); + destination.Add()->assign("3"); + + destination.MergeFrom(source); + + ASSERT_EQ(5, destination.size()); + + EXPECT_EQ("1", destination.Get(0)); + EXPECT_EQ("2", destination.Get(1)); + EXPECT_EQ("3", destination.Get(2)); + EXPECT_EQ("4", destination.Get(3)); + EXPECT_EQ("5", destination.Get(4)); +} + +TEST(RepeatedPtrField, MutableDataIsMutable) { + RepeatedPtrField field; + *field.Add() = "1"; + EXPECT_EQ("1", field.Get(0)); + // The fact that this line compiles would be enough, but we'll check the + // value anyway. + string** data = field.mutable_data(); + **data = "2"; + EXPECT_EQ("2", field.Get(0)); +} + +// =================================================================== + +// Iterator tests stolen from net/proto/proto-array_unittest. +class RepeatedFieldIteratorTest : public testing::Test { + protected: + virtual void SetUp() { + for (int i = 0; i < 3; ++i) { + proto_array_.Add(i); + } + } + + RepeatedField proto_array_; +}; + +TEST_F(RepeatedFieldIteratorTest, Convertible) { + RepeatedField::iterator iter = proto_array_.begin(); + RepeatedField::const_iterator c_iter = iter; + EXPECT_EQ(0, *c_iter); +} + +TEST_F(RepeatedFieldIteratorTest, MutableIteration) { + RepeatedField::iterator iter = proto_array_.begin(); + EXPECT_EQ(0, *iter); + ++iter; + EXPECT_EQ(1, *iter++); + EXPECT_EQ(2, *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + + EXPECT_EQ(2, *(proto_array_.end() - 1)); +} + +TEST_F(RepeatedFieldIteratorTest, ConstIteration) { + const RepeatedField& const_proto_array = proto_array_; + RepeatedField::const_iterator iter = const_proto_array.begin(); + EXPECT_EQ(0, *iter); + ++iter; + EXPECT_EQ(1, *iter++); + EXPECT_EQ(2, *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + EXPECT_EQ(2, *(proto_array_.end() - 1)); +} + +TEST_F(RepeatedFieldIteratorTest, Mutation) { + RepeatedField::iterator iter = proto_array_.begin(); + *iter = 7; + EXPECT_EQ(7, proto_array_.Get(0)); +} + +// ------------------------------------------------------------------- + +class RepeatedPtrFieldIteratorTest : public testing::Test { + protected: + virtual void SetUp() { + proto_array_.Add()->assign("foo"); + proto_array_.Add()->assign("bar"); + proto_array_.Add()->assign("baz"); + } + + RepeatedPtrField proto_array_; +}; + +TEST_F(RepeatedPtrFieldIteratorTest, Convertible) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + RepeatedPtrField::const_iterator c_iter = iter; +} + +TEST_F(RepeatedPtrFieldIteratorTest, MutableIteration) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + EXPECT_EQ("foo", *iter); + ++iter; + EXPECT_EQ("bar", *(iter++)); + EXPECT_EQ("baz", *iter); + ++iter; + EXPECT_TRUE(proto_array_.end() == iter); + EXPECT_EQ("baz", *(--proto_array_.end())); +} + +TEST_F(RepeatedPtrFieldIteratorTest, ConstIteration) { + const RepeatedPtrField& const_proto_array = proto_array_; + RepeatedPtrField::const_iterator iter = const_proto_array.begin(); + EXPECT_EQ("foo", *iter); + ++iter; + EXPECT_EQ("bar", *(iter++)); + EXPECT_EQ("baz", *iter); + ++iter; + EXPECT_TRUE(const_proto_array.end() == iter); + EXPECT_EQ("baz", *(--const_proto_array.end())); +} + +TEST_F(RepeatedPtrFieldIteratorTest, RandomAccess) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + RepeatedPtrField::iterator iter2 = iter; + ++iter2; + ++iter2; + EXPECT_TRUE(iter + 2 == iter2); + EXPECT_TRUE(iter == iter2 - 2); + EXPECT_EQ("baz", iter[2]); + EXPECT_EQ("baz", *(iter + 2)); + EXPECT_EQ(3, proto_array_.end() - proto_array_.begin()); +} + +TEST_F(RepeatedPtrFieldIteratorTest, Comparable) { + RepeatedPtrField::const_iterator iter = proto_array_.begin(); + RepeatedPtrField::const_iterator iter2 = iter + 1; + EXPECT_TRUE(iter == iter); + EXPECT_TRUE(iter != iter2); + EXPECT_TRUE(iter < iter2); + EXPECT_TRUE(iter <= iter2); + EXPECT_TRUE(iter <= iter); + EXPECT_TRUE(iter2 > iter); + EXPECT_TRUE(iter2 >= iter); + EXPECT_TRUE(iter >= iter); +} + +// Uninitialized iterator does not point to any of the RepeatedPtrField. +// Dereferencing an uninitialized iterator crashes the process. +TEST_F(RepeatedPtrFieldIteratorTest, UninitializedIterator) { + RepeatedPtrField::iterator iter; + EXPECT_TRUE(iter != proto_array_.begin()); + EXPECT_TRUE(iter != proto_array_.begin() + 1); + EXPECT_TRUE(iter != proto_array_.begin() + 2); + EXPECT_TRUE(iter != proto_array_.begin() + 3); + EXPECT_TRUE(iter != proto_array_.end()); +#ifdef GTEST_HAS_DEATH_TEST + ASSERT_DEATH(GOOGLE_LOG(INFO) << *iter, ""); +#endif +} + +TEST_F(RepeatedPtrFieldIteratorTest, STLAlgorithms_lower_bound) { + proto_array_.Clear(); + proto_array_.Add()->assign("a"); + proto_array_.Add()->assign("c"); + proto_array_.Add()->assign("d"); + proto_array_.Add()->assign("n"); + proto_array_.Add()->assign("p"); + proto_array_.Add()->assign("x"); + proto_array_.Add()->assign("y"); + + string v = "f"; + RepeatedPtrField::const_iterator it = + lower_bound(proto_array_.begin(), proto_array_.end(), v); + EXPECT_EQ(*it, "n"); + EXPECT_TRUE(it == proto_array_.begin() + 3); +} + +TEST_F(RepeatedPtrFieldIteratorTest, Mutation) { + RepeatedPtrField::iterator iter = proto_array_.begin(); + *iter = "qux"; + EXPECT_EQ("qux", proto_array_.Get(0)); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/service.cc b/src/google/protobuf/service.cc new file mode 100644 index 00000000..0c997930 --- /dev/null +++ b/src/google/protobuf/service.cc @@ -0,0 +1,32 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include + +namespace google { +namespace protobuf { + +Service::~Service() {} +RpcChannel::~RpcChannel() {} +RpcController::~RpcController() {} + +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/service.h b/src/google/protobuf/service.h new file mode 100644 index 00000000..22edf694 --- /dev/null +++ b/src/google/protobuf/service.h @@ -0,0 +1,273 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This module declares the abstract interfaces underlying proto2 RPC +// services. These are intented to be independent of any particular RPC +// implementation, so that proto2 services can be used on top of a variety +// of implementations. +// +// +// When you use the protocol compiler to compile a service definition, it +// generates two classes: An abstract interface for the service (with +// methods matching the service definition) and a "stub" implementation. +// A stub is just a type-safe wrapper around an RpcChannel which emulates a +// local implementation of the service. +// +// For example, the service definition: +// service MyService { +// rpc Foo(MyRequest) returns(MyResponse); +// } +// will generate abstract interface "MyService" and class "MyService::Stub". +// You could implement a MyService as follows: +// class MyServiceImpl : public MyService { +// public: +// MyServiceImpl() {} +// ~MyServiceImpl() {} +// +// // implements MyService --------------------------------------- +// +// void Foo(google::protobuf::RpcController* controller, +// const MyRequest* request, +// MyResponse* response, +// Closure* done) { +// // ... read request and fill in response ... +// done->Run(); +// } +// }; +// You would then register an instance of MyServiceImpl with your RPC server +// implementation. (How to do that depends on the implementation.) +// +// To call a remote MyServiceImpl, first you need an RpcChannel connected to it. +// How to construct a channel depends, again, on your RPC implementation. +// Here we use a hypothentical "MyRpcChannel" as an example: +// MyRpcChannel channel("rpc:hostname:1234/myservice"); +// MyRpcController controller; +// MyServiceImpl::Stub stub(&channel); +// FooRequest request; +// FooRespnose response; +// +// // ... fill in request ... +// +// stub.Foo(&controller, request, &response, NewCallback(HandleResponse)); +// +// On Thread-Safety: +// +// Different RPC implementations may make different guarantees about what +// threads they may run callbacks on, and what threads the application is +// allowed to use to call the RPC system. Portable software should be ready +// for callbacks to be called on any thread, but should not try to call the +// RPC system from any thread except for the ones on which it received the +// callbacks. Realistically, though, simple software will probably want to +// use a single-threaded RPC system while high-end software will want to +// use multiple threads. RPC implementations should provide multiple +// choices. + +#ifndef GOOGLE_PROTOBUF_SERVICE_H__ +#define GOOGLE_PROTOBUF_SERVICE_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// Defined in this file. +class Service; +class RpcController; +class RpcChannel; + +// Defined in other files. +class Descriptor; // descriptor.h +class ServiceDescriptor; // descriptor.h +class MethodDescriptor; // descriptor.h +class Message; // message.h + +// Abstract base interface for protocol-buffer-based RPC services. Services +// themselves are abstract interfaces (implemented either by servers or as +// stubs), but they subclass this base interface. The methods of this +// interface can be used to call the methods of the Service without knowing +// its exact type at compile time (analogous to Reflection). +class LIBPROTOBUF_EXPORT Service { + public: + inline Service() {} + virtual ~Service(); + + // When constructing a stub, you may pass STUB_OWNS_CHANNEL as the second + // parameter to the constructor to tell it to delete its RpcChannel when + // destroyed. + enum ChannelOwnership { + STUB_OWNS_CHANNEL, + STUB_DOESNT_OWN_CHANNEL + }; + + // Get the ServiceDescriptor describing this service and its methods. + virtual const ServiceDescriptor* GetDescriptor() = 0; + + // Call a method of the service specified by MethodDescriptor. This is + // normally implemented as a simple switch() that calls the standard + // definitions of the service's methods. + // + // Preconditions: + // * method->service() == GetDescriptor() + // * request and response are of the exact same classes as the objects + // returned by GetRequestPrototype(method) and + // GetResponsePrototype(method). + // * After the call has started, the request must not be modified and the + // response must not be accessed at all until "done" is called. + // * "controller" is of the correct type for the RPC implementation being + // used by this Service. For stubs, the "correct type" depends on the + // RpcChannel which the stub is using. Server-side Service + // implementations are expected to accept whatever type of RpcController + // the server-side RPC implementation uses. + // + // Postconditions: + // * "done" will be called when the method is complete. This may be + // before CallMethod() returns or it may be at some point in the future. + // * If the RPC succeeded, "response" contains the response returned by + // the server. + // * If the RPC failed, "response"'s contents are undefined. The + // RpcController can be queried to determine if an error occurred and + // possibly to get more information about the error. + virtual void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) = 0; + + // CallMethod() requires that the request and response passed in are of a + // particular subclass of Message. GetRequestPrototype() and + // GetResponsePrototype() get the default instances of these required types. + // You can then call Message::New() on these instances to construct mutable + // objects which you can then pass to CallMethod(). + // + // Example: + // const MethodDescriptor* method = + // service->GetDescriptor()->FindMethodByName("Foo"); + // Message* request = stub->GetRequestPrototype (method)->New(); + // Message* response = stub->GetResponsePrototype(method)->New(); + // request->ParseFromString(input); + // service->CallMethod(method, *request, response, callback); + virtual const Message& GetRequestPrototype( + const MethodDescriptor* method) const = 0; + virtual const Message& GetResponsePrototype( + const MethodDescriptor* method) const = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Service); +}; + +// An RpcController mediates a single method call. The primary purpose of +// the controller is to provide a way to manipulate settings specific to the +// RPC implementation and to find out about RPC-level errors. +// +// The methods provided by the RpcController interface are intended to be a +// "least common denominator" set of features which we expect all +// implementations to support. Specific implementations may provide more +// advanced features (e.g. deadline propagation). +class LIBPROTOBUF_EXPORT RpcController { + public: + inline RpcController() {} + virtual ~RpcController(); + + // Client-side methods --------------------------------------------- + // These calls may be made from the client side only. Their results + // are undefined on the server side (may crash). + + // Resets the RpcController to its initial state so that it may be reused in + // a new call. Must not be called while an RPC is in progress. + virtual void Reset() = 0; + + // After a call has finished, returns true if the call failed. The possible + // reasons for failure depend on the RPC implementation. Failed() must not + // be called before a call has finished. If Failed() returns true, the + // contents of the response message are undefined. + virtual bool Failed() const = 0; + + // If Failed() is true, returns a human-readable description of the error. + virtual string ErrorText() const = 0; + + // Advises the RPC system that the caller desires that the RPC call be + // canceled. The RPC system may cancel it immediately, may wait awhile and + // then cancel it, or may not even cancel the call at all. If the call is + // canceled, the "done" callback will still be called and the RpcController + // will indicate that the call failed at that time. + virtual void StartCancel() = 0; + + // Server-side methods --------------------------------------------- + // These calls may be made from the server side only. Their results + // are undefined on the client side (may crash). + + // Causes Failed() to return true on the client side. "reason" will be + // incorporated into the message returned by ErrorText(). If you find + // you need to return machine-readable information about failures, you + // should incorporate it into your response protocol buffer and should + // NOT call SetFailed(). + virtual void SetFailed(const string& reason) = 0; + + // If true, indicates that the client canceled the RPC, so the server may + // as well give up on replying to it. The server should still call the + // final "done" callback. + virtual bool IsCanceled() const = 0; + + // Asks that the given callback be called when the RPC is canceled. The + // callback will always be called exactly once. If the RPC completes without + // being canceled, the callback will be called after completion. If the RPC + // has already been canceled when NotifyOnCancel() is called, the callback + // will be called immediately. + // + // NotifyOnCancel() must be called no more than once per request. + virtual void NotifyOnCancel(Closure* callback) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcController); +}; + +// Abstract interface for an RPC channel. An RpcChannel represents a +// communication line to a Service which can be used to call that Service's +// methods. The Service may be running on another machine. Normally, you +// should not call an RpcChannel directly, but instead construct a stub Service +// wrapping it. Example: +// RpcChannel* channel = new MyRpcChannel("remotehost.example.com:1234"); +// MyService* service = new MyService::Stub(channel); +// service->MyMethod(request, &response, callback); +class LIBPROTOBUF_EXPORT RpcChannel { + public: + inline RpcChannel() {} + virtual ~RpcChannel(); + + // Call the given method of the remote service. The signature of this + // procedure looks the same as Service::CallMethod(), but the requirements + // are less strict in one important way: the request and response objects + // need not be of any specific class as long as their descriptors are + // method->input_type() and method->output_type(). + virtual void CallMethod(const MethodDescriptor* method, + RpcController* controller, + const Message* request, + Message* response, + Closure* done) = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(RpcChannel); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_SERVICE_H__ diff --git a/src/google/protobuf/stubs/common.cc b/src/google/protobuf/stubs/common.cc new file mode 100644 index 00000000..d7182841 --- /dev/null +++ b/src/google/protobuf/stubs/common.cc @@ -0,0 +1,261 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include +#include + +#include "config.h" + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN // We only need minimal includes +#include +#elif defined(HAVE_PTHREAD) +#include +#else +#error "No suitable threading library available." +#endif + +namespace google { +namespace protobuf { + +namespace internal { + +void VerifyVersion(int headerVersion, + int minLibraryVersion, + const char* filename) { + if (GOOGLE_PROTOBUF_VERSION < minLibraryVersion) { + // Library is too old for headers. + GOOGLE_LOG(FATAL) + << "This program requires version " << VersionString(minLibraryVersion) + << " of the Protocol Buffer runtime library, but the installed version " + "is " << VersionString(GOOGLE_PROTOBUF_VERSION) << ". Please update " + "your library. If you compiled the program yourself, make sure that " + "your headers are from the same version of Protocol Buffers as your " + "link-time library. (Version verification failed in \"" + << filename << "\".)"; + } + if (headerVersion < kMinHeaderVersionForLibrary) { + // Headers are too old for library. + GOOGLE_LOG(FATAL) + << "This program was compiled against version " + << VersionString(headerVersion) << " of the Protocol Buffer runtime " + "library, which is not compatible with the installed version (" + << VersionString(GOOGLE_PROTOBUF_VERSION) << "). Contact the program " + "author for an update. If you compiled the program yourself, make " + "sure that your headers are from the same version of Protocol Buffers " + "as your link-time library. (Version verification failed in \"" + << filename << "\".)"; + } +} + +string VersionString(int version) { + int major = version / 1000000; + int minor = (version / 1000) % 1000; + int micro = version % 1000; + + return strings::Substitute("$0.$1.$2", major, minor, micro); +} + +} // namespace internal + +// =================================================================== +// emulates google3/base/logging.cc + +namespace internal { + +void DefaultLogHandler(LogLevel level, const char* filename, int line, + const string& message) { + static const char* level_names[] = { "INFO", "WARNING", "ERROR", "FATAL" }; + + // We use fprintf() instead of cerr because we want this to work at static + // initialization time. + fprintf(stderr, "libprotobuf %s %s:%d] %s\n", + level_names[level], filename, line, message.c_str()); + fflush(stderr); // Needed on MSVC. +} + +void NullLogHandler(LogLevel level, const char* filename, int line, + const string& message) { + // Nothing. +} + +static LogHandler* log_handler_ = &DefaultLogHandler; +static int log_silencer_count_ = 0; +static Mutex log_silencer_count_mutex_; + +static string SimpleCtoa(char c) { return string(1, c); } + +#undef DECLARE_STREAM_OPERATOR +#define DECLARE_STREAM_OPERATOR(TYPE, TOSTRING) \ + LogMessage& LogMessage::operator<<(TYPE value) { \ + message_ += TOSTRING(value); \ + return *this; \ + } + +DECLARE_STREAM_OPERATOR(const string&, ) +DECLARE_STREAM_OPERATOR(const char* , ) +DECLARE_STREAM_OPERATOR(char , SimpleCtoa) +DECLARE_STREAM_OPERATOR(int , SimpleItoa) +DECLARE_STREAM_OPERATOR(uint , SimpleItoa) +DECLARE_STREAM_OPERATOR(double , SimpleDtoa) +#undef DECLARE_STREAM_OPERATOR + +LogMessage::LogMessage(LogLevel level, const char* filename, int line) + : level_(level), filename_(filename), line_(line) {} +LogMessage::~LogMessage() {} + +void LogMessage::Finish() { + bool suppress = false; + + if (level_ != LOGLEVEL_FATAL) { + MutexLock lock(&internal::log_silencer_count_mutex_); + suppress = internal::log_silencer_count_ > 0; + } + + if (!suppress) { + internal::log_handler_(level_, filename_, line_, message_); + } + + if (level_ == LOGLEVEL_FATAL) { + abort(); + } +} + +void LogFinisher::operator=(LogMessage& other) { + other.Finish(); +} + +} // namespace internal + +LogHandler* SetLogHandler(LogHandler* new_func) { + LogHandler* old = internal::log_handler_; + if (old == &internal::NullLogHandler) { + old = NULL; + } + if (new_func == NULL) { + internal::log_handler_ = &internal::NullLogHandler; + } else { + internal::log_handler_ = new_func; + } + return old; +} + +LogSilencer::LogSilencer() { + MutexLock lock(&internal::log_silencer_count_mutex_); + ++internal::log_silencer_count_; +}; + +LogSilencer::~LogSilencer() { + MutexLock lock(&internal::log_silencer_count_mutex_); + --internal::log_silencer_count_; +}; + +// =================================================================== +// emulates google3/base/callback.cc + +Closure::~Closure() {} + +namespace internal { FunctionClosure0::~FunctionClosure0() {} } + +void DoNothing() {} + +// =================================================================== +// emulates google3/base/mutex.cc + +#ifdef _WIN32 + +struct Mutex::Internal { + CRITICAL_SECTION mutex; +#ifndef NDEBUG + // Used only to implement AssertHeld(). + DWORD thread_id; +#endif +}; + +Mutex::Mutex() + : mInternal(new Internal) { + InitializeCriticalSection(&mInternal->mutex); +} + +Mutex::~Mutex() { + DeleteCriticalSection(&mInternal->mutex); + delete mInternal; +} + +void Mutex::Lock() { + EnterCriticalSection(&mInternal->mutex); +#ifndef NDEBUG + mInternal->thread_id = GetCurrentThreadId(); +#endif +} + +void Mutex::Unlock() { +#ifndef NDEBUG + mInternal->thread_id = 0; +#endif + LeaveCriticalSection(&mInternal->mutex); +} + +void Mutex::AssertHeld() { +#ifndef NDEBUG + GOOGLE_DCHECK_EQ(mInternal->thread_id, GetCurrentThreadId()); +#endif +} + +#elif defined(HAVE_PTHREAD) + +struct Mutex::Internal { + pthread_mutex_t mutex; +}; + +Mutex::Mutex() + : mInternal(new Internal) { + pthread_mutex_init(&mInternal->mutex, NULL); +} + +Mutex::~Mutex() { + pthread_mutex_destroy(&mInternal->mutex); + delete mInternal; +} + +void Mutex::Lock() { + int result = pthread_mutex_lock(&mInternal->mutex); + if (result != 0) { + GOOGLE_LOG(FATAL) << "pthread_mutex_lock: " << strerror(result); + } +} + +void Mutex::Unlock() { + int result = pthread_mutex_unlock(&mInternal->mutex); + if (result != 0) { + GOOGLE_LOG(FATAL) << "pthread_mutex_unlock: " << strerror(result); + } +} + +void Mutex::AssertHeld() { + // pthreads dosn't provide a way to check which thread holds the mutex. + // TODO(kenton): Maybe keep track of locking thread ID like with WIN32? +} + +#endif + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h new file mode 100644 index 00000000..89070d63 --- /dev/null +++ b/src/google/protobuf/stubs/common.h @@ -0,0 +1,1061 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) and others +// +// Contains basic types and utilities used by the rest of the library. + +#ifndef GOOGLE_PROTOBUF_COMMON_H__ +#define GOOGLE_PROTOBUF_COMMON_H__ + +#include +#include +#include +#include +#include +#ifndef _MSC_VER +#include +#endif + +namespace std {} + +namespace google { +namespace protobuf { + +using namespace std; // Don't do this at home, kids. + +#undef GOOGLE_DISALLOW_EVIL_CONSTRUCTORS +#define GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +#ifdef _MSC_VER + #ifdef LIBPROTOBUF_EXPORTS + #define LIBPROTOBUF_EXPORT __declspec(dllexport) + #else + #define LIBPROTOBUF_EXPORT __declspec(dllimport) + #endif + #ifdef LIBPROTOC_EXPORTS + #define LIBPROTOC_EXPORT __declspec(dllexport) + #else + #define LIBPROTOC_EXPORT __declspec(dllimport) + #endif +#else + #define LIBPROTOBUF_EXPORT + #define LIBPROTOC_EXPORT +#endif + +namespace internal { + +// Some of these constants are macros rather than const ints so that they can +// be used in #if directives. + +// The current version, represented as a single integer to make comparison +// easier: major * 10^6 + minor * 10^3 + micro +#define GOOGLE_PROTOBUF_VERSION 2000001 + +// The minimum library version which works with the current version of the +// headers. +#define GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION 2000001 + +// The minimum header version which works with the current version of +// the library. This constant should only be used by protoc's C++ code +// generator. +static const int kMinHeaderVersionForLibrary = 2000001; + +// The minimum protoc version which works with the current version of the +// headers. +#define GOOGLE_PROTOBUF_MIN_PROTOC_VERSION 2000001 + +// The minimum header version which works with the current version of +// protoc. This constant should only be used in VerifyVersion(). +static const int kMinHeaderVersionForProtoc = 2000001; + +// Verifies that the headers and libraries are compatible. Use the macro +// below to call this. +void LIBPROTOBUF_EXPORT VerifyVersion(int headerVersion, int minLibraryVersion, + const char* filename); + +// Converts a numeric version number to a string. +string LIBPROTOBUF_EXPORT VersionString(int version); + +} // namespace internal + +// Place this macro in your main() function (or somewhere before you attempt +// to use the protobuf library) to verify that the version you link against +// matches the headers you compiled against. If a version mismatch is +// detected, the process will abort. +#define GOOGLE_PROTOBUF_VERIFY_VERSION \ + ::google::protobuf::internal::VerifyVersion( \ + GOOGLE_PROTOBUF_VERSION, GOOGLE_PROTOBUF_MIN_LIBRARY_VERSION, \ + __FILE__) + +// =================================================================== +// from google3/base/port.h + +typedef unsigned int uint; + +#ifdef _MSC_VER +typedef __int8 int8; +typedef __int16 int16; +typedef __int32 int32; +typedef __int64 int64; + +typedef unsigned __int8 uint8; +typedef unsigned __int16 uint16; +typedef unsigned __int32 uint32; +typedef unsigned __int64 uint64; +#else +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; +#endif + +// long long macros to be used because gcc and vc++ use different suffixes, +// and different size specifiers in format strings +#undef GOOGLE_LONGLONG +#undef GOOGLE_ULONGLONG +#undef GOOGLE_LL_FORMAT + +#ifdef _MSC_VER +#define GOOGLE_LONGLONG(x) x##I64 +#define GOOGLE_ULONGLONG(x) x##UI64 +#define GOOGLE_LL_FORMAT "I64" // As in printf("%I64d", ...) +#else +#define GOOGLE_LONGLONG(x) x##LL +#define GOOGLE_ULONGLONG(x) x##ULL +#define GOOGLE_LL_FORMAT "ll" // As in "%lld". Note that "q" is poor form also. +#endif + +static const int32 kint32max = 0x7FFFFFFF; +static const int32 kint32min = -kint32max - 1; +static const int64 kint64max = GOOGLE_LONGLONG(0x7FFFFFFFFFFFFFFF); +static const int64 kint64min = -kint64max - 1; +static const uint32 kuint32max = 0xFFFFFFFFu; +static const uint64 kuint64max = GOOGLE_ULONGLONG(0xFFFFFFFFFFFFFFFF); + +#undef GOOGLE_ATTRIBUTE_ALWAYS_INLINE +#if defined(__GNUC__) && (__GNUC__ > 3 ||(__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +// For functions we want to force inline. +// Introduced in gcc 3.1. +#define GOOGLE_ATTRIBUTE_ALWAYS_INLINE __attribute__ ((always_inline)) +#else +// Other compilers will have to figure it out for themselves. +#define GOOGLE_ATTRIBUTE_ALWAYS_INLINE +#endif + +// =================================================================== +// from google3/base/basictypes.h + +// The GOOGLE_ARRAYSIZE(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. +// +// GOOGLE_ARRAYSIZE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using GOOGLE_ARRAYSIZE, you are (wrongfully) giving it a pointer. +// You should only use GOOGLE_ARRAYSIZE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. +// +// Kudos to Jorg Brown for this simple and elegant implementation. + +#undef GOOGLE_ARRAYSIZE +#define GOOGLE_ARRAYSIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) + +namespace internal { + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template +inline To implicit_cast(From const &f) { + return f; +} + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use implicit_cast<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. + +template // use like this: down_cast(foo); +inline To down_cast(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + implicit_cast(0); + } + + assert(f == NULL || dynamic_cast(f) != NULL); // RTTI: debug mode only! + return static_cast(f); +} + +} // namespace internal + +// We made these internal so that they would show up as such in the docs, +// but we don't want to stick "internal::" in front of them everywhere. +using internal::implicit_cast; +using internal::down_cast; + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +namespace internal { + +template +struct CompileAssert { +}; + +} // namespace internal + +#undef GOOGLE_COMPILE_ASSERT +#define GOOGLE_COMPILE_ASSERT(expr, msg) \ + typedef ::google::protobuf::internal::CompileAssert<(bool(expr))> \ + msg[bool(expr) ? 1 : -1] + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// =================================================================== +// from google3/base/scoped_ptr.h + +namespace internal { + +// This is an implementation designed to match the anticipated future TR2 +// implementation of the scoped_ptr class, and its closely-related brethren, +// scoped_array, scoped_ptr_malloc, and make_scoped_ptr. + +template class scoped_ptr; +template class scoped_array; + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// +// The size of a scoped_ptr is small: +// sizeof(scoped_ptr) == sizeof(C*) +template +class scoped_ptr { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with new. + explicit scoped_ptr(C* p = NULL) : ptr_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_ptr() { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != ptr_) { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + ptr_ = p; + } + } + + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + C* get() const { return ptr_; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return ptr_ == p; } + bool operator!=(C* p) const { return ptr_ != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + C* tmp = ptr_; + ptr_ = p2.ptr_; + p2.ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + private: + C* ptr_; + + // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't + // make sense, and if C2 == C, it still doesn't make sense because you should + // never have the same object owned by two different scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; + + // Disallow evil constructors + scoped_ptr(const scoped_ptr&); + void operator=(const scoped_ptr&); +}; + +// scoped_array is like scoped_ptr, except that the caller must allocate +// with new [] and the destructor deletes objects with delete []. +// +// As with scoped_ptr, a scoped_array either points to an object +// or is NULL. A scoped_array owns the object that it points to. +// +// Size: sizeof(scoped_array) == sizeof(C*) +template +class scoped_array { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_array. + // The input parameter must be allocated with new []. + explicit scoped_array(C* p = NULL) : array_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_array() { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != array_) { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + array_ = p; + } + } + + // Get one element of the current object. + // Will assert() if there is no current object, or index i is negative. + C& operator[](std::ptrdiff_t i) const { + assert(i >= 0); + assert(array_ != NULL); + return array_[i]; + } + + // Get a pointer to the zeroth element of the current object. + // If there is no current object, return NULL. + C* get() const { + return array_; + } + + // Comparison operators. + // These return whether two scoped_array refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return array_ == p; } + bool operator!=(C* p) const { return array_ != p; } + + // Swap two scoped arrays. + void swap(scoped_array& p2) { + C* tmp = array_; + array_ = p2.array_; + p2.array_ = tmp; + } + + // Release an array. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = array_; + array_ = NULL; + return retVal; + } + + private: + C* array_; + + // Forbid comparison of different scoped_array types. + template bool operator==(scoped_array const& p2) const; + template bool operator!=(scoped_array const& p2) const; + + // Disallow evil constructors + scoped_array(const scoped_array&); + void operator=(const scoped_array&); +}; + +} // namespace internal + +// We made these internal so that they would show up as such in the docs, +// but we don't want to stick "internal::" in front of them everywhere. +using internal::scoped_ptr; +using internal::scoped_array; + +// =================================================================== +// emulates google3/base/logging.h + +enum LogLevel { + LOGLEVEL_INFO, // Informational. This is never actually used by + // libprotobuf. + LOGLEVEL_WARNING, // Warns about issues that, although not technically a + // problem now, could cause problems in the future. For + // example, a // warning will be printed when parsing a + // message that is near the message size limit. + LOGLEVEL_ERROR, // An error occurred which should never happen during + // normal use. + LOGLEVEL_FATAL, // An error occurred from which the library cannot + // recover. This usually indicates a programming error + // in the code which calls the library, especially when + // compiled in debug mode. + +#ifdef NDEBUG + LOGLEVEL_DFATAL = LOGLEVEL_ERROR +#else + LOGLEVEL_DFATAL = LOGLEVEL_FATAL +#endif +}; + +namespace internal { + +class LogFinisher; + +class LIBPROTOBUF_EXPORT LogMessage { + public: + LogMessage(LogLevel level, const char* filename, int line); + ~LogMessage(); + + LogMessage& operator<<(const string& value); + LogMessage& operator<<(const char* value); + LogMessage& operator<<(char value); + LogMessage& operator<<(int value); + LogMessage& operator<<(uint value); + LogMessage& operator<<(double value); + + private: + friend class LogFinisher; + void Finish(); + + LogLevel level_; + const char* filename_; + int line_; + string message_; +}; + +// Used to make the entire "LOG(BLAH) << etc." expression have a void return +// type and print a newline after each message. +class LIBPROTOBUF_EXPORT LogFinisher { + public: + void operator=(LogMessage& other); +}; + +} // namespace internal + +// Undef everything in case we're being mixed with some other Google library +// which already defined them itself. Presumably all Google libraries will +// support the same syntax for these so it should not be a big deal if they +// end up using our definitions instead. +#undef GOOGLE_LOG +#undef GOOGLE_LOG_IF + +#undef GOOGLE_CHECK +#undef GOOGLE_CHECK_EQ +#undef GOOGLE_CHECK_NE +#undef GOOGLE_CHECK_LT +#undef GOOGLE_CHECK_LE +#undef GOOGLE_CHECK_GT +#undef GOOGLE_CHECK_GE + +#undef GOOGLE_DLOG +#undef GOOGLE_DCHECK +#undef GOOGLE_DCHECK_EQ +#undef GOOGLE_DCHECK_NE +#undef GOOGLE_DCHECK_LT +#undef GOOGLE_DCHECK_LE +#undef GOOGLE_DCHECK_GT +#undef GOOGLE_DCHECK_GE + +#define GOOGLE_LOG(LEVEL) \ + ::google::protobuf::internal::LogFinisher() = \ + ::google::protobuf::internal::LogMessage( \ + ::google::protobuf::LOGLEVEL_##LEVEL, __FILE__, __LINE__) +#define GOOGLE_LOG_IF(LEVEL, CONDITION) \ + !(CONDITION) ? (void)0 : GOOGLE_LOG(LEVEL) + +#define GOOGLE_CHECK(EXPRESSION) \ + GOOGLE_LOG_IF(FATAL, !(EXPRESSION)) << "CHECK failed: " #EXPRESSION ": " +#define GOOGLE_CHECK_EQ(A, B) GOOGLE_CHECK(A == B) +#define GOOGLE_CHECK_NE(A, B) GOOGLE_CHECK(A != B) +#define GOOGLE_CHECK_LT(A, B) GOOGLE_CHECK(A < B) +#define GOOGLE_CHECK_LE(A, B) GOOGLE_CHECK(A <= B) +#define GOOGLE_CHECK_GT(A, B) GOOGLE_CHECK(A > B) +#define GOOGLE_CHECK_GE(A, B) GOOGLE_CHECK(A >= B) + +#ifdef NDEBUG + +#define GOOGLE_DLOG GOOGLE_LOG_IF(false, INFO) + +#define GOOGLE_DCHECK(EXPRESSION) while(false) GOOGLE_CHECK(EXPRESSION) +#define GOOGLE_DCHECK_EQ(A, B) GOOGLE_DCHECK(A == B) +#define GOOGLE_DCHECK_NE(A, B) GOOGLE_DCHECK(A != B) +#define GOOGLE_DCHECK_LT(A, B) GOOGLE_DCHECK(A < B) +#define GOOGLE_DCHECK_LE(A, B) GOOGLE_DCHECK(A <= B) +#define GOOGLE_DCHECK_GT(A, B) GOOGLE_DCHECK(A > B) +#define GOOGLE_DCHECK_GE(A, B) GOOGLE_DCHECK(A >= B) + +#else // NDEBUG + +#define GOOGLE_DLOG GOOGLE_LOG + +#define GOOGLE_DCHECK GOOGLE_CHECK +#define GOOGLE_DCHECK_EQ GOOGLE_CHECK_EQ +#define GOOGLE_DCHECK_NE GOOGLE_CHECK_NE +#define GOOGLE_DCHECK_LT GOOGLE_CHECK_LT +#define GOOGLE_DCHECK_LE GOOGLE_CHECK_LE +#define GOOGLE_DCHECK_GT GOOGLE_CHECK_GT +#define GOOGLE_DCHECK_GE GOOGLE_CHECK_GE + +#endif // !NDEBUG + +typedef void LogHandler(LogLevel level, const char* filename, int line, + const string& message); + +// The protobuf library sometimes writes warning and error messages to +// stderr. These messages are primarily useful for developers, but may +// also help end users figure out a problem. If you would prefer that +// these messages be sent somewhere other than stderr, call SetLogHandler() +// to set your own handler. This returns the old handler. Set the handler +// to NULL to ignore log messages (but see also LogSilencer, below). +// +// Obviously, SetLogHandler is not thread-safe. You should only call it +// at initialization time, and probably not from library code. If you +// simply want to suppress log messages temporarily (e.g. because you +// have some code that tends to trigger them frequently and you know +// the warnings are not important to you), use the LogSilencer class +// below. +LIBPROTOBUF_EXPORT LogHandler* SetLogHandler(LogHandler* new_func); + +// Create a LogSilencer if you want to temporarily suppress all log +// messages. As long as any LogSilencer objects exist, non-fatal +// log messages will be discarded (the current LogHandler will *not* +// be called). Constructing a LogSilencer is thread-safe. You may +// accidentally suppress log messages occurring in another thread, but +// since messages are generally for debugging purposes only, this isn't +// a big deal. If you want to intercept log messages, use SetLogHandler(). +class LIBPROTOBUF_EXPORT LogSilencer { + public: + LogSilencer(); + ~LogSilencer(); +}; + +// =================================================================== +// emulates google3/base/callback.h + +// Abstract interface for a callback. When calling an RPC, you must provide +// a Closure to call when the procedure completes. See the Service interface +// in service.h. +// +// To automatically construct a Closure which calls a particular function or +// method with a particular set of parameters, use the NewCallback() function. +// Example: +// void FooDone(const FooResponse* response) { +// ... +// } +// +// void CallFoo() { +// ... +// // When done, call FooDone() and pass it a pointer to the response. +// Closure* callback = NewCallback(&FooDone, response); +// // Make the call. +// service->Foo(controller, request, response, callback); +// } +// +// Example that calls a method: +// class Handler { +// public: +// ... +// +// void FooDone(const FooResponse* response) { +// ... +// } +// +// void CallFoo() { +// ... +// // When done, call FooDone() and pass it a pointer to the response. +// Closure* callback = NewCallback(this, &Handler::FooDone, response); +// // Make the call. +// service->Foo(controller, request, response, callback); +// } +// }; +// +// Currently NewCallback() supports binding zero, one, or two arguments. +// +// Callbacks created with NewCallback() automatically delete themselves when +// executed. They should be used when a callback is to be called exactly +// once (usually the case with RPC callbacks). If a callback may be called +// a different number of times (including zero), create it with +// NewPermanentCallback() instead. You are then responsible for deleting the +// callback (using the "delete" keyword as normal). +// +// Note that NewCallback() is a bit touchy regarding argument types. Generally, +// the values you provide for the parameter bindings must exactly match the +// types accepted by the callback function. For example: +// void Foo(string s); +// NewCallback(&Foo, "foo"); // WON'T WORK: const char* != string +// NewCallback(&Foo, string("foo")); // WORKS +// Also note that the arguments cannot be references: +// void Foo(const string& s); +// string my_str; +// NewCallback(&Foo, my_str); // WON'T WORK: Can't use referecnes. +// However, correctly-typed pointers will work just fine. +class LIBPROTOBUF_EXPORT Closure { + public: + Closure() {} + virtual ~Closure(); + + virtual void Run() = 0; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Closure); +}; + +namespace internal { + +class LIBPROTOBUF_EXPORT FunctionClosure0 : public Closure { + public: + typedef void (*FunctionType)(); + + FunctionClosure0(FunctionType function, bool self_deleting) + : function_(function), self_deleting_(self_deleting) {} + ~FunctionClosure0(); + + void Run() { + function_(); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; +}; + +template +class MethodClosure0 : public Closure { + public: + typedef void (Class::*MethodType)(); + + MethodClosure0(Class* object, MethodType method, bool self_deleting) + : object_(object), method_(method), self_deleting_(self_deleting) {} + ~MethodClosure0() {} + + void Run() { + (object_->*method_)(); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; +}; + +template +class FunctionClosure1 : public Closure { + public: + typedef void (*FunctionType)(Arg1 arg1); + + FunctionClosure1(FunctionType function, bool self_deleting, + Arg1 arg1) + : function_(function), self_deleting_(self_deleting), + arg1_(arg1) {} + ~FunctionClosure1() {} + + void Run() { + function_(arg1_); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; + Arg1 arg1_; +}; + +template +class MethodClosure1 : public Closure { + public: + typedef void (Class::*MethodType)(Arg1 arg1); + + MethodClosure1(Class* object, MethodType method, bool self_deleting, + Arg1 arg1) + : object_(object), method_(method), self_deleting_(self_deleting), + arg1_(arg1) {} + ~MethodClosure1() {} + + void Run() { + (object_->*method_)(arg1_); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; + Arg1 arg1_; +}; + +template +class FunctionClosure2 : public Closure { + public: + typedef void (*FunctionType)(Arg1 arg1, Arg2 arg2); + + FunctionClosure2(FunctionType function, bool self_deleting, + Arg1 arg1, Arg2 arg2) + : function_(function), self_deleting_(self_deleting), + arg1_(arg1), arg2_(arg2) {} + ~FunctionClosure2() {} + + void Run() { + function_(arg1_, arg2_); + if (self_deleting_) delete this; + } + + private: + FunctionType function_; + bool self_deleting_; + Arg1 arg1_; + Arg2 arg2_; +}; + +template +class MethodClosure2 : public Closure { + public: + typedef void (Class::*MethodType)(Arg1 arg1, Arg2 arg2); + + MethodClosure2(Class* object, MethodType method, bool self_deleting, + Arg1 arg1, Arg2 arg2) + : object_(object), method_(method), self_deleting_(self_deleting), + arg1_(arg1), arg2_(arg2) {} + ~MethodClosure2() {} + + void Run() { + (object_->*method_)(arg1_, arg2_); + if (self_deleting_) delete this; + } + + private: + Class* object_; + MethodType method_; + bool self_deleting_; + Arg1 arg1_; + Arg2 arg2_; +}; + +} // namespace internal + +// See Closure. +inline Closure* NewCallback(void (*function)()) { + return new internal::FunctionClosure0(function, true); +} + +// See Closure. +inline Closure* NewPermanentCallback(void (*function)()) { + return new internal::FunctionClosure0(function, false); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)()) { + return new internal::MethodClosure0(object, method, true); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(Class* object, void (Class::*method)()) { + return new internal::MethodClosure0(object, method, false); +} + +// See Closure. +template +inline Closure* NewCallback(void (*function)(Arg1), + Arg1 arg1) { + return new internal::FunctionClosure1(function, true, arg1); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(void (*function)(Arg1), + Arg1 arg1) { + return new internal::FunctionClosure1(function, false, arg1); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1), + Arg1 arg1) { + return new internal::MethodClosure1(object, method, true, arg1); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(Class* object, void (Class::*method)(Arg1), + Arg1 arg1) { + return new internal::MethodClosure1(object, method, false, arg1); +} + +// See Closure. +template +inline Closure* NewCallback(void (*function)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::FunctionClosure2( + function, true, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewPermanentCallback(void (*function)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::FunctionClosure2( + function, false, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewCallback(Class* object, void (Class::*method)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::MethodClosure2( + object, method, true, arg1, arg2); +} + +// See Closure. +template +inline Closure* NewPermanentCallback( + Class* object, void (Class::*method)(Arg1, Arg2), + Arg1 arg1, Arg2 arg2) { + return new internal::MethodClosure2( + object, method, false, arg1, arg2); +} + +// A function which does nothing. Useful for creating no-op callbacks, e.g.: +// Closure* nothing = NewCallback(&DoNothing); +void LIBPROTOBUF_EXPORT DoNothing(); + +// =================================================================== +// emulates google3/base/mutex.h + +namespace internal { + +// A Mutex is a non-reentrant (aka non-recursive) mutex. At most one thread T +// may hold a mutex at a given time. If T attempts to Lock() the same Mutex +// while holding it, T will deadlock. +class LIBPROTOBUF_EXPORT Mutex { + public: + // Create a Mutex that is not held by anybody. + Mutex(); + + // Destructor + ~Mutex(); + + // Block if necessary until this Mutex is free, then acquire it exclusively. + void Lock(); + + // Release this Mutex. Caller must hold it exclusively. + void Unlock(); + + // Crash if this Mutex is not held exclusively by this thread. + // May fail to crash when it should; will never crash when it should not. + void AssertHeld(); + + private: + struct Internal; + Internal* mInternal; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Mutex); +}; + +// MutexLock(mu) acquires mu when constructed and releases it when destroyed. +class LIBPROTOBUF_EXPORT MutexLock { + public: + explicit MutexLock(Mutex *mu) : mu_(mu) { this->mu_->Lock(); } + ~MutexLock() { this->mu_->Unlock(); } + private: + Mutex *const mu_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLock); +}; + +// MutexLockMaybe is like MutexLock, but is a no-op when mu is NULL. +class LIBPROTOBUF_EXPORT MutexLockMaybe { + public: + explicit MutexLockMaybe(Mutex *mu) : + mu_(mu) { if (this->mu_ != NULL) { this->mu_->Lock(); } } + ~MutexLockMaybe() { if (this->mu_ != NULL) { this->mu_->Unlock(); } } + private: + Mutex *const mu_; + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MutexLockMaybe); +}; + +} // namespace internal + +// We made these internal so that they would show up as such in the docs, +// but we don't want to stick "internal::" in front of them everywhere. +using internal::Mutex; +using internal::MutexLock; +using internal::MutexLockMaybe; + +// =================================================================== +// from google3/base/type_traits.h + +namespace internal { + +// Specified by TR1 [4.7.4] Pointer modifications. +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { typedef T type; }; +template struct remove_pointer { + typedef T type; }; + +} // namespace internal + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMMON_H__ diff --git a/src/google/protobuf/stubs/common_unittest.cc b/src/google/protobuf/stubs/common_unittest.cc new file mode 100644 index 00000000..c339c5fd --- /dev/null +++ b/src/google/protobuf/stubs/common_unittest.cc @@ -0,0 +1,330 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include +#include + +#include +#include + +#include "config.h" + +namespace google { +namespace protobuf { +namespace { + +// TODO(kenton): More tests. + +#ifdef PACKAGE_VERSION // only defined when using automake, not MSVC + +TEST(VersionTest, VersionMatchesConfig) { + // Verify that the version string specified in config.h matches the one + // in common.h. The config.h version is a string which may have a suffix + // like "beta", so we remove that. + string version = PACKAGE_VERSION; + int pos = version.size(); + while (pos > 0 && !ascii_isdigit(version[pos-1])) { + --pos; + } + version.erase(pos); + + EXPECT_EQ(version, internal::VersionString(GOOGLE_PROTOBUF_VERSION)); +} + +#endif // PACKAGE_VERSION + +TEST(CommonTest, IntMinMaxConstants) { + // kint32min was declared incorrectly in the first release of protobufs. + // Ugh. + EXPECT_LT(kint32min, kint32max); + EXPECT_EQ(static_cast(kint32min), static_cast(kint32max) + 1); + EXPECT_LT(kint64min, kint64max); + EXPECT_EQ(static_cast(kint64min), static_cast(kint64max) + 1); + EXPECT_EQ(0, kuint32max + 1); + EXPECT_EQ(0, kuint64max + 1); +} + +vector captured_messages_; + +void CaptureLog(LogLevel level, const char* filename, int line, + const string& message) { + captured_messages_.push_back( + strings::Substitute("$0 $1:$2: $3", + implicit_cast(level), filename, line, message)); +} + +TEST(LoggingTest, DefaultLogging) { + CaptureTestStderr(); + int line = __LINE__; + GOOGLE_LOG(INFO ) << "A message."; + GOOGLE_LOG(WARNING) << "A warning."; + GOOGLE_LOG(ERROR ) << "An error."; + + string text = GetCapturedTestStderr(); + EXPECT_EQ( + "libprotobuf INFO "__FILE__":" + SimpleItoa(line + 1) + "] A message.\n" + "libprotobuf WARNING "__FILE__":" + SimpleItoa(line + 2) + "] A warning.\n" + "libprotobuf ERROR "__FILE__":" + SimpleItoa(line + 3) + "] An error.\n", + text); +} + +TEST(LoggingTest, NullLogging) { + LogHandler* old_handler = SetLogHandler(NULL); + + CaptureTestStderr(); + GOOGLE_LOG(INFO ) << "A message."; + GOOGLE_LOG(WARNING) << "A warning."; + GOOGLE_LOG(ERROR ) << "An error."; + + EXPECT_TRUE(SetLogHandler(old_handler) == NULL); + + string text = GetCapturedTestStderr(); + EXPECT_EQ("", text); +} + +TEST(LoggingTest, CaptureLogging) { + captured_messages_.clear(); + + LogHandler* old_handler = SetLogHandler(&CaptureLog); + + int start_line = __LINE__; + GOOGLE_LOG(ERROR) << "An error."; + GOOGLE_LOG(WARNING) << "A warning."; + + EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog); + + ASSERT_EQ(2, captured_messages_.size()); + EXPECT_EQ( + "2 "__FILE__":" + SimpleItoa(start_line + 1) + ": An error.", + captured_messages_[0]); + EXPECT_EQ( + "1 "__FILE__":" + SimpleItoa(start_line + 2) + ": A warning.", + captured_messages_[1]); +} + +TEST(LoggingTest, SilenceLogging) { + captured_messages_.clear(); + + LogHandler* old_handler = SetLogHandler(&CaptureLog); + + int line1 = __LINE__; GOOGLE_LOG(INFO) << "Visible1"; + LogSilencer* silencer1 = new LogSilencer; + GOOGLE_LOG(INFO) << "Not visible."; + LogSilencer* silencer2 = new LogSilencer; + GOOGLE_LOG(INFO) << "Not visible."; + delete silencer1; + GOOGLE_LOG(INFO) << "Not visible."; + delete silencer2; + int line2 = __LINE__; GOOGLE_LOG(INFO) << "Visible2"; + + EXPECT_TRUE(SetLogHandler(old_handler) == &CaptureLog); + + ASSERT_EQ(2, captured_messages_.size()); + EXPECT_EQ( + "0 "__FILE__":" + SimpleItoa(line1) + ": Visible1", + captured_messages_[0]); + EXPECT_EQ( + "0 "__FILE__":" + SimpleItoa(line2) + ": Visible2", + captured_messages_[1]); +} + +class ClosureTest : public testing::Test { + public: + void SetA123Method() { a_ = 123; } + static void SetA123Function() { current_instance_->a_ = 123; } + + void SetAMethod(int a) { a_ = a; } + void SetCMethod(string c) { c_ = c; } + + static void SetAFunction(int a) { current_instance_->a_ = a; } + static void SetCFunction(string c) { current_instance_->c_ = c; } + + void SetABMethod(int a, const char* b) { a_ = a; b_ = b; } + static void SetABFunction(int a, const char* b) { + current_instance_->a_ = a; + current_instance_->b_ = b; + } + + virtual void SetUp() { + current_instance_ = this; + a_ = 0; + b_ = NULL; + c_.clear(); + } + + int a_; + const char* b_; + string c_; + + static ClosureTest* current_instance_; +}; + +ClosureTest* ClosureTest::current_instance_ = NULL; + +TEST_F(ClosureTest, TestClosureFunction0) { + Closure* closure = NewCallback(&SetA123Function); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); +} + +TEST_F(ClosureTest, TestClosureMethod0) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetA123Method); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); +} + +TEST_F(ClosureTest, TestClosureFunction1) { + Closure* closure = NewCallback(&SetAFunction, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); +} + +TEST_F(ClosureTest, TestClosureMethod1) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetAMethod, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); +} + +TEST_F(ClosureTest, TestClosureFunction1String) { + Closure* closure = NewCallback(&SetCFunction, string("test")); + EXPECT_NE("test", c_); + closure->Run(); + EXPECT_EQ("test", c_); +} + +TEST_F(ClosureTest, TestClosureMethod1String) { + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetCMethod, string("test")); + EXPECT_NE("test", c_); + closure->Run(); + EXPECT_EQ("test", c_); +} + +TEST_F(ClosureTest, TestClosureFunction2) { + const char* cstr = "hello"; + Closure* closure = NewCallback(&SetABFunction, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); +} + +TEST_F(ClosureTest, TestClosureMethod2) { + const char* cstr = "hello"; + Closure* closure = NewCallback(current_instance_, + &ClosureTest::SetABMethod, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); +} + +// Repeat all of the above with NewPermanentCallback() + +TEST_F(ClosureTest, TestPermanentClosureFunction0) { + Closure* closure = NewPermanentCallback(&SetA123Function); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(123, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod0) { + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetA123Method); + EXPECT_NE(123, a_); + closure->Run(); + EXPECT_EQ(123, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(123, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureFunction1) { + Closure* closure = NewPermanentCallback(&SetAFunction, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(456, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod1) { + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetAMethod, 456); + EXPECT_NE(456, a_); + closure->Run(); + EXPECT_EQ(456, a_); + a_ = 0; + closure->Run(); + EXPECT_EQ(456, a_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureFunction2) { + const char* cstr = "hello"; + Closure* closure = NewPermanentCallback(&SetABFunction, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + a_ = 0; + b_ = NULL; + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + delete closure; +} + +TEST_F(ClosureTest, TestPermanentClosureMethod2) { + const char* cstr = "hello"; + Closure* closure = NewPermanentCallback(current_instance_, + &ClosureTest::SetABMethod, 789, cstr); + EXPECT_NE(789, a_); + EXPECT_NE(cstr, b_); + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + a_ = 0; + b_ = NULL; + closure->Run(); + EXPECT_EQ(789, a_); + EXPECT_EQ(cstr, b_); + delete closure; +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/hash.cc b/src/google/protobuf/stubs/hash.cc new file mode 100644 index 00000000..43fb9d73 --- /dev/null +++ b/src/google/protobuf/stubs/hash.cc @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include + +namespace google { +namespace protobuf { + +// Nothing needed here right now. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/hash.h b/src/google/protobuf/stubs/hash.h new file mode 100644 index 00000000..a62b3f6e --- /dev/null +++ b/src/google/protobuf/stubs/hash.h @@ -0,0 +1,123 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// +// Deals with the fact that hash_map is not defined everywhere. + +#ifndef GOOGLE_PROTOBUF_STUBS_HASH_H__ +#define GOOGLE_PROTOBUF_STUBS_HASH_H__ + +#include +#include +#include "config.h" + +#if defined(HAVE_HASH_MAP) && defined(HAVE_HASH_SET) +#include HASH_MAP_H +#include HASH_SET_H +#else +// TODO(kenton): Deal with non-existence of hash_map somehow. Maybe emulate +// it with map? +#error "Your STL implementation lacks hash_map and/or hash_set." +#endif + +namespace google { +namespace protobuf { + +#ifdef _MSC_VER + +template +struct hash : public HASH_NAMESPACE::hash_compare { +}; + +// MSVC's hash_compare hashes based on the string contents but +// compares based on the string pointer. WTF? +class CstringLess { + public: + inline bool operator()(const char* a, const char* b) const { + return strcmp(a, b) < 0; + } +}; + +template <> +struct hash + : public HASH_NAMESPACE::hash_compare { +}; + +template , + typename EqualKey = int > +class hash_map : public HASH_NAMESPACE::hash_map< + Key, Data, HashFcn> { +}; + +template , + typename EqualKey = int > +class hash_set : public HASH_NAMESPACE::hash_set< + Key, HashFcn> { +}; + +#else + +template +struct hash : public HASH_NAMESPACE::hash { +}; + +template +struct hash { + inline size_t operator()(const Key* key) const { + return reinterpret_cast(key); + } +}; + +template <> +struct hash : public HASH_NAMESPACE::hash { +}; + +template , + typename EqualKey = std::equal_to > +class hash_map : public HASH_NAMESPACE::hash_map< + Key, Data, HashFcn, EqualKey> { +}; + +template , + typename EqualKey = std::equal_to > +class hash_set : public HASH_NAMESPACE::hash_set< + Key, HashFcn, EqualKey> { +}; + +#endif + +template <> +struct hash { + inline size_t operator()(const string& key) const { + return hash()(key.c_str()); + } + + static const size_t bucket_size = 4; + static const size_t min_buckets = 8; + inline size_t operator()(const string& a, const string& b) const { + return a < b; + } +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_HASH_H__ diff --git a/src/google/protobuf/stubs/map-util.cc b/src/google/protobuf/stubs/map-util.cc new file mode 100644 index 00000000..af05af30 --- /dev/null +++ b/src/google/protobuf/stubs/map-util.cc @@ -0,0 +1,28 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/util/gtl/map-util.cc +// Author: Anton Carver + +#include + +namespace google { +namespace protobuf { + +// Template module. Nothing to see here. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/map-util.h b/src/google/protobuf/stubs/map-util.h new file mode 100644 index 00000000..ee8073fe --- /dev/null +++ b/src/google/protobuf/stubs/map-util.h @@ -0,0 +1,90 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/util/gtl/map-util.h +// Author: Anton Carver + +#ifndef GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ +#define GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ + +#include + +namespace google { +namespace protobuf { + +// Perform a lookup in a map or hash_map. +// If the key is present a const pointer to the associated value is returned, +// otherwise a NULL pointer is returned. +template +const typename Collection::value_type::second_type* +FindOrNull(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return &it->second; +} + +// Perform a lookup in a map or hash_map whose values are pointers. +// If the key is present a const pointer to the associated value is returned, +// otherwise a NULL pointer is returned. +// This function does not distinguish between a missing key and a key mapped +// to a NULL value. +template +const typename Collection::value_type::second_type +FindPtrOrNull(const Collection& collection, + const typename Collection::value_type::first_type& key) { + typename Collection::const_iterator it = collection.find(key); + if (it == collection.end()) { + return 0; + } + return it->second; +} + +// Change the value associated with a particular key in a map or hash_map. +// If the key is not present in the map the key and value are inserted, +// otherwise the value is updated to be a copy of the value provided. +// True indicates that an insert took place, false indicates an update. +template +bool InsertOrUpdate(Collection * const collection, + const Key& key, const Value& value) { + pair ret = + collection->insert(typename Collection::value_type(key, value)); + if (!ret.second) { + // update + ret.first->second = value; + return false; + } + return true; +} + +// Insert a new key and value into a map or hash_map. +// If the key is not present in the map the key and value are +// inserted, otherwise nothing happens. True indicates that an insert +// took place, false indicates the key was already present. +template +bool InsertIfNotPresent(Collection * const collection, + const Key& key, const Value& value) { + pair ret = + collection->insert(typename Collection::value_type(key, value)); + return ret.second; +} + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_MAP_UTIL_H__ diff --git a/src/google/protobuf/stubs/stl_util-inl.cc b/src/google/protobuf/stubs/stl_util-inl.cc new file mode 100644 index 00000000..445c646e --- /dev/null +++ b/src/google/protobuf/stubs/stl_util-inl.cc @@ -0,0 +1,27 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/util/gtl/stl_util-inl.cc + +#include + +namespace google { +namespace protobuf { + +// Template module. Nothing to see here. + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/stl_util-inl.h b/src/google/protobuf/stubs/stl_util-inl.h new file mode 100644 index 00000000..db079a77 --- /dev/null +++ b/src/google/protobuf/stubs/stl_util-inl.h @@ -0,0 +1,107 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/util/gtl/stl_util-inl.h + +#ifndef GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ +#define GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ + +#include + +namespace google { +namespace protobuf { + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template +void STLDeleteContainerPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// Inside Google, this function implements a horrible, disgusting hack in which +// we reach into the string's private implementation and resize it without +// initializing the new bytes. In some cases doing this can significantly +// improve performance. However, since it's totally non-portable it has no +// place in open source code. Feel free to fill this function in with your +// own disgusting hack if you want the perf boost. +inline void STLStringResizeUninitialized(string* s, size_t new_size) { + s->resize(new_size); +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(string* str) { + // DO NOT USE const_cast(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// ElementDeleter (defined below), which ensures that your container's elements +// are deleted when the ElementDeleter goes out of scope. +template +void STLDeleteElements(T *container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template +void STLDeleteValues(T *v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_STL_UTIL_INL_H__ diff --git a/src/google/protobuf/stubs/strutil.cc b/src/google/protobuf/stubs/strutil.cc new file mode 100644 index 00000000..07caaf76 --- /dev/null +++ b/src/google/protobuf/stubs/strutil.cc @@ -0,0 +1,1121 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/strings/strutil.cc + +#include +#include +#include // FLT_DIG and DBL_DIG +#include +#include + +#ifdef _WIN32 +// MSVC has only _snprintf, not snprintf. +// +// MinGW has both snprintf and _snprintf, but they appear to be different +// functions. The former is buggy. When invoked like so: +// char buffer[32]; +// snprintf(buffer, 32, "%.*g\n", FLT_DIG, 1.23e10f); +// it prints "1.23000e+10". This is plainly wrong: %g should never print +// trailing zeros after the decimal point. For some reason this bug only +// occurs with some input values, not all. In any case, _snprintf does the +// right thing, so we use it. +#define snprintf _snprintf +#endif + +namespace google { +namespace protobuf { + +inline bool IsNaN(double value) { + // NaN is never equal to anything, even itself. + return value != value; +} + +// The definitions of these in ctype.h change based on locale. Since our +// string manipulation is all in relation to the protocol buffer and C++ +// languages, we always want to use the C locale. So, we re-define these +// exactly as we want them. +static bool isxdigit(char c) { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); +} + +static bool isprint(char c) { + return c >= 0x20 && c <= 0x7E; +} + +// ---------------------------------------------------------------------- +// StripString +// Replaces any occurrence of the character 'remove' (or the characters +// in 'remove') with the character 'replacewith'. +// ---------------------------------------------------------------------- +void StripString(string* s, const char* remove, char replacewith) { + const char * str_start = s->c_str(); + const char * str = str_start; + for (str = strpbrk(str, remove); + str != NULL; + str = strpbrk(str + 1, remove)) { + (*s)[str - str_start] = replacewith; + } +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Replace the "old" pattern with the "new" pattern in a string, +// and append the result to "res". If replace_all is false, +// it only replaces the first instance of "old." +// ---------------------------------------------------------------------- + +void StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all, + string* res) { + if (oldsub.empty()) { + res->append(s); // if empty, append the given string. + return; + } + + string::size_type start_pos = 0; + string::size_type pos; + do { + pos = s.find(oldsub, start_pos); + if (pos == string::npos) { + break; + } + res->append(s, start_pos, pos - start_pos); + res->append(newsub); + start_pos = pos + oldsub.size(); // start searching again after the "old" + } while (replace_all); + res->append(s, start_pos, s.length() - start_pos); +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Give me a string and two patterns "old" and "new", and I replace +// the first instance of "old" in the string with "new", if it +// exists. If "global" is true; call this repeatedly until it +// fails. RETURN a new string, regardless of whether the replacement +// happened or not. +// ---------------------------------------------------------------------- + +string StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all) { + string ret; + StringReplace(s, oldsub, newsub, replace_all, &ret); + return ret; +} + +// ---------------------------------------------------------------------- +// SplitStringUsing() +// Split a string using a character delimiter. Append the components +// to 'result'. +// +// Note: For multi-character delimiters, this routine will split on *ANY* of +// the characters in the string, not the entire string as a single delimiter. +// ---------------------------------------------------------------------- +template +static inline +void SplitStringToIteratorUsing(const string& full, + const char* delim, + ITR& result) { + // Optimize the common case where delim is a single character. + if (delim[0] != '\0' && delim[1] == '\0') { + char c = delim[0]; + const char* p = full.data(); + const char* end = p + full.size(); + while (p != end) { + if (*p == c) { + ++p; + } else { + const char* start = p; + while (++p != end && *p != c); + *result++ = string(start, p - start); + } + } + return; + } + + string::size_type begin_index, end_index; + begin_index = full.find_first_not_of(delim); + while (begin_index != string::npos) { + end_index = full.find_first_of(delim, begin_index); + if (end_index == string::npos) { + *result++ = full.substr(begin_index); + return; + } + *result++ = full.substr(begin_index, (end_index - begin_index)); + begin_index = full.find_first_not_of(delim, end_index); + } +} + +void SplitStringUsing(const string& full, + const char* delim, + vector* result) { + back_insert_iterator< vector > it(*result); + SplitStringToIteratorUsing(full, delim, it); +} + +// ---------------------------------------------------------------------- +// JoinStrings() +// This merges a vector of string components with delim inserted +// as separaters between components. +// +// ---------------------------------------------------------------------- +template +static void JoinStringsIterator(const ITERATOR& start, + const ITERATOR& end, + const char* delim, + string* result) { + GOOGLE_CHECK(result != NULL); + result->clear(); + int delim_length = strlen(delim); + + // Precompute resulting length so we can reserve() memory in one shot. + int length = 0; + for (ITERATOR iter = start; iter != end; ++iter) { + if (iter != start) { + length += delim_length; + } + length += iter->size(); + } + result->reserve(length); + + // Now combine everything. + for (ITERATOR iter = start; iter != end; ++iter) { + if (iter != start) { + result->append(delim, delim_length); + } + result->append(iter->data(), iter->size()); + } +} + +void JoinStrings(const vector& components, + const char* delim, + string * result) { + JoinStringsIterator(components.begin(), components.end(), delim, result); +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeSequences() +// This does all the unescaping that C does: \ooo, \r, \n, etc +// Returns length of resulting string. +// The implementation of \x parses any positive number of hex digits, +// but it is an error if the value requires more than 8 bits, and the +// result is truncated to 8 bits. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// ---------------------------------------------------------------------- + +#define IS_OCTAL_DIGIT(c) (((c) >= '0') && ((c) <= '7')) + +inline int hex_digit_to_int(char c) { + /* Assume ASCII. */ + assert('0' == 0x30 && 'A' == 0x41 && 'a' == 0x61); + assert(isxdigit(c)); + int x = static_cast(c); + if (x > '9') { + x += 9; + } + return x & 0xf; +} + +// Protocol buffers doesn't ever care about errors, but I don't want to remove +// the code. +#define LOG_STRING(LEVEL, VECTOR) GOOGLE_LOG_IF(LEVEL, false) + +int UnescapeCEscapeSequences(const char* source, char* dest) { + return UnescapeCEscapeSequences(source, dest, NULL); +} + +int UnescapeCEscapeSequences(const char* source, char* dest, + vector *errors) { + GOOGLE_DCHECK(errors == NULL) << "Error reporting not implemented."; + + char* d = dest; + const char* p = source; + + // Small optimization for case where source = dest and there's no escaping + while ( p == d && *p != '\0' && *p != '\\' ) + p++, d++; + + while (*p != '\0') { + if (*p != '\\') { + *d++ = *p++; + } else { + switch ( *++p ) { // skip past the '\\' + case '\0': + LOG_STRING(ERROR, errors) << "String cannot end with \\"; + *d = '\0'; + return d - dest; // we're done with p + case 'a': *d++ = '\a'; break; + case 'b': *d++ = '\b'; break; + case 'f': *d++ = '\f'; break; + case 'n': *d++ = '\n'; break; + case 'r': *d++ = '\r'; break; + case 't': *d++ = '\t'; break; + case 'v': *d++ = '\v'; break; + case '\\': *d++ = '\\'; break; + case '?': *d++ = '\?'; break; // \? Who knew? + case '\'': *d++ = '\''; break; + case '"': *d++ = '\"'; break; + case '0': case '1': case '2': case '3': // octal digit: 1 to 3 digits + case '4': case '5': case '6': case '7': { + char ch = *p - '0'; + if ( IS_OCTAL_DIGIT(p[1]) ) + ch = ch * 8 + *++p - '0'; + if ( IS_OCTAL_DIGIT(p[1]) ) // safe (and easy) to do this twice + ch = ch * 8 + *++p - '0'; // now points at last digit + *d++ = ch; + break; + } + case 'x': case 'X': { + if (!isxdigit(p[1])) { + if (p[1] == '\0') { + LOG_STRING(ERROR, errors) << "String cannot end with \\x"; + } else { + LOG_STRING(ERROR, errors) << + "\\x cannot be followed by non-hex digit: \\" << *p << p[1]; + } + break; + } + unsigned int ch = 0; + const char *hex_start = p; + while (isxdigit(p[1])) // arbitrarily many hex digits + ch = (ch << 4) + hex_digit_to_int(*++p); + if (ch > 0xFF) + LOG_STRING(ERROR, errors) << "Value of " << + "\\" << string(hex_start, p+1-hex_start) << " exceeds 8 bits"; + *d++ = ch; + break; + } +#if 0 // TODO(kenton): Support \u and \U? Requires runetochar(). + case 'u': { + // \uhhhh => convert 4 hex digits to UTF-8 + char32 rune = 0; + const char *hex_start = p; + for (int i = 0; i < 4; ++i) { + if (isxdigit(p[1])) { // Look one char ahead. + rune = (rune << 4) + hex_digit_to_int(*++p); // Advance p. + } else { + LOG_STRING(ERROR, errors) + << "\\u must be followed by 4 hex digits: \\" + << string(hex_start, p+1-hex_start); + break; + } + } + d += runetochar(d, &rune); + break; + } + case 'U': { + // \Uhhhhhhhh => convert 8 hex digits to UTF-8 + char32 rune = 0; + const char *hex_start = p; + for (int i = 0; i < 8; ++i) { + if (isxdigit(p[1])) { // Look one char ahead. + // Don't change rune until we're sure this + // is within the Unicode limit, but do advance p. + char32 newrune = (rune << 4) + hex_digit_to_int(*++p); + if (newrune > 0x10FFFF) { + LOG_STRING(ERROR, errors) + << "Value of \\" + << string(hex_start, p + 1 - hex_start) + << " exceeds Unicode limit (0x10FFFF)"; + break; + } else { + rune = newrune; + } + } else { + LOG_STRING(ERROR, errors) + << "\\U must be followed by 8 hex digits: \\" + << string(hex_start, p+1-hex_start); + break; + } + } + d += runetochar(d, &rune); + break; + } +#endif + default: + LOG_STRING(ERROR, errors) << "Unknown escape sequence: \\" << *p; + } + p++; // read past letter we escaped + } + } + *d = '\0'; + return d - dest; +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeString() +// This does the same thing as UnescapeCEscapeSequences, but creates +// a new string. The caller does not need to worry about allocating +// a dest buffer. This should be used for non performance critical +// tasks such as printing debug messages. It is safe for src and dest +// to be the same. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// +// In the first and second calls, the length of dest is returned. In the +// the third call, the new string is returned. +// ---------------------------------------------------------------------- +int UnescapeCEscapeString(const string& src, string* dest) { + return UnescapeCEscapeString(src, dest, NULL); +} + +int UnescapeCEscapeString(const string& src, string* dest, + vector *errors) { + scoped_array unescaped(new char[src.size() + 1]); + int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), errors); + GOOGLE_CHECK(dest); + dest->assign(unescaped.get(), len); + return len; +} + +string UnescapeCEscapeString(const string& src) { + scoped_array unescaped(new char[src.size() + 1]); + int len = UnescapeCEscapeSequences(src.c_str(), unescaped.get(), NULL); + return string(unescaped.get(), len); +} + +// ---------------------------------------------------------------------- +// CEscapeString() +// CHexEscapeString() +// Copies 'src' to 'dest', escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. The 'Hex' version uses +// hexadecimal rather than octal sequences. +// Returns the number of bytes written to 'dest' (not including the \0) +// or -1 if there was insufficient space. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +static int CEscapeInternal(const char* src, int src_len, char* dest, + int dest_len, bool use_hex) { + const char* src_end = src + src_len; + int used = 0; + bool last_hex_escape = false; // true if last output char was \xNN + + for (; src < src_end; src++) { + if (dest_len - used < 2) // Need space for two letter escape + return -1; + + bool is_hex_escape = false; + switch (*src) { + case '\n': dest[used++] = '\\'; dest[used++] = 'n'; break; + case '\r': dest[used++] = '\\'; dest[used++] = 'r'; break; + case '\t': dest[used++] = '\\'; dest[used++] = 't'; break; + case '\"': dest[used++] = '\\'; dest[used++] = '\"'; break; + case '\'': dest[used++] = '\\'; dest[used++] = '\''; break; + case '\\': dest[used++] = '\\'; dest[used++] = '\\'; break; + default: + // Note that if we emit \xNN and the src character after that is a hex + // digit then that digit must be escaped too to prevent it being + // interpreted as part of the character code by C. + if (!isprint(*src) || (last_hex_escape && isxdigit(*src))) { + if (dest_len - used < 4) // need space for 4 letter escape + return -1; + sprintf(dest + used, (use_hex ? "\\x%02x" : "\\%03o"), + static_cast(*src)); + is_hex_escape = use_hex; + used += 4; + } else { + dest[used++] = *src; break; + } + } + last_hex_escape = is_hex_escape; + } + + if (dest_len - used < 1) // make sure that there is room for \0 + return -1; + + dest[used] = '\0'; // doesn't count towards return value though + return used; +} + +int CEscapeString(const char* src, int src_len, char* dest, int dest_len) { + return CEscapeInternal(src, src_len, dest, dest_len, false); +} + +// ---------------------------------------------------------------------- +// CEscape() +// CHexEscape() +// Copies 'src' to result, escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. The 'Hex' version +// hexadecimal rather than octal sequences. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +string CEscape(const string& src) { + const int dest_length = src.size() * 4 + 1; // Maximum possible expansion + scoped_array dest(new char[dest_length]); + const int len = CEscapeInternal(src.data(), src.size(), + dest.get(), dest_length, false); + GOOGLE_DCHECK_GE(len, 0); + return string(dest.get(), len); +} + +// ---------------------------------------------------------------------- +// strto32_adaptor() +// strtou32_adaptor() +// Implementation of strto[u]l replacements that have identical +// overflow and underflow characteristics for both ILP-32 and LP-64 +// platforms, including errno preservation in error-free calls. +// ---------------------------------------------------------------------- + +int32 strto32_adaptor(const char *nptr, char **endptr, int base) { + const int saved_errno = errno; + errno = 0; + const long result = strtol(nptr, endptr, base); + if (errno == ERANGE && result == LONG_MIN) { + return kint32min; + } else if (errno == ERANGE && result == LONG_MAX) { + return kint32max; + } else if (errno == 0 && result < kint32min) { + errno = ERANGE; + return kint32min; + } else if (errno == 0 && result > kint32max) { + errno = ERANGE; + return kint32max; + } + if (errno == 0) + errno = saved_errno; + return static_cast(result); +} + +uint32 strtou32_adaptor(const char *nptr, char **endptr, int base) { + const int saved_errno = errno; + errno = 0; + const unsigned long result = strtoul(nptr, endptr, base); + if (errno == ERANGE && result == ULONG_MAX) { + return kuint32max; + } else if (errno == 0 && result > kuint32max) { + errno = ERANGE; + return kuint32max; + } + if (errno == 0) + errno = saved_errno; + return static_cast(result); +} + +// ---------------------------------------------------------------------- +// FastIntToBuffer() +// FastInt64ToBuffer() +// FastHexToBuffer() +// FastHex64ToBuffer() +// FastHex32ToBuffer() +// ---------------------------------------------------------------------- + +// Offset into buffer where FastInt64ToBuffer places the end of string +// null character. Also used by FastInt64ToBufferLeft. +static const int kFastInt64ToBufferOffset = 21; + +char *FastInt64ToBuffer(int64 i, char* buffer) { + // We could collapse the positive and negative sections, but that + // would be slightly slower for positive numbers... + // 22 bytes is enough to store -2**64, -18446744073709551616. + char* p = buffer + kFastInt64ToBufferOffset; + *p-- = '\0'; + if (i >= 0) { + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; + } else { + // On different platforms, % and / have different behaviors for + // negative numbers, so we need to jump through hoops to make sure + // we don't divide negative numbers. + if (i > -10) { + i = -i; + *p-- = '0' + i; + *p = '-'; + return p; + } else { + // Make sure we aren't at MIN_INT, in which case we can't say i = -i + i = i + 10; + i = -i; + *p-- = '0' + i % 10; + // Undo what we did a moment ago + i = i / 10 + 1; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + *p = '-'; + return p; + } + } +} + +// Offset into buffer where FastInt32ToBuffer places the end of string +// null character. Also used by FastInt32ToBufferLeft +static const int kFastInt32ToBufferOffset = 11; + +// Yes, this is a duplicate of FastInt64ToBuffer. But, we need this for the +// compiler to generate 32 bit arithmetic instructions. It's much faster, at +// least with 32 bit binaries. +char *FastInt32ToBuffer(int32 i, char* buffer) { + // We could collapse the positive and negative sections, but that + // would be slightly slower for positive numbers... + // 12 bytes is enough to store -2**32, -4294967296. + char* p = buffer + kFastInt32ToBufferOffset; + *p-- = '\0'; + if (i >= 0) { + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + return p + 1; + } else { + // On different platforms, % and / have different behaviors for + // negative numbers, so we need to jump through hoops to make sure + // we don't divide negative numbers. + if (i > -10) { + i = -i; + *p-- = '0' + i; + *p = '-'; + return p; + } else { + // Make sure we aren't at MIN_INT, in which case we can't say i = -i + i = i + 10; + i = -i; + *p-- = '0' + i % 10; + // Undo what we did a moment ago + i = i / 10 + 1; + do { + *p-- = '0' + i % 10; + i /= 10; + } while (i > 0); + *p = '-'; + return p; + } + } +} + +char *FastHexToBuffer(int i, char* buffer) { + GOOGLE_CHECK(i >= 0) << "FastHexToBuffer() wants non-negative integers, not " << i; + + static const char *hexdigits = "0123456789abcdef"; + char *p = buffer + 21; + *p-- = '\0'; + do { + *p-- = hexdigits[i & 15]; // mod by 16 + i >>= 4; // divide by 16 + } while (i > 0); + return p + 1; +} + +char *InternalFastHexToBuffer(uint64 value, char* buffer, int num_byte) { + static const char *hexdigits = "0123456789abcdef"; + buffer[num_byte] = '\0'; + for (int i = num_byte - 1; i >= 0; i--) { + buffer[i] = hexdigits[uint32(value) & 0xf]; + value >>= 4; + } + return buffer; +} + +char *FastHex64ToBuffer(uint64 value, char* buffer) { + return InternalFastHexToBuffer(value, buffer, 16); +} + +char *FastHex32ToBuffer(uint32 value, char* buffer) { + return InternalFastHexToBuffer(value, buffer, 8); +} + +static inline char* PlaceNum(char* p, int num, char prev_sep) { + *p-- = '0' + num % 10; + *p-- = '0' + num / 10; + *p-- = prev_sep; + return p; +} + +// ---------------------------------------------------------------------- +// FastInt32ToBufferLeft() +// FastUInt32ToBufferLeft() +// FastInt64ToBufferLeft() +// FastUInt64ToBufferLeft() +// +// Like the Fast*ToBuffer() functions above, these are intended for speed. +// Unlike the Fast*ToBuffer() functions, however, these functions write +// their output to the beginning of the buffer (hence the name, as the +// output is left-aligned). The caller is responsible for ensuring that +// the buffer has enough space to hold the output. +// +// Returns a pointer to the end of the string (i.e. the null character +// terminating the string). +// ---------------------------------------------------------------------- + +static const char two_ASCII_digits[100][2] = { + {'0','0'}, {'0','1'}, {'0','2'}, {'0','3'}, {'0','4'}, + {'0','5'}, {'0','6'}, {'0','7'}, {'0','8'}, {'0','9'}, + {'1','0'}, {'1','1'}, {'1','2'}, {'1','3'}, {'1','4'}, + {'1','5'}, {'1','6'}, {'1','7'}, {'1','8'}, {'1','9'}, + {'2','0'}, {'2','1'}, {'2','2'}, {'2','3'}, {'2','4'}, + {'2','5'}, {'2','6'}, {'2','7'}, {'2','8'}, {'2','9'}, + {'3','0'}, {'3','1'}, {'3','2'}, {'3','3'}, {'3','4'}, + {'3','5'}, {'3','6'}, {'3','7'}, {'3','8'}, {'3','9'}, + {'4','0'}, {'4','1'}, {'4','2'}, {'4','3'}, {'4','4'}, + {'4','5'}, {'4','6'}, {'4','7'}, {'4','8'}, {'4','9'}, + {'5','0'}, {'5','1'}, {'5','2'}, {'5','3'}, {'5','4'}, + {'5','5'}, {'5','6'}, {'5','7'}, {'5','8'}, {'5','9'}, + {'6','0'}, {'6','1'}, {'6','2'}, {'6','3'}, {'6','4'}, + {'6','5'}, {'6','6'}, {'6','7'}, {'6','8'}, {'6','9'}, + {'7','0'}, {'7','1'}, {'7','2'}, {'7','3'}, {'7','4'}, + {'7','5'}, {'7','6'}, {'7','7'}, {'7','8'}, {'7','9'}, + {'8','0'}, {'8','1'}, {'8','2'}, {'8','3'}, {'8','4'}, + {'8','5'}, {'8','6'}, {'8','7'}, {'8','8'}, {'8','9'}, + {'9','0'}, {'9','1'}, {'9','2'}, {'9','3'}, {'9','4'}, + {'9','5'}, {'9','6'}, {'9','7'}, {'9','8'}, {'9','9'} +}; + +char* FastUInt32ToBufferLeft(uint32 u, char* buffer) { + int digits; + const char *ASCII_digits = NULL; + // The idea of this implementation is to trim the number of divides to as few + // as possible by using multiplication and subtraction rather than mod (%), + // and by outputting two digits at a time rather than one. + // The huge-number case is first, in the hopes that the compiler will output + // that case in one branch-free block of code, and only output conditional + // branches into it from below. + if (u >= 1000000000) { // >= 1,000,000,000 + digits = u / 100000000; // 100,000,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt100_000_000: + u -= digits * 100000000; // 100,000,000 +lt100_000_000: + digits = u / 1000000; // 1,000,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt1_000_000: + u -= digits * 1000000; // 1,000,000 +lt1_000_000: + digits = u / 10000; // 10,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt10_000: + u -= digits * 10000; // 10,000 +lt10_000: + digits = u / 100; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +sublt100: + u -= digits * 100; +lt100: + digits = u; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; +done: + *buffer = 0; + return buffer; + } + + if (u < 100) { + digits = u; + if (u >= 10) goto lt100; + *buffer++ = '0' + digits; + goto done; + } + if (u < 10000) { // 10,000 + if (u >= 1000) goto lt10_000; + digits = u / 100; + *buffer++ = '0' + digits; + goto sublt100; + } + if (u < 1000000) { // 1,000,000 + if (u >= 100000) goto lt1_000_000; + digits = u / 10000; // 10,000 + *buffer++ = '0' + digits; + goto sublt10_000; + } + if (u < 100000000) { // 100,000,000 + if (u >= 10000000) goto lt100_000_000; + digits = u / 1000000; // 1,000,000 + *buffer++ = '0' + digits; + goto sublt1_000_000; + } + // we already know that u < 1,000,000,000 + digits = u / 100000000; // 100,000,000 + *buffer++ = '0' + digits; + goto sublt100_000_000; +} + +char* FastInt32ToBufferLeft(int32 i, char* buffer) { + uint32 u = i; + if (i < 0) { + *buffer++ = '-'; + u = -i; + } + return FastUInt32ToBufferLeft(u, buffer); +} + +char* FastUInt64ToBufferLeft(uint64 u64, char* buffer) { + int digits; + const char *ASCII_digits = NULL; + + uint32 u = static_cast(u64); + if (u == u64) return FastUInt32ToBufferLeft(u, buffer); + + uint64 top_11_digits = u64 / 1000000000; + buffer = FastUInt64ToBufferLeft(top_11_digits, buffer); + u = u64 - (top_11_digits * 1000000000); + + digits = u / 10000000; // 10,000,000 + GOOGLE_DCHECK_LT(digits, 100); + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 10000000; // 10,000,000 + digits = u / 100000; // 100,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 100000; // 100,000 + digits = u / 1000; // 1,000 + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 1000; // 1,000 + digits = u / 10; + ASCII_digits = two_ASCII_digits[digits]; + buffer[0] = ASCII_digits[0]; + buffer[1] = ASCII_digits[1]; + buffer += 2; + u -= digits * 10; + digits = u; + *buffer++ = '0' + digits; + *buffer = 0; + return buffer; +} + +char* FastInt64ToBufferLeft(int64 i, char* buffer) { + uint64 u = i; + if (i < 0) { + *buffer++ = '-'; + u = -i; + } + return FastUInt64ToBufferLeft(u, buffer); +} + +// ---------------------------------------------------------------------- +// SimpleItoa() +// Description: converts an integer to a string. +// +// Return value: string +// ---------------------------------------------------------------------- + +string SimpleItoa(int i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned int i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +string SimpleItoa(long i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned long i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +string SimpleItoa(long long i) { + char buffer[kFastToBufferSize]; + return (sizeof(i) == 4) ? + FastInt32ToBuffer(i, buffer) : + FastInt64ToBuffer(i, buffer); +} + +string SimpleItoa(unsigned long long i) { + char buffer[kFastToBufferSize]; + return string(buffer, (sizeof(i) == 4) ? + FastUInt32ToBufferLeft(i, buffer) : + FastUInt64ToBufferLeft(i, buffer)); +} + +// ---------------------------------------------------------------------- +// SimpleDtoa() +// SimpleFtoa() +// DoubleToBuffer() +// FloatToBuffer() +// We want to print the value without losing precision, but we also do +// not want to print more digits than necessary. This turns out to be +// trickier than it sounds. Numbers like 0.2 cannot be represented +// exactly in binary. If we print 0.2 with a very large precision, +// e.g. "%.50g", we get "0.2000000000000000111022302462515654042363167". +// On the other hand, if we set the precision too low, we lose +// significant digits when printing numbers that actually need them. +// It turns out there is no precision value that does the right thing +// for all numbers. +// +// Our strategy is to first try printing with a precision that is never +// over-precise, then parse the result with strtod() to see if it +// matches. If not, we print again with a precision that will always +// give a precise result, but may use more digits than necessary. +// +// An arguably better strategy would be to use the algorithm described +// in "How to Print Floating-Point Numbers Accurately" by Steele & +// White, e.g. as implemented by David M. Gay's dtoa(). It turns out, +// however, that the following implementation is about as fast as +// DMG's code. Furthermore, DMG's code locks mutexes, which means it +// will not scale well on multi-core machines. DMG's code is slightly +// more accurate (in that it will never use more digits than +// necessary), but this is probably irrelevant for most users. +// +// Rob Pike and Ken Thompson also have an implementation of dtoa() in +// third_party/fmt/fltfmt.cc. Their implementation is similar to this +// one in that it makes guesses and then uses strtod() to check them. +// Their implementation is faster because they use their own code to +// generate the digits in the first place rather than use snprintf(), +// thus avoiding format string parsing overhead. However, this makes +// it considerably more complicated than the following implementation, +// and it is embedded in a larger library. If speed turns out to be +// an issue, we could re-implement this in terms of their +// implementation. +// ---------------------------------------------------------------------- + +string SimpleDtoa(double value) { + char buffer[kDoubleToBufferSize]; + return DoubleToBuffer(value, buffer); +} + +string SimpleFtoa(float value) { + char buffer[kFloatToBufferSize]; + return FloatToBuffer(value, buffer); +} + +static inline bool IsValidFloatChar(char c) { + return ('0' <= c && c <= '9') || + c == 'e' || c == 'E' || + c == '+' || c == '-'; +} + +void DelocalizeRadix(char* buffer) { + // Fast check: if the buffer has a normal decimal point, assume no + // translation is needed. + if (strchr(buffer, '.') != NULL) return; + + // Find the first unknown character. + while (IsValidFloatChar(*buffer)) ++buffer; + + if (*buffer == '\0') { + // No radix character found. + return; + } + + // We are now pointing at the locale-specific radix character. Replace it + // with '.'. + *buffer = '.'; + ++buffer; + + if (!IsValidFloatChar(*buffer) && *buffer != '\0') { + // It appears the radix was a multi-byte character. We need to remove the + // extra bytes. + char* target = buffer; + do { ++buffer; } while (!IsValidFloatChar(*buffer) && *buffer != '\0'); + memmove(target, buffer, strlen(buffer) + 1); + } +} + +char* DoubleToBuffer(double value, char* buffer) { + // DBL_DIG is 15 for IEEE-754 doubles, which are used on almost all + // platforms these days. Just in case some system exists where DBL_DIG + // is significantly larger -- and risks overflowing our buffer -- we have + // this assert. + GOOGLE_COMPILE_ASSERT(DBL_DIG < 20, DBL_DIG_is_too_big); + + if (value == numeric_limits::infinity()) { + strcpy(buffer, "inf"); + return buffer; + } else if (value == -numeric_limits::infinity()) { + strcpy(buffer, "-inf"); + return buffer; + } else if (IsNaN(value)) { + strcpy(buffer, "nan"); + return buffer; + } + + int snprintf_result = + snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG, value); + + // The snprintf should never overflow because the buffer is significantly + // larger than the precision we asked for. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); + + // We need to make parsed_value volatile in order to force the compiler to + // write it out to the stack. Otherwise, it may keep the value in a + // register, and if it does that, it may keep it as a long double instead + // of a double. This long double may have extra bits that make it compare + // unequal to "value" even though it would be exactly equal if it were + // truncated to a double. + volatile double parsed_value = strtod(buffer, NULL); + if (parsed_value != value) { + int snprintf_result = + snprintf(buffer, kDoubleToBufferSize, "%.*g", DBL_DIG+2, value); + + // Should never overflow; see above. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kDoubleToBufferSize); + } + + DelocalizeRadix(buffer); + return buffer; +} + +bool safe_strtof(const char* str, float* value) { + char* endptr; + errno = 0; // errno only gets set on errors +#ifdef _WIN32 // has no strtof() + *value = strtod(str, &endptr); +#else + *value = strtof(str, &endptr); +#endif + return *str != 0 && *endptr == 0 && errno == 0; +} + +char* FloatToBuffer(float value, char* buffer) { + // FLT_DIG is 6 for IEEE-754 floats, which are used on almost all + // platforms these days. Just in case some system exists where FLT_DIG + // is significantly larger -- and risks overflowing our buffer -- we have + // this assert. + GOOGLE_COMPILE_ASSERT(FLT_DIG < 10, FLT_DIG_is_too_big); + + if (value == numeric_limits::infinity()) { + strcpy(buffer, "inf"); + return buffer; + } else if (value == -numeric_limits::infinity()) { + strcpy(buffer, "-inf"); + return buffer; + } else if (IsNaN(value)) { + strcpy(buffer, "nan"); + return buffer; + } + + int snprintf_result = + snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG, value); + + // The snprintf should never overflow because the buffer is significantly + // larger than the precision we asked for. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); + + float parsed_value; + if (!safe_strtof(buffer, &parsed_value) || parsed_value != value) { + int snprintf_result = + snprintf(buffer, kFloatToBufferSize, "%.*g", FLT_DIG+2, value); + + // Should never overflow; see above. + GOOGLE_DCHECK(snprintf_result > 0 && snprintf_result < kFloatToBufferSize); + } + + DelocalizeRadix(buffer); + return buffer; +} + +// ---------------------------------------------------------------------- +// NoLocaleStrtod() +// This code will make you cry. +// ---------------------------------------------------------------------- + +// Returns a string identical to *input except that the character pointed to +// by radix_pos (which should be '.') is replaced with the locale-specific +// radix character. +string LocalizeRadix(const char* input, const char* radix_pos) { + // Determine the locale-specific radix character by calling sprintf() to + // print the number 1.5, then stripping off the digits. As far as I can + // tell, this is the only portable, thread-safe way to get the C library + // to divuldge the locale's radix character. No, localeconv() is NOT + // thread-safe. + char temp[16]; + int size = sprintf(temp, "%.1f", 1.5); + GOOGLE_CHECK_EQ(temp[0], '1'); + GOOGLE_CHECK_EQ(temp[size-1], '5'); + GOOGLE_CHECK_LE(size, 6); + + // Now replace the '.' in the input with it. + string result; + result.reserve(strlen(input) + size - 3); + result.append(input, radix_pos); + result.append(temp + 1, size - 2); + result.append(radix_pos + 1); + return result; +} + +double NoLocaleStrtod(const char* text, char** original_endptr) { + // We cannot simply set the locale to "C" temporarily with setlocale() + // as this is not thread-safe. Instead, we try to parse in the current + // locale first. If parsing stops at a '.' character, then this is a + // pretty good hint that we're actually in some other locale in which + // '.' is not the radix character. + + char* temp_endptr; + double result = strtod(text, &temp_endptr); + if (original_endptr != NULL) *original_endptr = temp_endptr; + if (*temp_endptr != '.') return result; + + // Parsing halted on a '.'. Perhaps we're in a different locale? Let's + // try to replace the '.' with a locale-specific radix character and + // try again. + string localized = LocalizeRadix(text, temp_endptr); + const char* localized_cstr = localized.c_str(); + char* localized_endptr; + result = strtod(localized_cstr, &localized_endptr); + if ((localized_endptr - localized_cstr) > + (temp_endptr - text)) { + // This attempt got further, so replacing the decimal must have helped. + // Update original_endptr to point at the right location. + if (original_endptr != NULL) { + // size_diff is non-zero if the localized radix has multiple bytes. + int size_diff = localized.size() - strlen(text); + // const_cast is necessary to match the strtod() interface. + *original_endptr = const_cast( + text + (localized_endptr - localized_cstr - size_diff)); + } + } + + return result; +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/strutil.h b/src/google/protobuf/stubs/strutil.h new file mode 100644 index 00000000..ff919617 --- /dev/null +++ b/src/google/protobuf/stubs/strutil.h @@ -0,0 +1,432 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// from google3/strings/strutil.h + +#ifndef GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ +#define GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ + +#include +#include + +namespace google { +namespace protobuf { + +#ifdef _MSC_VER +#define strtoll _strtoi64 +#define strtoull _strtoui64 +#endif + +// ---------------------------------------------------------------------- +// ascii_isalnum() +// Check if an ASCII character is alphanumeric. We can't use ctype's +// isalnum() because it is affected by locale. This function is applied +// to identifiers in the protocol buffer language, not to natural-language +// strings, so locale should not be taken into account. +// ascii_isdigit() +// Like above, but only accepts digits. +// ---------------------------------------------------------------------- + +inline bool ascii_isalnum(char c) { + return ('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9'); +} + +inline bool ascii_isdigit(char c) { + return ('0' <= c && c <= '9'); +} + +// ---------------------------------------------------------------------- +// HasPrefixString() +// Check if a string begins with a given prefix. +// StripPrefixString() +// Given a string and a putative prefix, returns the string minus the +// prefix string if the prefix matches, otherwise the original +// string. +// ---------------------------------------------------------------------- +inline bool HasPrefixString(const string& str, + const string& prefix) { + return str.size() >= prefix.size() && + str.compare(0, prefix.size(), prefix) == 0; +} + +inline string StripPrefixString(const string& str, const string& prefix) { + if (HasPrefixString(str, prefix)) { + return str.substr(prefix.size()); + } else { + return str; + } +} + +// ---------------------------------------------------------------------- +// HasSuffixString() +// Return true if str ends in suffix. +// StripSuffixString() +// Given a string and a putative suffix, returns the string minus the +// suffix string if the suffix matches, otherwise the original +// string. +// ---------------------------------------------------------------------- +inline bool HasSuffixString(const string& str, + const string& suffix) { + return str.size() >= suffix.size() && + str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0; +} + +inline string StripSuffixString(const string& str, const string& suffix) { + if (HasSuffixString(str, suffix)) { + return str.substr(0, str.size() - suffix.size()); + } else { + return str; + } +} + +// ---------------------------------------------------------------------- +// StripString +// Replaces any occurrence of the character 'remove' (or the characters +// in 'remove') with the character 'replacewith'. +// Good for keeping html characters or protocol characters (\t) out +// of places where they might cause a problem. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void StripString(string* s, const char* remove, + char replacewith); + +// ---------------------------------------------------------------------- +// LowerString() +// UpperString() +// Convert the characters in "s" to lowercase or uppercase. ASCII-only: +// these functions intentionally ignore locale because they are applied to +// identifiers used in the Protocol Buffer language, not to natural-language +// strings. +// ---------------------------------------------------------------------- + +inline void LowerString(string * s) { + string::iterator end = s->end(); + for (string::iterator i = s->begin(); i != end; ++i) { + // tolower() changes based on locale. We don't want this! + if ('A' <= *i && *i <= 'Z') *i += 'a' - 'A'; + } +} + +inline void UpperString(string * s) { + string::iterator end = s->end(); + for (string::iterator i = s->begin(); i != end; ++i) { + // toupper() changes based on locale. We don't want this! + if ('a' <= *i && *i <= 'z') *i += 'A' - 'a'; + } +} + +// ---------------------------------------------------------------------- +// StringReplace() +// Give me a string and two patterns "old" and "new", and I replace +// the first instance of "old" in the string with "new", if it +// exists. RETURN a new string, regardless of whether the replacement +// happened or not. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT string StringReplace(const string& s, const string& oldsub, + const string& newsub, bool replace_all); + +// ---------------------------------------------------------------------- +// SplitStringUsing() +// Split a string using a character delimiter. Append the components +// to 'result'. If there are consecutive delimiters, this function skips +// over all of them. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void SplitStringUsing(const string& full, const char* delim, + vector* res); + +// ---------------------------------------------------------------------- +// JoinStrings() +// These methods concatenate a vector of strings into a C++ string, using +// the C-string "delim" as a separator between components. There are two +// flavors of the function, one flavor returns the concatenated string, +// another takes a pointer to the target string. In the latter case the +// target string is cleared and overwritten. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT void JoinStrings(const vector& components, + const char* delim, string* result); + +inline string JoinStrings(const vector& components, + const char* delim) { + string result; + JoinStrings(components, delim, &result); + return result; +} + +// ---------------------------------------------------------------------- +// UnescapeCEscapeSequences() +// Copies "source" to "dest", rewriting C-style escape sequences +// -- '\n', '\r', '\\', '\ooo', etc -- to their ASCII +// equivalents. "dest" must be sufficiently large to hold all +// the characters in the rewritten string (i.e. at least as large +// as strlen(source) + 1 should be safe, since the replacements +// are always shorter than the original escaped sequences). It's +// safe for source and dest to be the same. RETURNS the length +// of dest. +// +// It allows hex sequences \xhh, or generally \xhhhhh with an +// arbitrary number of hex digits, but all of them together must +// specify a value of a single byte (e.g. \x0045 is equivalent +// to \x45, and \x1234 is erroneous). +// +// It also allows escape sequences of the form \uhhhh (exactly four +// hex digits, upper or lower case) or \Uhhhhhhhh (exactly eight +// hex digits, upper or lower case) to specify a Unicode code +// point. The dest array will contain the UTF8-encoded version of +// that code-point (e.g., if source contains \u2019, then dest will +// contain the three bytes 0xE2, 0x80, and 0x99). For the inverse +// transformation, use UniLib::UTF8EscapeString +// (util/utf8/unilib.h), not CEscapeString. +// +// Errors: In the first form of the call, errors are reported with +// LOG(ERROR). The same is true for the second form of the call if +// the pointer to the string vector is NULL; otherwise, error +// messages are stored in the vector. In either case, the effect on +// the dest array is not defined, but rest of the source will be +// processed. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest); +LIBPROTOBUF_EXPORT int UnescapeCEscapeSequences(const char* source, char* dest, + vector *errors); + +// ---------------------------------------------------------------------- +// UnescapeCEscapeString() +// This does the same thing as UnescapeCEscapeSequences, but creates +// a new string. The caller does not need to worry about allocating +// a dest buffer. This should be used for non performance critical +// tasks such as printing debug messages. It is safe for src and dest +// to be the same. +// +// The second call stores its errors in a supplied string vector. +// If the string vector pointer is NULL, it reports the errors with LOG(). +// +// In the first and second calls, the length of dest is returned. In the +// the third call, the new string is returned. +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest); +LIBPROTOBUF_EXPORT int UnescapeCEscapeString(const string& src, string* dest, + vector *errors); +LIBPROTOBUF_EXPORT string UnescapeCEscapeString(const string& src); + +// ---------------------------------------------------------------------- +// CEscapeString() +// Copies 'src' to 'dest', escaping dangerous characters using +// C-style escape sequences. This is very useful for preparing query +// flags. 'src' and 'dest' should not overlap. +// Returns the number of bytes written to 'dest' (not including the \0) +// or -1 if there was insufficient space. +// +// Currently only \n, \r, \t, ", ', \ and !isprint() chars are escaped. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT int CEscapeString(const char* src, int src_len, + char* dest, int dest_len); + +// ---------------------------------------------------------------------- +// CEscape() +// More convenient form of CEscapeString: returns result as a "string". +// This version is slower than CEscapeString() because it does more +// allocation. However, it is much more convenient to use in +// non-speed-critical code like logging messages etc. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string CEscape(const string& src); + +// ---------------------------------------------------------------------- +// strto32() +// strtou32() +// strto64() +// strtou64() +// Architecture-neutral plug compatible replacements for strtol() and +// strtoul(). Long's have different lengths on ILP-32 and LP-64 +// platforms, so using these is safer, from the point of view of +// overflow behavior, than using the standard libc functions. +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT int32 strto32_adaptor(const char *nptr, char **endptr, + int base); +LIBPROTOBUF_EXPORT uint32 strtou32_adaptor(const char *nptr, char **endptr, + int base); + +inline int32 strto32(const char *nptr, char **endptr, int base) { + if (sizeof(int32) == sizeof(long)) + return strtol(nptr, endptr, base); + else + return strto32_adaptor(nptr, endptr, base); +} + +inline uint32 strtou32(const char *nptr, char **endptr, int base) { + if (sizeof(uint32) == sizeof(unsigned long)) + return strtoul(nptr, endptr, base); + else + return strtou32_adaptor(nptr, endptr, base); +} + +// For now, long long is 64-bit on all the platforms we care about, so these +// functions can simply pass the call to strto[u]ll. +inline int64 strto64(const char *nptr, char **endptr, int base) { + GOOGLE_COMPILE_ASSERT(sizeof(int64) == sizeof(long long), + sizeof_int64_is_not_sizeof_long_long); + return strtoll(nptr, endptr, base); +} + +inline uint64 strtou64(const char *nptr, char **endptr, int base) { + GOOGLE_COMPILE_ASSERT(sizeof(uint64) == sizeof(unsigned long long), + sizeof_uint64_is_not_sizeof_long_long); + return strtoull(nptr, endptr, base); +} + +// ---------------------------------------------------------------------- +// FastIntToBuffer() +// FastHexToBuffer() +// FastHex64ToBuffer() +// FastHex32ToBuffer() +// FastTimeToBuffer() +// These are intended for speed. FastIntToBuffer() assumes the +// integer is non-negative. FastHexToBuffer() puts output in +// hex rather than decimal. FastTimeToBuffer() puts the output +// into RFC822 format. +// +// FastHex64ToBuffer() puts a 64-bit unsigned value in hex-format, +// padded to exactly 16 bytes (plus one byte for '\0') +// +// FastHex32ToBuffer() puts a 32-bit unsigned value in hex-format, +// padded to exactly 8 bytes (plus one byte for '\0') +// +// All functions take the output buffer as an arg. +// They all return a pointer to the beginning of the output, +// which may not be the beginning of the input buffer. +// ---------------------------------------------------------------------- + +// Suggested buffer size for FastToBuffer functions. Also works with +// DoubleToBuffer() and FloatToBuffer(). +static const int kFastToBufferSize = 32; + +LIBPROTOBUF_EXPORT char* FastInt32ToBuffer(int32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastInt64ToBuffer(int64 i, char* buffer); +char* FastUInt32ToBuffer(uint32 i, char* buffer); // inline below +char* FastUInt64ToBuffer(uint64 i, char* buffer); // inline below +LIBPROTOBUF_EXPORT char* FastHexToBuffer(int i, char* buffer); +LIBPROTOBUF_EXPORT char* FastHex64ToBuffer(uint64 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastHex32ToBuffer(uint32 i, char* buffer); + +// at least 22 bytes long +inline char* FastIntToBuffer(int i, char* buffer) { + return (sizeof(i) == 4 ? + FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer)); +} +inline char* FastUIntToBuffer(unsigned int i, char* buffer) { + return (sizeof(i) == 4 ? + FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer)); +} +inline char* FastLongToBuffer(long i, char* buffer) { + return (sizeof(i) == 4 ? + FastInt32ToBuffer(i, buffer) : FastInt64ToBuffer(i, buffer)); +} +inline char* FastULongToBuffer(unsigned long i, char* buffer) { + return (sizeof(i) == 4 ? + FastUInt32ToBuffer(i, buffer) : FastUInt64ToBuffer(i, buffer)); +} + +// ---------------------------------------------------------------------- +// FastInt32ToBufferLeft() +// FastUInt32ToBufferLeft() +// FastInt64ToBufferLeft() +// FastUInt64ToBufferLeft() +// +// Like the Fast*ToBuffer() functions above, these are intended for speed. +// Unlike the Fast*ToBuffer() functions, however, these functions write +// their output to the beginning of the buffer (hence the name, as the +// output is left-aligned). The caller is responsible for ensuring that +// the buffer has enough space to hold the output. +// +// Returns a pointer to the end of the string (i.e. the null character +// terminating the string). +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT char* FastInt32ToBufferLeft(int32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastUInt32ToBufferLeft(uint32 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastInt64ToBufferLeft(int64 i, char* buffer); +LIBPROTOBUF_EXPORT char* FastUInt64ToBufferLeft(uint64 i, char* buffer); + +// Just define these in terms of the above. +inline char* FastUInt32ToBuffer(uint32 i, char* buffer) { + FastUInt32ToBufferLeft(i, buffer); + return buffer; +} +inline char* FastUInt64ToBuffer(uint64 i, char* buffer) { + FastUInt64ToBufferLeft(i, buffer); + return buffer; +} + +// ---------------------------------------------------------------------- +// SimpleItoa() +// Description: converts an integer to a string. +// +// Return value: string +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string SimpleItoa(int i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned int i); +LIBPROTOBUF_EXPORT string SimpleItoa(long i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long i); +LIBPROTOBUF_EXPORT string SimpleItoa(long long i); +LIBPROTOBUF_EXPORT string SimpleItoa(unsigned long long i); + +// ---------------------------------------------------------------------- +// SimpleDtoa() +// SimpleFtoa() +// DoubleToBuffer() +// FloatToBuffer() +// Description: converts a double or float to a string which, if +// passed to NoLocaleStrtod(), will produce the exact same original double +// (except in case of NaN; all NaNs are considered the same value). +// We try to keep the string short but it's not guaranteed to be as +// short as possible. +// +// DoubleToBuffer() and FloatToBuffer() write the text to the given +// buffer and return it. The buffer must be at least +// kDoubleToBufferSize bytes for doubles and kFloatToBufferSize +// bytes for floats. kFastToBufferSize is also guaranteed to be large +// enough to hold either. +// +// Return value: string +// ---------------------------------------------------------------------- +LIBPROTOBUF_EXPORT string SimpleDtoa(double value); +LIBPROTOBUF_EXPORT string SimpleFtoa(float value); + +LIBPROTOBUF_EXPORT char* DoubleToBuffer(double i, char* buffer); +LIBPROTOBUF_EXPORT char* FloatToBuffer(float i, char* buffer); + +// In practice, doubles should never need more than 24 bytes and floats +// should never need more than 14 (including null terminators), but we +// overestimate to be safe. +static const int kDoubleToBufferSize = 32; +static const int kFloatToBufferSize = 24; + +// ---------------------------------------------------------------------- +// NoLocaleStrtod() +// Exactly like strtod(), except it always behaves as if in the "C" +// locale (i.e. decimal points must be '.'s). +// ---------------------------------------------------------------------- + +LIBPROTOBUF_EXPORT double NoLocaleStrtod(const char* text, char** endptr); + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_STRUTIL_H__ + + diff --git a/src/google/protobuf/stubs/strutil_unittest.cc b/src/google/protobuf/stubs/strutil_unittest.cc new file mode 100644 index 00000000..58ffd32e --- /dev/null +++ b/src/google/protobuf/stubs/strutil_unittest.cc @@ -0,0 +1,68 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include + +#include +#include + +namespace google { +namespace protobuf { +namespace { + +// TODO(kenton): Copy strutil tests from google3? + +TEST(StringUtilityTest, ImmuneToLocales) { + // Remember the old locale. + char* old_locale_cstr = setlocale(LC_NUMERIC, NULL); + ASSERT_TRUE(old_locale_cstr != NULL); + string old_locale = old_locale_cstr; + + // Set the locale to "C". + ASSERT_TRUE(setlocale(LC_NUMERIC, "C") != NULL); + + EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL)); + EXPECT_EQ("1.5", SimpleDtoa(1.5)); + EXPECT_EQ("1.5", SimpleFtoa(1.5)); + + // Verify that the endptr is set correctly even if not all text was parsed. + const char* text = "1.5f"; + char* endptr; + EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr)); + EXPECT_EQ(3, endptr - text); + + if (setlocale(LC_NUMERIC, "es_ES") == NULL && + setlocale(LC_NUMERIC, "es_ES.utf8") == NULL) { + // Some systems may not have the desired locale available. + GOOGLE_LOG(WARNING) + << "Couldn't set locale to es_ES. Skipping this test."; + } else { + EXPECT_EQ(1.5, NoLocaleStrtod("1.5", NULL)); + EXPECT_EQ("1.5", SimpleDtoa(1.5)); + EXPECT_EQ("1.5", SimpleFtoa(1.5)); + EXPECT_EQ(1.5, NoLocaleStrtod(text, &endptr)); + EXPECT_EQ(3, endptr - text); + } + + // Return to original locale. + setlocale(LC_NUMERIC, old_locale.c_str()); +} + +} // anonymous namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/substitute.cc b/src/google/protobuf/stubs/substitute.cc new file mode 100644 index 00000000..340be5e8 --- /dev/null +++ b/src/google/protobuf/stubs/substitute.cc @@ -0,0 +1,120 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace strings { + +using internal::SubstituteArg; + +// Returns the number of args in arg_array which were passed explicitly +// to Substitute(). +static int CountSubstituteArgs(const SubstituteArg* const* args_array) { + int count = 0; + while (args_array[count] != NULL && args_array[count]->size() != -1) { + ++count; + } + return count; +} + +string Substitute( + const char* format, + const SubstituteArg& arg0, const SubstituteArg& arg1, + const SubstituteArg& arg2, const SubstituteArg& arg3, + const SubstituteArg& arg4, const SubstituteArg& arg5, + const SubstituteArg& arg6, const SubstituteArg& arg7, + const SubstituteArg& arg8, const SubstituteArg& arg9) { + string result; + SubstituteAndAppend(&result, format, arg0, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8, arg9); + return result; +} + +void SubstituteAndAppend( + string* output, const char* format, + const SubstituteArg& arg0, const SubstituteArg& arg1, + const SubstituteArg& arg2, const SubstituteArg& arg3, + const SubstituteArg& arg4, const SubstituteArg& arg5, + const SubstituteArg& arg6, const SubstituteArg& arg7, + const SubstituteArg& arg8, const SubstituteArg& arg9) { + const SubstituteArg* const args_array[] = { + &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, NULL + }; + + // Determine total size needed. + int size = 0; + for (int i = 0; format[i] != '\0'; i++) { + if (format[i] == '$') { + if (ascii_isdigit(format[i+1])) { + int index = format[i+1] - '0'; + if (args_array[index]->size() == -1) { + GOOGLE_LOG(DFATAL) + << "strings::Substitute format string invalid: asked for \"$" + << index << "\", but only " << CountSubstituteArgs(args_array) + << " args were given. Full format string was: \"" + << CEscape(format) << "\"."; + return; + } + size += args_array[index]->size(); + ++i; // Skip next char. + } else if (format[i+1] == '$') { + ++size; + ++i; // Skip next char. + } else { + GOOGLE_LOG(DFATAL) + << "Invalid strings::Substitute() format string: \"" + << CEscape(format) << "\"."; + return; + } + } else { + ++size; + } + } + + if (size == 0) return; + + // Build the string. + int original_size = output->size(); + STLStringResizeUninitialized(output, original_size + size); + char* target = string_as_array(output) + original_size; + for (int i = 0; format[i] != '\0'; i++) { + if (format[i] == '$') { + if (ascii_isdigit(format[i+1])) { + const SubstituteArg* src = args_array[format[i+1] - '0']; + memcpy(target, src->data(), src->size()); + target += src->size(); + ++i; // Skip next char. + } else if (format[i+1] == '$') { + *target++ = '$'; + ++i; // Skip next char. + } + } else { + *target++ = format[i]; + } + } + + GOOGLE_DCHECK_EQ(target - output->data(), output->size()); +} + +} // namespace strings +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/stubs/substitute.h b/src/google/protobuf/stubs/substitute.h new file mode 100644 index 00000000..143e4828 --- /dev/null +++ b/src/google/protobuf/stubs/substitute.h @@ -0,0 +1,156 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// from google3/strings/substitute.h + +#include +#include +#include + +#ifndef GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ +#define GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ + +namespace google { +namespace protobuf { +namespace strings { + +// ---------------------------------------------------------------------- +// strings::Substitute() +// strings::SubstituteAndAppend() +// Kind of like StringPrintf, but different. +// +// Example: +// string GetMessage(string first_name, string last_name, int age) { +// return strings::Substitute("My name is $0 $1 and I am $2 years old.", +// first_name, last_name, age); +// } +// +// Differences from StringPrintf: +// * The format string does not identify the types of arguments. +// Instead, the magic of C++ deals with this for us. See below +// for a list of accepted types. +// * Substitutions in the format string are identified by a '$' +// followed by a digit. So, you can use arguments out-of-order and +// use the same argument multiple times. +// * It's much faster than StringPrintf. +// +// Supported types: +// * Strings (const char*, const string&) +// * Note that this means you do not have to add .c_str() to all of +// your strings. In fact, you shouldn't; it will be slower. +// * int32, int64, uint32, uint64: Formatted using SimpleItoa(). +// * float, double: Formatted using SimpleFtoa() and SimpleDtoa(). +// * bool: Printed as "true" or "false". +// +// SubstituteAndAppend() is like Substitute() but appends the result to +// *output. Example: +// +// string str; +// strings::SubstituteAndAppend(&str, +// "My name is $0 $1 and I am $2 years old.", +// first_name, last_name, age); +// +// Substitute() is significantly faster than StringPrintf(). For very +// large strings, it may be orders of magnitude faster. +// ---------------------------------------------------------------------- + +namespace internal { // Implementation details. + +class SubstituteArg { + public: + inline SubstituteArg(const char* value) + : text_(value), size_(strlen(text_)) {} + inline SubstituteArg(const string& value) + : text_(value.data()), size_(value.size()) {} + + // Indicates that no argument was given. + inline explicit SubstituteArg() + : text_(NULL), size_(-1) {} + + // Primitives + // We don't overload for signed and unsigned char because if people are + // explicitly declaring their chars as signed or unsigned then they are + // probably actually using them as 8-bit integers and would probably + // prefer an integer representation. But, we don't really know. So, we + // make the caller decide what to do. + inline SubstituteArg(char value) + : text_(scratch_), size_(1) { scratch_[0] = value; } + inline SubstituteArg(short value) + : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned short value) + : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(int value) + : text_(FastInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned int value) + : text_(FastUInt32ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(long value) + : text_(FastLongToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned long value) + : text_(FastULongToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(long long value) + : text_(FastInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(unsigned long long value) + : text_(FastUInt64ToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(float value) + : text_(FloatToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(double value) + : text_(DoubleToBuffer(value, scratch_)), size_(strlen(text_)) {} + inline SubstituteArg(bool value) + : text_(value ? "true" : "false"), size_(strlen(text_)) {} + + inline const char* data() const { return text_; } + inline int size() const { return size_; } + + private: + const char* text_; + int size_; + char scratch_[kFastToBufferSize]; +}; + +} // namespace internal + +LIBPROTOBUF_EXPORT string Substitute( + const char* format, + const internal::SubstituteArg& arg0 = internal::SubstituteArg(), + const internal::SubstituteArg& arg1 = internal::SubstituteArg(), + const internal::SubstituteArg& arg2 = internal::SubstituteArg(), + const internal::SubstituteArg& arg3 = internal::SubstituteArg(), + const internal::SubstituteArg& arg4 = internal::SubstituteArg(), + const internal::SubstituteArg& arg5 = internal::SubstituteArg(), + const internal::SubstituteArg& arg6 = internal::SubstituteArg(), + const internal::SubstituteArg& arg7 = internal::SubstituteArg(), + const internal::SubstituteArg& arg8 = internal::SubstituteArg(), + const internal::SubstituteArg& arg9 = internal::SubstituteArg()); + +LIBPROTOBUF_EXPORT void SubstituteAndAppend( + string* output, const char* format, + const internal::SubstituteArg& arg0 = internal::SubstituteArg(), + const internal::SubstituteArg& arg1 = internal::SubstituteArg(), + const internal::SubstituteArg& arg2 = internal::SubstituteArg(), + const internal::SubstituteArg& arg3 = internal::SubstituteArg(), + const internal::SubstituteArg& arg4 = internal::SubstituteArg(), + const internal::SubstituteArg& arg5 = internal::SubstituteArg(), + const internal::SubstituteArg& arg6 = internal::SubstituteArg(), + const internal::SubstituteArg& arg7 = internal::SubstituteArg(), + const internal::SubstituteArg& arg8 = internal::SubstituteArg(), + const internal::SubstituteArg& arg9 = internal::SubstituteArg()); + +} // namespace strings +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_STUBS_SUBSTITUTE_H_ diff --git a/src/google/protobuf/test_util.cc b/src/google/protobuf/test_util.cc new file mode 100644 index 00000000..59d98736 --- /dev/null +++ b/src/google/protobuf/test_util.cc @@ -0,0 +1,1920 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +void TestUtil::SetAllFields(unittest::TestAllTypes* message) { + message->set_optional_int32 (101); + message->set_optional_int64 (102); + message->set_optional_uint32 (103); + message->set_optional_uint64 (104); + message->set_optional_sint32 (105); + message->set_optional_sint64 (106); + message->set_optional_fixed32 (107); + message->set_optional_fixed64 (108); + message->set_optional_sfixed32(109); + message->set_optional_sfixed64(110); + message->set_optional_float (111); + message->set_optional_double (112); + message->set_optional_bool (true); + message->set_optional_string ("115"); + message->set_optional_bytes ("116"); + + message->mutable_optionalgroup ()->set_a(117); + message->mutable_optional_nested_message ()->set_bb(118); + message->mutable_optional_foreign_message()->set_c(119); + message->mutable_optional_import_message ()->set_d(120); + + message->set_optional_nested_enum (unittest::TestAllTypes::BAZ); + message->set_optional_foreign_enum(unittest::FOREIGN_BAZ ); + message->set_optional_import_enum (unittest_import::IMPORT_BAZ); + + // StringPiece and Cord fields are only accessible via reflection in the + // open source release; see comments in compiler/cpp/string_field.cc. + message->GetReflection()->SetString( + message, + message->GetDescriptor()->FindFieldByName("optional_string_piece"), + "124"); + message->GetReflection()->SetString( + message, + message->GetDescriptor()->FindFieldByName("optional_cord"), + "125"); + + // ----------------------------------------------------------------- + + message->add_repeated_int32 (201); + message->add_repeated_int64 (202); + message->add_repeated_uint32 (203); + message->add_repeated_uint64 (204); + message->add_repeated_sint32 (205); + message->add_repeated_sint64 (206); + message->add_repeated_fixed32 (207); + message->add_repeated_fixed64 (208); + message->add_repeated_sfixed32(209); + message->add_repeated_sfixed64(210); + message->add_repeated_float (211); + message->add_repeated_double (212); + message->add_repeated_bool (true); + message->add_repeated_string ("215"); + message->add_repeated_bytes ("216"); + + message->add_repeatedgroup ()->set_a(217); + message->add_repeated_nested_message ()->set_bb(218); + message->add_repeated_foreign_message()->set_c(219); + message->add_repeated_import_message ()->set_d(220); + + message->add_repeated_nested_enum (unittest::TestAllTypes::BAR); + message->add_repeated_foreign_enum(unittest::FOREIGN_BAR ); + message->add_repeated_import_enum (unittest_import::IMPORT_BAR); + + message->GetReflection()->AddString( + message, + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + "224"); + message->GetReflection()->AddString( + message, + message->GetDescriptor()->FindFieldByName("repeated_cord"), + "225"); + + // Add a second one of each field. + message->add_repeated_int32 (301); + message->add_repeated_int64 (302); + message->add_repeated_uint32 (303); + message->add_repeated_uint64 (304); + message->add_repeated_sint32 (305); + message->add_repeated_sint64 (306); + message->add_repeated_fixed32 (307); + message->add_repeated_fixed64 (308); + message->add_repeated_sfixed32(309); + message->add_repeated_sfixed64(310); + message->add_repeated_float (311); + message->add_repeated_double (312); + message->add_repeated_bool (false); + message->add_repeated_string ("315"); + message->add_repeated_bytes ("316"); + + message->add_repeatedgroup ()->set_a(317); + message->add_repeated_nested_message ()->set_bb(318); + message->add_repeated_foreign_message()->set_c(319); + message->add_repeated_import_message ()->set_d(320); + + message->add_repeated_nested_enum (unittest::TestAllTypes::BAZ); + message->add_repeated_foreign_enum(unittest::FOREIGN_BAZ ); + message->add_repeated_import_enum (unittest_import::IMPORT_BAZ); + + message->GetReflection()->AddString( + message, + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + "324"); + message->GetReflection()->AddString( + message, + message->GetDescriptor()->FindFieldByName("repeated_cord"), + "325"); + + // ----------------------------------------------------------------- + + message->set_default_int32 (401); + message->set_default_int64 (402); + message->set_default_uint32 (403); + message->set_default_uint64 (404); + message->set_default_sint32 (405); + message->set_default_sint64 (406); + message->set_default_fixed32 (407); + message->set_default_fixed64 (408); + message->set_default_sfixed32(409); + message->set_default_sfixed64(410); + message->set_default_float (411); + message->set_default_double (412); + message->set_default_bool (false); + message->set_default_string ("415"); + message->set_default_bytes ("416"); + + message->set_default_nested_enum (unittest::TestAllTypes::FOO); + message->set_default_foreign_enum(unittest::FOREIGN_FOO ); + message->set_default_import_enum (unittest_import::IMPORT_FOO); + + message->GetReflection()->SetString( + message, + message->GetDescriptor()->FindFieldByName("default_string_piece"), + "424"); + message->GetReflection()->SetString( + message, + message->GetDescriptor()->FindFieldByName("default_cord"), + "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyRepeatedFields(unittest::TestAllTypes* message) { + message->set_repeated_int32 (1, 501); + message->set_repeated_int64 (1, 502); + message->set_repeated_uint32 (1, 503); + message->set_repeated_uint64 (1, 504); + message->set_repeated_sint32 (1, 505); + message->set_repeated_sint64 (1, 506); + message->set_repeated_fixed32 (1, 507); + message->set_repeated_fixed64 (1, 508); + message->set_repeated_sfixed32(1, 509); + message->set_repeated_sfixed64(1, 510); + message->set_repeated_float (1, 511); + message->set_repeated_double (1, 512); + message->set_repeated_bool (1, true); + message->set_repeated_string (1, "515"); + message->set_repeated_bytes (1, "516"); + + message->mutable_repeatedgroup (1)->set_a(517); + message->mutable_repeated_nested_message (1)->set_bb(518); + message->mutable_repeated_foreign_message(1)->set_c(519); + message->mutable_repeated_import_message (1)->set_d(520); + + message->set_repeated_nested_enum (1, unittest::TestAllTypes::FOO); + message->set_repeated_foreign_enum(1, unittest::FOREIGN_FOO ); + message->set_repeated_import_enum (1, unittest_import::IMPORT_FOO); + + message->GetReflection()->SetRepeatedString( + message, + message->GetDescriptor()->FindFieldByName("repeated_string_piece"), + 1, "424"); + message->GetReflection()->SetRepeatedString( + message, + message->GetDescriptor()->FindFieldByName("repeated_cord"), + 1, "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllFieldsSet(const unittest::TestAllTypes& message) { + EXPECT_TRUE(message.has_optional_int32 ()); + EXPECT_TRUE(message.has_optional_int64 ()); + EXPECT_TRUE(message.has_optional_uint32 ()); + EXPECT_TRUE(message.has_optional_uint64 ()); + EXPECT_TRUE(message.has_optional_sint32 ()); + EXPECT_TRUE(message.has_optional_sint64 ()); + EXPECT_TRUE(message.has_optional_fixed32 ()); + EXPECT_TRUE(message.has_optional_fixed64 ()); + EXPECT_TRUE(message.has_optional_sfixed32()); + EXPECT_TRUE(message.has_optional_sfixed64()); + EXPECT_TRUE(message.has_optional_float ()); + EXPECT_TRUE(message.has_optional_double ()); + EXPECT_TRUE(message.has_optional_bool ()); + EXPECT_TRUE(message.has_optional_string ()); + EXPECT_TRUE(message.has_optional_bytes ()); + + EXPECT_TRUE(message.has_optionalgroup ()); + EXPECT_TRUE(message.has_optional_nested_message ()); + EXPECT_TRUE(message.has_optional_foreign_message()); + EXPECT_TRUE(message.has_optional_import_message ()); + + EXPECT_TRUE(message.optionalgroup ().has_a()); + EXPECT_TRUE(message.optional_nested_message ().has_bb()); + EXPECT_TRUE(message.optional_foreign_message().has_c()); + EXPECT_TRUE(message.optional_import_message ().has_d()); + + EXPECT_TRUE(message.has_optional_nested_enum ()); + EXPECT_TRUE(message.has_optional_foreign_enum()); + EXPECT_TRUE(message.has_optional_import_enum ()); + + EXPECT_TRUE(message.has_optional_string_piece()); + EXPECT_TRUE(message.has_optional_cord()); + + EXPECT_EQ(101 , message.optional_int32 ()); + EXPECT_EQ(102 , message.optional_int64 ()); + EXPECT_EQ(103 , message.optional_uint32 ()); + EXPECT_EQ(104 , message.optional_uint64 ()); + EXPECT_EQ(105 , message.optional_sint32 ()); + EXPECT_EQ(106 , message.optional_sint64 ()); + EXPECT_EQ(107 , message.optional_fixed32 ()); + EXPECT_EQ(108 , message.optional_fixed64 ()); + EXPECT_EQ(109 , message.optional_sfixed32()); + EXPECT_EQ(110 , message.optional_sfixed64()); + EXPECT_EQ(111 , message.optional_float ()); + EXPECT_EQ(112 , message.optional_double ()); + EXPECT_EQ(true , message.optional_bool ()); + EXPECT_EQ("115", message.optional_string ()); + EXPECT_EQ("116", message.optional_bytes ()); + + EXPECT_EQ(117, message.optionalgroup ().a()); + EXPECT_EQ(118, message.optional_nested_message ().bb()); + EXPECT_EQ(119, message.optional_foreign_message().c()); + EXPECT_EQ(120, message.optional_import_message ().d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.optional_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.optional_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.optional_import_enum ()); + + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, message.repeated_int32_size ()); + ASSERT_EQ(2, message.repeated_int64_size ()); + ASSERT_EQ(2, message.repeated_uint32_size ()); + ASSERT_EQ(2, message.repeated_uint64_size ()); + ASSERT_EQ(2, message.repeated_sint32_size ()); + ASSERT_EQ(2, message.repeated_sint64_size ()); + ASSERT_EQ(2, message.repeated_fixed32_size ()); + ASSERT_EQ(2, message.repeated_fixed64_size ()); + ASSERT_EQ(2, message.repeated_sfixed32_size()); + ASSERT_EQ(2, message.repeated_sfixed64_size()); + ASSERT_EQ(2, message.repeated_float_size ()); + ASSERT_EQ(2, message.repeated_double_size ()); + ASSERT_EQ(2, message.repeated_bool_size ()); + ASSERT_EQ(2, message.repeated_string_size ()); + ASSERT_EQ(2, message.repeated_bytes_size ()); + + ASSERT_EQ(2, message.repeatedgroup_size ()); + ASSERT_EQ(2, message.repeated_nested_message_size ()); + ASSERT_EQ(2, message.repeated_foreign_message_size()); + ASSERT_EQ(2, message.repeated_import_message_size ()); + ASSERT_EQ(2, message.repeated_nested_enum_size ()); + ASSERT_EQ(2, message.repeated_foreign_enum_size ()); + ASSERT_EQ(2, message.repeated_import_enum_size ()); + + ASSERT_EQ(2, message.repeated_string_piece_size()); + ASSERT_EQ(2, message.repeated_cord_size()); + + EXPECT_EQ(201 , message.repeated_int32 (0)); + EXPECT_EQ(202 , message.repeated_int64 (0)); + EXPECT_EQ(203 , message.repeated_uint32 (0)); + EXPECT_EQ(204 , message.repeated_uint64 (0)); + EXPECT_EQ(205 , message.repeated_sint32 (0)); + EXPECT_EQ(206 , message.repeated_sint64 (0)); + EXPECT_EQ(207 , message.repeated_fixed32 (0)); + EXPECT_EQ(208 , message.repeated_fixed64 (0)); + EXPECT_EQ(209 , message.repeated_sfixed32(0)); + EXPECT_EQ(210 , message.repeated_sfixed64(0)); + EXPECT_EQ(211 , message.repeated_float (0)); + EXPECT_EQ(212 , message.repeated_double (0)); + EXPECT_EQ(true , message.repeated_bool (0)); + EXPECT_EQ("215", message.repeated_string (0)); + EXPECT_EQ("216", message.repeated_bytes (0)); + + EXPECT_EQ(217, message.repeatedgroup (0).a()); + EXPECT_EQ(218, message.repeated_nested_message (0).bb()); + EXPECT_EQ(219, message.repeated_foreign_message(0).c()); + EXPECT_EQ(220, message.repeated_import_message (0).d()); + + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.repeated_foreign_enum(0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (0)); + + EXPECT_EQ(301 , message.repeated_int32 (1)); + EXPECT_EQ(302 , message.repeated_int64 (1)); + EXPECT_EQ(303 , message.repeated_uint32 (1)); + EXPECT_EQ(304 , message.repeated_uint64 (1)); + EXPECT_EQ(305 , message.repeated_sint32 (1)); + EXPECT_EQ(306 , message.repeated_sint64 (1)); + EXPECT_EQ(307 , message.repeated_fixed32 (1)); + EXPECT_EQ(308 , message.repeated_fixed64 (1)); + EXPECT_EQ(309 , message.repeated_sfixed32(1)); + EXPECT_EQ(310 , message.repeated_sfixed64(1)); + EXPECT_EQ(311 , message.repeated_float (1)); + EXPECT_EQ(312 , message.repeated_double (1)); + EXPECT_EQ(false, message.repeated_bool (1)); + EXPECT_EQ("315", message.repeated_string (1)); + EXPECT_EQ("316", message.repeated_bytes (1)); + + EXPECT_EQ(317, message.repeatedgroup (1).a()); + EXPECT_EQ(318, message.repeated_nested_message (1).bb()); + EXPECT_EQ(319, message.repeated_foreign_message(1).c()); + EXPECT_EQ(320, message.repeated_import_message (1).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.repeated_nested_enum (1)); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.repeated_foreign_enum(1)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.repeated_import_enum (1)); + + + // ----------------------------------------------------------------- + + EXPECT_TRUE(message.has_default_int32 ()); + EXPECT_TRUE(message.has_default_int64 ()); + EXPECT_TRUE(message.has_default_uint32 ()); + EXPECT_TRUE(message.has_default_uint64 ()); + EXPECT_TRUE(message.has_default_sint32 ()); + EXPECT_TRUE(message.has_default_sint64 ()); + EXPECT_TRUE(message.has_default_fixed32 ()); + EXPECT_TRUE(message.has_default_fixed64 ()); + EXPECT_TRUE(message.has_default_sfixed32()); + EXPECT_TRUE(message.has_default_sfixed64()); + EXPECT_TRUE(message.has_default_float ()); + EXPECT_TRUE(message.has_default_double ()); + EXPECT_TRUE(message.has_default_bool ()); + EXPECT_TRUE(message.has_default_string ()); + EXPECT_TRUE(message.has_default_bytes ()); + + EXPECT_TRUE(message.has_default_nested_enum ()); + EXPECT_TRUE(message.has_default_foreign_enum()); + EXPECT_TRUE(message.has_default_import_enum ()); + + + EXPECT_EQ(401 , message.default_int32 ()); + EXPECT_EQ(402 , message.default_int64 ()); + EXPECT_EQ(403 , message.default_uint32 ()); + EXPECT_EQ(404 , message.default_uint64 ()); + EXPECT_EQ(405 , message.default_sint32 ()); + EXPECT_EQ(406 , message.default_sint64 ()); + EXPECT_EQ(407 , message.default_fixed32 ()); + EXPECT_EQ(408 , message.default_fixed64 ()); + EXPECT_EQ(409 , message.default_sfixed32()); + EXPECT_EQ(410 , message.default_sfixed64()); + EXPECT_EQ(411 , message.default_float ()); + EXPECT_EQ(412 , message.default_double ()); + EXPECT_EQ(false, message.default_bool ()); + EXPECT_EQ("415", message.default_string ()); + EXPECT_EQ("416", message.default_bytes ()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.default_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_FOO , message.default_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.default_import_enum ()); + +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectClear(const unittest::TestAllTypes& message) { + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(message.has_optional_int32 ()); + EXPECT_FALSE(message.has_optional_int64 ()); + EXPECT_FALSE(message.has_optional_uint32 ()); + EXPECT_FALSE(message.has_optional_uint64 ()); + EXPECT_FALSE(message.has_optional_sint32 ()); + EXPECT_FALSE(message.has_optional_sint64 ()); + EXPECT_FALSE(message.has_optional_fixed32 ()); + EXPECT_FALSE(message.has_optional_fixed64 ()); + EXPECT_FALSE(message.has_optional_sfixed32()); + EXPECT_FALSE(message.has_optional_sfixed64()); + EXPECT_FALSE(message.has_optional_float ()); + EXPECT_FALSE(message.has_optional_double ()); + EXPECT_FALSE(message.has_optional_bool ()); + EXPECT_FALSE(message.has_optional_string ()); + EXPECT_FALSE(message.has_optional_bytes ()); + + EXPECT_FALSE(message.has_optionalgroup ()); + EXPECT_FALSE(message.has_optional_nested_message ()); + EXPECT_FALSE(message.has_optional_foreign_message()); + EXPECT_FALSE(message.has_optional_import_message ()); + + EXPECT_FALSE(message.has_optional_nested_enum ()); + EXPECT_FALSE(message.has_optional_foreign_enum()); + EXPECT_FALSE(message.has_optional_import_enum ()); + + EXPECT_FALSE(message.has_optional_string_piece()); + EXPECT_FALSE(message.has_optional_cord()); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , message.optional_int32 ()); + EXPECT_EQ(0 , message.optional_int64 ()); + EXPECT_EQ(0 , message.optional_uint32 ()); + EXPECT_EQ(0 , message.optional_uint64 ()); + EXPECT_EQ(0 , message.optional_sint32 ()); + EXPECT_EQ(0 , message.optional_sint64 ()); + EXPECT_EQ(0 , message.optional_fixed32 ()); + EXPECT_EQ(0 , message.optional_fixed64 ()); + EXPECT_EQ(0 , message.optional_sfixed32()); + EXPECT_EQ(0 , message.optional_sfixed64()); + EXPECT_EQ(0 , message.optional_float ()); + EXPECT_EQ(0 , message.optional_double ()); + EXPECT_EQ(false, message.optional_bool ()); + EXPECT_EQ("" , message.optional_string ()); + EXPECT_EQ("" , message.optional_bytes ()); + + // Embedded messages should also be clear. + EXPECT_FALSE(message.optionalgroup ().has_a()); + EXPECT_FALSE(message.optional_nested_message ().has_bb()); + EXPECT_FALSE(message.optional_foreign_message().has_c()); + EXPECT_FALSE(message.optional_import_message ().has_d()); + + EXPECT_EQ(0, message.optionalgroup ().a()); + EXPECT_EQ(0, message.optional_nested_message ().bb()); + EXPECT_EQ(0, message.optional_foreign_message().c()); + EXPECT_EQ(0, message.optional_import_message ().d()); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ(unittest::TestAllTypes::FOO, message.optional_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_FOO , message.optional_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.optional_import_enum ()); + + + // Repeated fields are empty. + EXPECT_EQ(0, message.repeated_int32_size ()); + EXPECT_EQ(0, message.repeated_int64_size ()); + EXPECT_EQ(0, message.repeated_uint32_size ()); + EXPECT_EQ(0, message.repeated_uint64_size ()); + EXPECT_EQ(0, message.repeated_sint32_size ()); + EXPECT_EQ(0, message.repeated_sint64_size ()); + EXPECT_EQ(0, message.repeated_fixed32_size ()); + EXPECT_EQ(0, message.repeated_fixed64_size ()); + EXPECT_EQ(0, message.repeated_sfixed32_size()); + EXPECT_EQ(0, message.repeated_sfixed64_size()); + EXPECT_EQ(0, message.repeated_float_size ()); + EXPECT_EQ(0, message.repeated_double_size ()); + EXPECT_EQ(0, message.repeated_bool_size ()); + EXPECT_EQ(0, message.repeated_string_size ()); + EXPECT_EQ(0, message.repeated_bytes_size ()); + + EXPECT_EQ(0, message.repeatedgroup_size ()); + EXPECT_EQ(0, message.repeated_nested_message_size ()); + EXPECT_EQ(0, message.repeated_foreign_message_size()); + EXPECT_EQ(0, message.repeated_import_message_size ()); + EXPECT_EQ(0, message.repeated_nested_enum_size ()); + EXPECT_EQ(0, message.repeated_foreign_enum_size ()); + EXPECT_EQ(0, message.repeated_import_enum_size ()); + + EXPECT_EQ(0, message.repeated_string_piece_size()); + EXPECT_EQ(0, message.repeated_cord_size()); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(message.has_default_int32 ()); + EXPECT_FALSE(message.has_default_int64 ()); + EXPECT_FALSE(message.has_default_uint32 ()); + EXPECT_FALSE(message.has_default_uint64 ()); + EXPECT_FALSE(message.has_default_sint32 ()); + EXPECT_FALSE(message.has_default_sint64 ()); + EXPECT_FALSE(message.has_default_fixed32 ()); + EXPECT_FALSE(message.has_default_fixed64 ()); + EXPECT_FALSE(message.has_default_sfixed32()); + EXPECT_FALSE(message.has_default_sfixed64()); + EXPECT_FALSE(message.has_default_float ()); + EXPECT_FALSE(message.has_default_double ()); + EXPECT_FALSE(message.has_default_bool ()); + EXPECT_FALSE(message.has_default_string ()); + EXPECT_FALSE(message.has_default_bytes ()); + + EXPECT_FALSE(message.has_default_nested_enum ()); + EXPECT_FALSE(message.has_default_foreign_enum()); + EXPECT_FALSE(message.has_default_import_enum ()); + + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , message.default_int32 ()); + EXPECT_EQ( 42 , message.default_int64 ()); + EXPECT_EQ( 43 , message.default_uint32 ()); + EXPECT_EQ( 44 , message.default_uint64 ()); + EXPECT_EQ(-45 , message.default_sint32 ()); + EXPECT_EQ( 46 , message.default_sint64 ()); + EXPECT_EQ( 47 , message.default_fixed32 ()); + EXPECT_EQ( 48 , message.default_fixed64 ()); + EXPECT_EQ( 49 , message.default_sfixed32()); + EXPECT_EQ(-50 , message.default_sfixed64()); + EXPECT_EQ( 51.5 , message.default_float ()); + EXPECT_EQ( 52e3 , message.default_double ()); + EXPECT_EQ(true , message.default_bool ()); + EXPECT_EQ("hello", message.default_string ()); + EXPECT_EQ("world", message.default_bytes ()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.default_nested_enum ()); + EXPECT_EQ(unittest::FOREIGN_BAR , message.default_foreign_enum()); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.default_import_enum ()); + +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectRepeatedFieldsModified( + const unittest::TestAllTypes& message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + ASSERT_EQ(2, message.repeated_int32_size ()); + ASSERT_EQ(2, message.repeated_int64_size ()); + ASSERT_EQ(2, message.repeated_uint32_size ()); + ASSERT_EQ(2, message.repeated_uint64_size ()); + ASSERT_EQ(2, message.repeated_sint32_size ()); + ASSERT_EQ(2, message.repeated_sint64_size ()); + ASSERT_EQ(2, message.repeated_fixed32_size ()); + ASSERT_EQ(2, message.repeated_fixed64_size ()); + ASSERT_EQ(2, message.repeated_sfixed32_size()); + ASSERT_EQ(2, message.repeated_sfixed64_size()); + ASSERT_EQ(2, message.repeated_float_size ()); + ASSERT_EQ(2, message.repeated_double_size ()); + ASSERT_EQ(2, message.repeated_bool_size ()); + ASSERT_EQ(2, message.repeated_string_size ()); + ASSERT_EQ(2, message.repeated_bytes_size ()); + + ASSERT_EQ(2, message.repeatedgroup_size ()); + ASSERT_EQ(2, message.repeated_nested_message_size ()); + ASSERT_EQ(2, message.repeated_foreign_message_size()); + ASSERT_EQ(2, message.repeated_import_message_size ()); + ASSERT_EQ(2, message.repeated_nested_enum_size ()); + ASSERT_EQ(2, message.repeated_foreign_enum_size ()); + ASSERT_EQ(2, message.repeated_import_enum_size ()); + + ASSERT_EQ(2, message.repeated_string_piece_size()); + ASSERT_EQ(2, message.repeated_cord_size()); + + EXPECT_EQ(201 , message.repeated_int32 (0)); + EXPECT_EQ(202 , message.repeated_int64 (0)); + EXPECT_EQ(203 , message.repeated_uint32 (0)); + EXPECT_EQ(204 , message.repeated_uint64 (0)); + EXPECT_EQ(205 , message.repeated_sint32 (0)); + EXPECT_EQ(206 , message.repeated_sint64 (0)); + EXPECT_EQ(207 , message.repeated_fixed32 (0)); + EXPECT_EQ(208 , message.repeated_fixed64 (0)); + EXPECT_EQ(209 , message.repeated_sfixed32(0)); + EXPECT_EQ(210 , message.repeated_sfixed64(0)); + EXPECT_EQ(211 , message.repeated_float (0)); + EXPECT_EQ(212 , message.repeated_double (0)); + EXPECT_EQ(true , message.repeated_bool (0)); + EXPECT_EQ("215", message.repeated_string (0)); + EXPECT_EQ("216", message.repeated_bytes (0)); + + EXPECT_EQ(217, message.repeatedgroup (0).a()); + EXPECT_EQ(218, message.repeated_nested_message (0).bb()); + EXPECT_EQ(219, message.repeated_foreign_message(0).c()); + EXPECT_EQ(220, message.repeated_import_message (0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.repeated_nested_enum (0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.repeated_foreign_enum(0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.repeated_import_enum (0)); + + + // Actually verify the second (modified) elements now. + EXPECT_EQ(501 , message.repeated_int32 (1)); + EXPECT_EQ(502 , message.repeated_int64 (1)); + EXPECT_EQ(503 , message.repeated_uint32 (1)); + EXPECT_EQ(504 , message.repeated_uint64 (1)); + EXPECT_EQ(505 , message.repeated_sint32 (1)); + EXPECT_EQ(506 , message.repeated_sint64 (1)); + EXPECT_EQ(507 , message.repeated_fixed32 (1)); + EXPECT_EQ(508 , message.repeated_fixed64 (1)); + EXPECT_EQ(509 , message.repeated_sfixed32(1)); + EXPECT_EQ(510 , message.repeated_sfixed64(1)); + EXPECT_EQ(511 , message.repeated_float (1)); + EXPECT_EQ(512 , message.repeated_double (1)); + EXPECT_EQ(true , message.repeated_bool (1)); + EXPECT_EQ("515", message.repeated_string (1)); + EXPECT_EQ("516", message.repeated_bytes (1)); + + EXPECT_EQ(517, message.repeatedgroup (1).a()); + EXPECT_EQ(518, message.repeated_nested_message (1).bb()); + EXPECT_EQ(519, message.repeated_foreign_message(1).c()); + EXPECT_EQ(520, message.repeated_import_message (1).d()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.repeated_nested_enum (1)); + EXPECT_EQ(unittest::FOREIGN_FOO , message.repeated_foreign_enum(1)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.repeated_import_enum (1)); + +} + +// =================================================================== +// Extensions +// +// All this code is exactly equivalent to the above code except that it's +// manipulating extension fields instead of normal ones. +// +// I gave up on the 80-char limit here. Sorry. + +void TestUtil::SetAllExtensions(unittest::TestAllExtensions* message) { + message->SetExtension(unittest::optional_int32_extension , 101); + message->SetExtension(unittest::optional_int64_extension , 102); + message->SetExtension(unittest::optional_uint32_extension , 103); + message->SetExtension(unittest::optional_uint64_extension , 104); + message->SetExtension(unittest::optional_sint32_extension , 105); + message->SetExtension(unittest::optional_sint64_extension , 106); + message->SetExtension(unittest::optional_fixed32_extension , 107); + message->SetExtension(unittest::optional_fixed64_extension , 108); + message->SetExtension(unittest::optional_sfixed32_extension, 109); + message->SetExtension(unittest::optional_sfixed64_extension, 110); + message->SetExtension(unittest::optional_float_extension , 111); + message->SetExtension(unittest::optional_double_extension , 112); + message->SetExtension(unittest::optional_bool_extension , true); + message->SetExtension(unittest::optional_string_extension , "115"); + message->SetExtension(unittest::optional_bytes_extension , "116"); + + message->MutableExtension(unittest::optionalgroup_extension )->set_a(117); + message->MutableExtension(unittest::optional_nested_message_extension )->set_bb(118); + message->MutableExtension(unittest::optional_foreign_message_extension)->set_c(119); + message->MutableExtension(unittest::optional_import_message_extension )->set_d(120); + + message->SetExtension(unittest::optional_nested_enum_extension , unittest::TestAllTypes::BAZ); + message->SetExtension(unittest::optional_foreign_enum_extension, unittest::FOREIGN_BAZ ); + message->SetExtension(unittest::optional_import_enum_extension , unittest_import::IMPORT_BAZ); + + message->SetExtension(unittest::optional_string_piece_extension, "124"); + message->SetExtension(unittest::optional_cord_extension, "125"); + + // ----------------------------------------------------------------- + + message->AddExtension(unittest::repeated_int32_extension , 201); + message->AddExtension(unittest::repeated_int64_extension , 202); + message->AddExtension(unittest::repeated_uint32_extension , 203); + message->AddExtension(unittest::repeated_uint64_extension , 204); + message->AddExtension(unittest::repeated_sint32_extension , 205); + message->AddExtension(unittest::repeated_sint64_extension , 206); + message->AddExtension(unittest::repeated_fixed32_extension , 207); + message->AddExtension(unittest::repeated_fixed64_extension , 208); + message->AddExtension(unittest::repeated_sfixed32_extension, 209); + message->AddExtension(unittest::repeated_sfixed64_extension, 210); + message->AddExtension(unittest::repeated_float_extension , 211); + message->AddExtension(unittest::repeated_double_extension , 212); + message->AddExtension(unittest::repeated_bool_extension , true); + message->AddExtension(unittest::repeated_string_extension , "215"); + message->AddExtension(unittest::repeated_bytes_extension , "216"); + + message->AddExtension(unittest::repeatedgroup_extension )->set_a(217); + message->AddExtension(unittest::repeated_nested_message_extension )->set_bb(218); + message->AddExtension(unittest::repeated_foreign_message_extension)->set_c(219); + message->AddExtension(unittest::repeated_import_message_extension )->set_d(220); + + message->AddExtension(unittest::repeated_nested_enum_extension , unittest::TestAllTypes::BAR); + message->AddExtension(unittest::repeated_foreign_enum_extension, unittest::FOREIGN_BAR ); + message->AddExtension(unittest::repeated_import_enum_extension , unittest_import::IMPORT_BAR); + + message->AddExtension(unittest::repeated_string_piece_extension, "224"); + message->AddExtension(unittest::repeated_cord_extension, "225"); + + // Add a second one of each field. + message->AddExtension(unittest::repeated_int32_extension , 301); + message->AddExtension(unittest::repeated_int64_extension , 302); + message->AddExtension(unittest::repeated_uint32_extension , 303); + message->AddExtension(unittest::repeated_uint64_extension , 304); + message->AddExtension(unittest::repeated_sint32_extension , 305); + message->AddExtension(unittest::repeated_sint64_extension , 306); + message->AddExtension(unittest::repeated_fixed32_extension , 307); + message->AddExtension(unittest::repeated_fixed64_extension , 308); + message->AddExtension(unittest::repeated_sfixed32_extension, 309); + message->AddExtension(unittest::repeated_sfixed64_extension, 310); + message->AddExtension(unittest::repeated_float_extension , 311); + message->AddExtension(unittest::repeated_double_extension , 312); + message->AddExtension(unittest::repeated_bool_extension , false); + message->AddExtension(unittest::repeated_string_extension , "315"); + message->AddExtension(unittest::repeated_bytes_extension , "316"); + + message->AddExtension(unittest::repeatedgroup_extension )->set_a(317); + message->AddExtension(unittest::repeated_nested_message_extension )->set_bb(318); + message->AddExtension(unittest::repeated_foreign_message_extension)->set_c(319); + message->AddExtension(unittest::repeated_import_message_extension )->set_d(320); + + message->AddExtension(unittest::repeated_nested_enum_extension , unittest::TestAllTypes::BAZ); + message->AddExtension(unittest::repeated_foreign_enum_extension, unittest::FOREIGN_BAZ ); + message->AddExtension(unittest::repeated_import_enum_extension , unittest_import::IMPORT_BAZ); + + message->AddExtension(unittest::repeated_string_piece_extension, "324"); + message->AddExtension(unittest::repeated_cord_extension, "325"); + + // ----------------------------------------------------------------- + + message->SetExtension(unittest::default_int32_extension , 401); + message->SetExtension(unittest::default_int64_extension , 402); + message->SetExtension(unittest::default_uint32_extension , 403); + message->SetExtension(unittest::default_uint64_extension , 404); + message->SetExtension(unittest::default_sint32_extension , 405); + message->SetExtension(unittest::default_sint64_extension , 406); + message->SetExtension(unittest::default_fixed32_extension , 407); + message->SetExtension(unittest::default_fixed64_extension , 408); + message->SetExtension(unittest::default_sfixed32_extension, 409); + message->SetExtension(unittest::default_sfixed64_extension, 410); + message->SetExtension(unittest::default_float_extension , 411); + message->SetExtension(unittest::default_double_extension , 412); + message->SetExtension(unittest::default_bool_extension , false); + message->SetExtension(unittest::default_string_extension , "415"); + message->SetExtension(unittest::default_bytes_extension , "416"); + + message->SetExtension(unittest::default_nested_enum_extension , unittest::TestAllTypes::FOO); + message->SetExtension(unittest::default_foreign_enum_extension, unittest::FOREIGN_FOO ); + message->SetExtension(unittest::default_import_enum_extension , unittest_import::IMPORT_FOO); + + message->SetExtension(unittest::default_string_piece_extension, "424"); + message->SetExtension(unittest::default_cord_extension, "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::SetAllFieldsAndExtensions( + unittest::TestFieldOrderings* message) { + GOOGLE_CHECK(message); + message->set_my_int(1); + message->set_my_string("foo"); + message->set_my_float(1.0); + message->SetExtension(unittest::my_extension_int, 23); + message->SetExtension(unittest::my_extension_string, "bar"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ModifyRepeatedExtensions(unittest::TestAllExtensions* message) { + message->SetExtension(unittest::repeated_int32_extension , 1, 501); + message->SetExtension(unittest::repeated_int64_extension , 1, 502); + message->SetExtension(unittest::repeated_uint32_extension , 1, 503); + message->SetExtension(unittest::repeated_uint64_extension , 1, 504); + message->SetExtension(unittest::repeated_sint32_extension , 1, 505); + message->SetExtension(unittest::repeated_sint64_extension , 1, 506); + message->SetExtension(unittest::repeated_fixed32_extension , 1, 507); + message->SetExtension(unittest::repeated_fixed64_extension , 1, 508); + message->SetExtension(unittest::repeated_sfixed32_extension, 1, 509); + message->SetExtension(unittest::repeated_sfixed64_extension, 1, 510); + message->SetExtension(unittest::repeated_float_extension , 1, 511); + message->SetExtension(unittest::repeated_double_extension , 1, 512); + message->SetExtension(unittest::repeated_bool_extension , 1, true); + message->SetExtension(unittest::repeated_string_extension , 1, "515"); + message->SetExtension(unittest::repeated_bytes_extension , 1, "516"); + + message->MutableExtension(unittest::repeatedgroup_extension , 1)->set_a(517); + message->MutableExtension(unittest::repeated_nested_message_extension , 1)->set_bb(518); + message->MutableExtension(unittest::repeated_foreign_message_extension, 1)->set_c(519); + message->MutableExtension(unittest::repeated_import_message_extension , 1)->set_d(520); + + message->SetExtension(unittest::repeated_nested_enum_extension , 1, unittest::TestAllTypes::FOO); + message->SetExtension(unittest::repeated_foreign_enum_extension, 1, unittest::FOREIGN_FOO ); + message->SetExtension(unittest::repeated_import_enum_extension , 1, unittest_import::IMPORT_FOO); + + message->SetExtension(unittest::repeated_string_piece_extension, 1, "524"); + message->SetExtension(unittest::repeated_cord_extension, 1, "525"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllExtensionsSet( + const unittest::TestAllExtensions& message) { + EXPECT_TRUE(message.HasExtension(unittest::optional_int32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_int64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_uint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_fixed32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_fixed64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_sfixed32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_sfixed64_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_float_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_double_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_bool_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_string_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_bytes_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::optionalgroup_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_nested_message_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_foreign_message_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_import_message_extension )); + + EXPECT_TRUE(message.GetExtension(unittest::optionalgroup_extension ).has_a()); + EXPECT_TRUE(message.GetExtension(unittest::optional_nested_message_extension ).has_bb()); + EXPECT_TRUE(message.GetExtension(unittest::optional_foreign_message_extension).has_c()); + EXPECT_TRUE(message.GetExtension(unittest::optional_import_message_extension ).has_d()); + + EXPECT_TRUE(message.HasExtension(unittest::optional_nested_enum_extension )); + EXPECT_TRUE(message.HasExtension(unittest::optional_foreign_enum_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_import_enum_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::optional_string_piece_extension)); + EXPECT_TRUE(message.HasExtension(unittest::optional_cord_extension)); + + EXPECT_EQ(101 , message.GetExtension(unittest::optional_int32_extension )); + EXPECT_EQ(102 , message.GetExtension(unittest::optional_int64_extension )); + EXPECT_EQ(103 , message.GetExtension(unittest::optional_uint32_extension )); + EXPECT_EQ(104 , message.GetExtension(unittest::optional_uint64_extension )); + EXPECT_EQ(105 , message.GetExtension(unittest::optional_sint32_extension )); + EXPECT_EQ(106 , message.GetExtension(unittest::optional_sint64_extension )); + EXPECT_EQ(107 , message.GetExtension(unittest::optional_fixed32_extension )); + EXPECT_EQ(108 , message.GetExtension(unittest::optional_fixed64_extension )); + EXPECT_EQ(109 , message.GetExtension(unittest::optional_sfixed32_extension)); + EXPECT_EQ(110 , message.GetExtension(unittest::optional_sfixed64_extension)); + EXPECT_EQ(111 , message.GetExtension(unittest::optional_float_extension )); + EXPECT_EQ(112 , message.GetExtension(unittest::optional_double_extension )); + EXPECT_EQ(true , message.GetExtension(unittest::optional_bool_extension )); + EXPECT_EQ("115", message.GetExtension(unittest::optional_string_extension )); + EXPECT_EQ("116", message.GetExtension(unittest::optional_bytes_extension )); + + EXPECT_EQ(117, message.GetExtension(unittest::optionalgroup_extension ).a()); + EXPECT_EQ(118, message.GetExtension(unittest::optional_nested_message_extension ).bb()); + EXPECT_EQ(119, message.GetExtension(unittest::optional_foreign_message_extension).c()); + EXPECT_EQ(120, message.GetExtension(unittest::optional_import_message_extension ).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.GetExtension(unittest::optional_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.GetExtension(unittest::optional_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.GetExtension(unittest::optional_import_enum_extension )); + + EXPECT_EQ("124", message.GetExtension(unittest::optional_string_piece_extension)); + EXPECT_EQ("125", message.GetExtension(unittest::optional_cord_extension)); + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bytes_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeatedgroup_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_piece_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_cord_extension)); + + EXPECT_EQ(201 , message.GetExtension(unittest::repeated_int32_extension , 0)); + EXPECT_EQ(202 , message.GetExtension(unittest::repeated_int64_extension , 0)); + EXPECT_EQ(203 , message.GetExtension(unittest::repeated_uint32_extension , 0)); + EXPECT_EQ(204 , message.GetExtension(unittest::repeated_uint64_extension , 0)); + EXPECT_EQ(205 , message.GetExtension(unittest::repeated_sint32_extension , 0)); + EXPECT_EQ(206 , message.GetExtension(unittest::repeated_sint64_extension , 0)); + EXPECT_EQ(207 , message.GetExtension(unittest::repeated_fixed32_extension , 0)); + EXPECT_EQ(208 , message.GetExtension(unittest::repeated_fixed64_extension , 0)); + EXPECT_EQ(209 , message.GetExtension(unittest::repeated_sfixed32_extension, 0)); + EXPECT_EQ(210 , message.GetExtension(unittest::repeated_sfixed64_extension, 0)); + EXPECT_EQ(211 , message.GetExtension(unittest::repeated_float_extension , 0)); + EXPECT_EQ(212 , message.GetExtension(unittest::repeated_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 0)); + EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension , 0)); + EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension , 0)); + + EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension , 0).a()); + EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb()); + EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c()); + EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::repeated_foreign_enum_extension, 0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 0)); + + EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 0)); + EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 0)); + + EXPECT_EQ(301 , message.GetExtension(unittest::repeated_int32_extension , 1)); + EXPECT_EQ(302 , message.GetExtension(unittest::repeated_int64_extension , 1)); + EXPECT_EQ(303 , message.GetExtension(unittest::repeated_uint32_extension , 1)); + EXPECT_EQ(304 , message.GetExtension(unittest::repeated_uint64_extension , 1)); + EXPECT_EQ(305 , message.GetExtension(unittest::repeated_sint32_extension , 1)); + EXPECT_EQ(306 , message.GetExtension(unittest::repeated_sint64_extension , 1)); + EXPECT_EQ(307 , message.GetExtension(unittest::repeated_fixed32_extension , 1)); + EXPECT_EQ(308 , message.GetExtension(unittest::repeated_fixed64_extension , 1)); + EXPECT_EQ(309 , message.GetExtension(unittest::repeated_sfixed32_extension, 1)); + EXPECT_EQ(310 , message.GetExtension(unittest::repeated_sfixed64_extension, 1)); + EXPECT_EQ(311 , message.GetExtension(unittest::repeated_float_extension , 1)); + EXPECT_EQ(312 , message.GetExtension(unittest::repeated_double_extension , 1)); + EXPECT_EQ(false, message.GetExtension(unittest::repeated_bool_extension , 1)); + EXPECT_EQ("315", message.GetExtension(unittest::repeated_string_extension , 1)); + EXPECT_EQ("316", message.GetExtension(unittest::repeated_bytes_extension , 1)); + + EXPECT_EQ(317, message.GetExtension(unittest::repeatedgroup_extension , 1).a()); + EXPECT_EQ(318, message.GetExtension(unittest::repeated_nested_message_extension , 1).bb()); + EXPECT_EQ(319, message.GetExtension(unittest::repeated_foreign_message_extension, 1).c()); + EXPECT_EQ(320, message.GetExtension(unittest::repeated_import_message_extension , 1).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAZ, message.GetExtension(unittest::repeated_nested_enum_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_BAZ , message.GetExtension(unittest::repeated_foreign_enum_extension, 1)); + EXPECT_EQ(unittest_import::IMPORT_BAZ, message.GetExtension(unittest::repeated_import_enum_extension , 1)); + + EXPECT_EQ("324", message.GetExtension(unittest::repeated_string_piece_extension, 1)); + EXPECT_EQ("325", message.GetExtension(unittest::repeated_cord_extension, 1)); + + // ----------------------------------------------------------------- + + EXPECT_TRUE(message.HasExtension(unittest::default_int32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_int64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_uint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_uint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sint32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sint64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_fixed32_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_fixed64_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_sfixed32_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_sfixed64_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_float_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_double_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_bool_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_string_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_bytes_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::default_nested_enum_extension )); + EXPECT_TRUE(message.HasExtension(unittest::default_foreign_enum_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_import_enum_extension )); + + EXPECT_TRUE(message.HasExtension(unittest::default_string_piece_extension)); + EXPECT_TRUE(message.HasExtension(unittest::default_cord_extension)); + + EXPECT_EQ(401 , message.GetExtension(unittest::default_int32_extension )); + EXPECT_EQ(402 , message.GetExtension(unittest::default_int64_extension )); + EXPECT_EQ(403 , message.GetExtension(unittest::default_uint32_extension )); + EXPECT_EQ(404 , message.GetExtension(unittest::default_uint64_extension )); + EXPECT_EQ(405 , message.GetExtension(unittest::default_sint32_extension )); + EXPECT_EQ(406 , message.GetExtension(unittest::default_sint64_extension )); + EXPECT_EQ(407 , message.GetExtension(unittest::default_fixed32_extension )); + EXPECT_EQ(408 , message.GetExtension(unittest::default_fixed64_extension )); + EXPECT_EQ(409 , message.GetExtension(unittest::default_sfixed32_extension)); + EXPECT_EQ(410 , message.GetExtension(unittest::default_sfixed64_extension)); + EXPECT_EQ(411 , message.GetExtension(unittest::default_float_extension )); + EXPECT_EQ(412 , message.GetExtension(unittest::default_double_extension )); + EXPECT_EQ(false, message.GetExtension(unittest::default_bool_extension )); + EXPECT_EQ("415", message.GetExtension(unittest::default_string_extension )); + EXPECT_EQ("416", message.GetExtension(unittest::default_bytes_extension )); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::default_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::default_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::default_import_enum_extension )); + + EXPECT_EQ("424", message.GetExtension(unittest::default_string_piece_extension)); + EXPECT_EQ("425", message.GetExtension(unittest::default_cord_extension)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectExtensionsClear( + const unittest::TestAllExtensions& message) { + string serialized; + ASSERT_TRUE(message.SerializeToString(&serialized)); + EXPECT_EQ("", serialized); + EXPECT_EQ(0, message.ByteSize()); + + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(message.HasExtension(unittest::optional_int32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_int64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_uint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_uint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_fixed32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_fixed64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_sfixed32_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_sfixed64_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_float_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_double_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_bool_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_string_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_bytes_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optionalgroup_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_nested_message_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_foreign_message_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_import_message_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optional_nested_enum_extension )); + EXPECT_FALSE(message.HasExtension(unittest::optional_foreign_enum_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_import_enum_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::optional_string_piece_extension)); + EXPECT_FALSE(message.HasExtension(unittest::optional_cord_extension)); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , message.GetExtension(unittest::optional_int32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_int64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_uint32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_uint64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sint32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sint64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_fixed32_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_fixed64_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sfixed32_extension)); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_sfixed64_extension)); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_float_extension )); + EXPECT_EQ(0 , message.GetExtension(unittest::optional_double_extension )); + EXPECT_EQ(false, message.GetExtension(unittest::optional_bool_extension )); + EXPECT_EQ("" , message.GetExtension(unittest::optional_string_extension )); + EXPECT_EQ("" , message.GetExtension(unittest::optional_bytes_extension )); + + // Embedded messages should also be clear. + EXPECT_FALSE(message.GetExtension(unittest::optionalgroup_extension ).has_a()); + EXPECT_FALSE(message.GetExtension(unittest::optional_nested_message_extension ).has_bb()); + EXPECT_FALSE(message.GetExtension(unittest::optional_foreign_message_extension).has_c()); + EXPECT_FALSE(message.GetExtension(unittest::optional_import_message_extension ).has_d()); + + EXPECT_EQ(0, message.GetExtension(unittest::optionalgroup_extension ).a()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_nested_message_extension ).bb()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_foreign_message_extension).c()); + EXPECT_EQ(0, message.GetExtension(unittest::optional_import_message_extension ).d()); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::optional_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::optional_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::optional_import_enum_extension )); + + EXPECT_EQ("", message.GetExtension(unittest::optional_string_piece_extension)); + EXPECT_EQ("", message.GetExtension(unittest::optional_cord_extension)); + + // Repeated fields are empty. + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_int32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_int64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_uint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_uint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sint32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sint64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_fixed32_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_fixed64_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_float_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_double_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_bool_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_string_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_bytes_extension )); + + EXPECT_EQ(0, message.ExtensionSize(unittest::repeatedgroup_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_nested_message_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_import_message_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_string_piece_extension)); + EXPECT_EQ(0, message.ExtensionSize(unittest::repeated_cord_extension)); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(message.HasExtension(unittest::default_int32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_int64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_uint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_uint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sint32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sint64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_fixed32_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_fixed64_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_sfixed32_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_sfixed64_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_float_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_double_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_bool_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_string_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_bytes_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::default_nested_enum_extension )); + EXPECT_FALSE(message.HasExtension(unittest::default_foreign_enum_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_import_enum_extension )); + + EXPECT_FALSE(message.HasExtension(unittest::default_string_piece_extension)); + EXPECT_FALSE(message.HasExtension(unittest::default_cord_extension)); + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , message.GetExtension(unittest::default_int32_extension )); + EXPECT_EQ( 42 , message.GetExtension(unittest::default_int64_extension )); + EXPECT_EQ( 43 , message.GetExtension(unittest::default_uint32_extension )); + EXPECT_EQ( 44 , message.GetExtension(unittest::default_uint64_extension )); + EXPECT_EQ(-45 , message.GetExtension(unittest::default_sint32_extension )); + EXPECT_EQ( 46 , message.GetExtension(unittest::default_sint64_extension )); + EXPECT_EQ( 47 , message.GetExtension(unittest::default_fixed32_extension )); + EXPECT_EQ( 48 , message.GetExtension(unittest::default_fixed64_extension )); + EXPECT_EQ( 49 , message.GetExtension(unittest::default_sfixed32_extension)); + EXPECT_EQ(-50 , message.GetExtension(unittest::default_sfixed64_extension)); + EXPECT_EQ( 51.5 , message.GetExtension(unittest::default_float_extension )); + EXPECT_EQ( 52e3 , message.GetExtension(unittest::default_double_extension )); + EXPECT_EQ(true , message.GetExtension(unittest::default_bool_extension )); + EXPECT_EQ("hello", message.GetExtension(unittest::default_string_extension )); + EXPECT_EQ("world", message.GetExtension(unittest::default_bytes_extension )); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::default_nested_enum_extension )); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::default_foreign_enum_extension)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::default_import_enum_extension )); + + EXPECT_EQ("abc", message.GetExtension(unittest::default_string_piece_extension)); + EXPECT_EQ("123", message.GetExtension(unittest::default_cord_extension)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectRepeatedExtensionsModified( + const unittest::TestAllExtensions& message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_int64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_uint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sint64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed32_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_fixed64_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed32_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_sfixed64_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_float_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_double_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bool_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_bytes_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeatedgroup_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_message_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_message_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_nested_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_foreign_enum_extension )); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_import_enum_extension )); + + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_string_piece_extension)); + ASSERT_EQ(2, message.ExtensionSize(unittest::repeated_cord_extension)); + + EXPECT_EQ(201 , message.GetExtension(unittest::repeated_int32_extension , 0)); + EXPECT_EQ(202 , message.GetExtension(unittest::repeated_int64_extension , 0)); + EXPECT_EQ(203 , message.GetExtension(unittest::repeated_uint32_extension , 0)); + EXPECT_EQ(204 , message.GetExtension(unittest::repeated_uint64_extension , 0)); + EXPECT_EQ(205 , message.GetExtension(unittest::repeated_sint32_extension , 0)); + EXPECT_EQ(206 , message.GetExtension(unittest::repeated_sint64_extension , 0)); + EXPECT_EQ(207 , message.GetExtension(unittest::repeated_fixed32_extension , 0)); + EXPECT_EQ(208 , message.GetExtension(unittest::repeated_fixed64_extension , 0)); + EXPECT_EQ(209 , message.GetExtension(unittest::repeated_sfixed32_extension, 0)); + EXPECT_EQ(210 , message.GetExtension(unittest::repeated_sfixed64_extension, 0)); + EXPECT_EQ(211 , message.GetExtension(unittest::repeated_float_extension , 0)); + EXPECT_EQ(212 , message.GetExtension(unittest::repeated_double_extension , 0)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 0)); + EXPECT_EQ("215", message.GetExtension(unittest::repeated_string_extension , 0)); + EXPECT_EQ("216", message.GetExtension(unittest::repeated_bytes_extension , 0)); + + EXPECT_EQ(217, message.GetExtension(unittest::repeatedgroup_extension , 0).a()); + EXPECT_EQ(218, message.GetExtension(unittest::repeated_nested_message_extension , 0).bb()); + EXPECT_EQ(219, message.GetExtension(unittest::repeated_foreign_message_extension, 0).c()); + EXPECT_EQ(220, message.GetExtension(unittest::repeated_import_message_extension , 0).d()); + + EXPECT_EQ(unittest::TestAllTypes::BAR, message.GetExtension(unittest::repeated_nested_enum_extension , 0)); + EXPECT_EQ(unittest::FOREIGN_BAR , message.GetExtension(unittest::repeated_foreign_enum_extension, 0)); + EXPECT_EQ(unittest_import::IMPORT_BAR, message.GetExtension(unittest::repeated_import_enum_extension , 0)); + + EXPECT_EQ("224", message.GetExtension(unittest::repeated_string_piece_extension, 0)); + EXPECT_EQ("225", message.GetExtension(unittest::repeated_cord_extension, 0)); + + // Actually verify the second (modified) elements now. + EXPECT_EQ(501 , message.GetExtension(unittest::repeated_int32_extension , 1)); + EXPECT_EQ(502 , message.GetExtension(unittest::repeated_int64_extension , 1)); + EXPECT_EQ(503 , message.GetExtension(unittest::repeated_uint32_extension , 1)); + EXPECT_EQ(504 , message.GetExtension(unittest::repeated_uint64_extension , 1)); + EXPECT_EQ(505 , message.GetExtension(unittest::repeated_sint32_extension , 1)); + EXPECT_EQ(506 , message.GetExtension(unittest::repeated_sint64_extension , 1)); + EXPECT_EQ(507 , message.GetExtension(unittest::repeated_fixed32_extension , 1)); + EXPECT_EQ(508 , message.GetExtension(unittest::repeated_fixed64_extension , 1)); + EXPECT_EQ(509 , message.GetExtension(unittest::repeated_sfixed32_extension, 1)); + EXPECT_EQ(510 , message.GetExtension(unittest::repeated_sfixed64_extension, 1)); + EXPECT_EQ(511 , message.GetExtension(unittest::repeated_float_extension , 1)); + EXPECT_EQ(512 , message.GetExtension(unittest::repeated_double_extension , 1)); + EXPECT_EQ(true , message.GetExtension(unittest::repeated_bool_extension , 1)); + EXPECT_EQ("515", message.GetExtension(unittest::repeated_string_extension , 1)); + EXPECT_EQ("516", message.GetExtension(unittest::repeated_bytes_extension , 1)); + + EXPECT_EQ(517, message.GetExtension(unittest::repeatedgroup_extension , 1).a()); + EXPECT_EQ(518, message.GetExtension(unittest::repeated_nested_message_extension , 1).bb()); + EXPECT_EQ(519, message.GetExtension(unittest::repeated_foreign_message_extension, 1).c()); + EXPECT_EQ(520, message.GetExtension(unittest::repeated_import_message_extension , 1).d()); + + EXPECT_EQ(unittest::TestAllTypes::FOO, message.GetExtension(unittest::repeated_nested_enum_extension , 1)); + EXPECT_EQ(unittest::FOREIGN_FOO , message.GetExtension(unittest::repeated_foreign_enum_extension, 1)); + EXPECT_EQ(unittest_import::IMPORT_FOO, message.GetExtension(unittest::repeated_import_enum_extension , 1)); + + EXPECT_EQ("524", message.GetExtension(unittest::repeated_string_piece_extension, 1)); + EXPECT_EQ("525", message.GetExtension(unittest::repeated_cord_extension, 1)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ExpectAllFieldsAndExtensionsInOrder(const string& serialized) { + // We set each field individually, serialize separately, and concatenate all + // the strings in canonical order to determine the expected serialization. + string expected; + unittest::TestFieldOrderings message; + message.set_my_int(1); // Field 1. + message.AppendToString(&expected); + message.Clear(); + message.SetExtension(unittest::my_extension_int, 23); // Field 5. + message.AppendToString(&expected); + message.Clear(); + message.set_my_string("foo"); // Field 11. + message.AppendToString(&expected); + message.Clear(); + message.SetExtension(unittest::my_extension_string, "bar"); // Field 50. + message.AppendToString(&expected); + message.Clear(); + message.set_my_float(1.0); // Field 101. + message.AppendToString(&expected); + message.Clear(); + + // We don't EXPECT_EQ() since we don't want to print raw bytes to stdout. + EXPECT_TRUE(serialized == expected); +} + +// =================================================================== + +TestUtil::ReflectionTester::ReflectionTester( + const Descriptor* base_descriptor) + : base_descriptor_(base_descriptor) { + + const DescriptorPool* pool = base_descriptor->file()->pool(); + + nested_b_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.NestedMessage.bb"); + foreign_c_ = + pool->FindFieldByName("protobuf_unittest.ForeignMessage.c"); + import_d_ = + pool->FindFieldByName("protobuf_unittest_import.ImportMessage.d"); + nested_foo_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.FOO"); + nested_bar_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.BAR"); + nested_baz_ = + pool->FindEnumValueByName("protobuf_unittest.TestAllTypes.BAZ"); + foreign_foo_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_FOO"); + foreign_bar_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_BAR"); + foreign_baz_ = + pool->FindEnumValueByName("protobuf_unittest.FOREIGN_BAZ"); + import_foo_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_FOO"); + import_bar_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_BAR"); + import_baz_ = + pool->FindEnumValueByName("protobuf_unittest_import.IMPORT_BAZ"); + + if (base_descriptor_->name() == "TestAllExtensions") { + group_a_ = + pool->FindFieldByName("protobuf_unittest.OptionalGroup_extension.a"); + repeated_group_a_ = + pool->FindFieldByName("protobuf_unittest.RepeatedGroup_extension.a"); + } else { + group_a_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.OptionalGroup.a"); + repeated_group_a_ = + pool->FindFieldByName("protobuf_unittest.TestAllTypes.RepeatedGroup.a"); + } + + EXPECT_TRUE(group_a_ != NULL); + EXPECT_TRUE(repeated_group_a_ != NULL); + EXPECT_TRUE(nested_b_ != NULL); + EXPECT_TRUE(foreign_c_ != NULL); + EXPECT_TRUE(import_d_ != NULL); + EXPECT_TRUE(nested_foo_ != NULL); + EXPECT_TRUE(nested_bar_ != NULL); + EXPECT_TRUE(nested_baz_ != NULL); + EXPECT_TRUE(foreign_foo_ != NULL); + EXPECT_TRUE(foreign_bar_ != NULL); + EXPECT_TRUE(foreign_baz_ != NULL); + EXPECT_TRUE(import_foo_ != NULL); + EXPECT_TRUE(import_bar_ != NULL); + EXPECT_TRUE(import_baz_ != NULL); +} + +// Shorthand to get a FieldDescriptor for a field of unittest::TestAllTypes. +const FieldDescriptor* TestUtil::ReflectionTester::F(const string& name) { + const FieldDescriptor* result = NULL; + if (base_descriptor_->name() == "TestAllExtensions") { + result = base_descriptor_->file()->FindExtensionByName(name + "_extension"); + } else { + result = base_descriptor_->FindFieldByName(name); + } + GOOGLE_CHECK(result != NULL); + return result; +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::SetAllFieldsViaReflection(Message* message) { + const Reflection* reflection = message->GetReflection(); + Message* sub_message; + + reflection->SetInt32 (message, F("optional_int32" ), 101); + reflection->SetInt64 (message, F("optional_int64" ), 102); + reflection->SetUInt32(message, F("optional_uint32" ), 103); + reflection->SetUInt64(message, F("optional_uint64" ), 104); + reflection->SetInt32 (message, F("optional_sint32" ), 105); + reflection->SetInt64 (message, F("optional_sint64" ), 106); + reflection->SetUInt32(message, F("optional_fixed32" ), 107); + reflection->SetUInt64(message, F("optional_fixed64" ), 108); + reflection->SetInt32 (message, F("optional_sfixed32"), 109); + reflection->SetInt64 (message, F("optional_sfixed64"), 110); + reflection->SetFloat (message, F("optional_float" ), 111); + reflection->SetDouble(message, F("optional_double" ), 112); + reflection->SetBool (message, F("optional_bool" ), true); + reflection->SetString(message, F("optional_string" ), "115"); + reflection->SetString(message, F("optional_bytes" ), "116"); + + sub_message = reflection->MutableMessage(message, F("optionalgroup")); + sub_message->GetReflection()->SetInt32(sub_message, group_a_, 117); + sub_message = reflection->MutableMessage(message, F("optional_nested_message")); + sub_message->GetReflection()->SetInt32(sub_message, nested_b_, 118); + sub_message = reflection->MutableMessage(message, F("optional_foreign_message")); + sub_message->GetReflection()->SetInt32(sub_message, foreign_c_, 119); + sub_message = reflection->MutableMessage(message, F("optional_import_message")); + sub_message->GetReflection()->SetInt32(sub_message, import_d_, 120); + + reflection->SetEnum(message, F("optional_nested_enum" ), nested_baz_); + reflection->SetEnum(message, F("optional_foreign_enum"), foreign_baz_); + reflection->SetEnum(message, F("optional_import_enum" ), import_baz_); + + reflection->SetString(message, F("optional_string_piece"), "124"); + reflection->SetString(message, F("optional_cord"), "125"); + + // ----------------------------------------------------------------- + + reflection->AddInt32 (message, F("repeated_int32" ), 201); + reflection->AddInt64 (message, F("repeated_int64" ), 202); + reflection->AddUInt32(message, F("repeated_uint32" ), 203); + reflection->AddUInt64(message, F("repeated_uint64" ), 204); + reflection->AddInt32 (message, F("repeated_sint32" ), 205); + reflection->AddInt64 (message, F("repeated_sint64" ), 206); + reflection->AddUInt32(message, F("repeated_fixed32" ), 207); + reflection->AddUInt64(message, F("repeated_fixed64" ), 208); + reflection->AddInt32 (message, F("repeated_sfixed32"), 209); + reflection->AddInt64 (message, F("repeated_sfixed64"), 210); + reflection->AddFloat (message, F("repeated_float" ), 211); + reflection->AddDouble(message, F("repeated_double" ), 212); + reflection->AddBool (message, F("repeated_bool" ), true); + reflection->AddString(message, F("repeated_string" ), "215"); + reflection->AddString(message, F("repeated_bytes" ), "216"); + + sub_message = reflection->AddMessage(message, F("repeatedgroup")); + sub_message->GetReflection()->SetInt32(sub_message, repeated_group_a_, 217); + sub_message = reflection->AddMessage(message, F("repeated_nested_message")); + sub_message->GetReflection()->SetInt32(sub_message, nested_b_, 218); + sub_message = reflection->AddMessage(message, F("repeated_foreign_message")); + sub_message->GetReflection()->SetInt32(sub_message, foreign_c_, 219); + sub_message = reflection->AddMessage(message, F("repeated_import_message")); + sub_message->GetReflection()->SetInt32(sub_message, import_d_, 220); + + reflection->AddEnum(message, F("repeated_nested_enum" ), nested_bar_); + reflection->AddEnum(message, F("repeated_foreign_enum"), foreign_bar_); + reflection->AddEnum(message, F("repeated_import_enum" ), import_bar_); + + reflection->AddString(message, F("repeated_string_piece"), "224"); + reflection->AddString(message, F("repeated_cord"), "225"); + + // Add a second one of each field. + reflection->AddInt32 (message, F("repeated_int32" ), 301); + reflection->AddInt64 (message, F("repeated_int64" ), 302); + reflection->AddUInt32(message, F("repeated_uint32" ), 303); + reflection->AddUInt64(message, F("repeated_uint64" ), 304); + reflection->AddInt32 (message, F("repeated_sint32" ), 305); + reflection->AddInt64 (message, F("repeated_sint64" ), 306); + reflection->AddUInt32(message, F("repeated_fixed32" ), 307); + reflection->AddUInt64(message, F("repeated_fixed64" ), 308); + reflection->AddInt32 (message, F("repeated_sfixed32"), 309); + reflection->AddInt64 (message, F("repeated_sfixed64"), 310); + reflection->AddFloat (message, F("repeated_float" ), 311); + reflection->AddDouble(message, F("repeated_double" ), 312); + reflection->AddBool (message, F("repeated_bool" ), false); + reflection->AddString(message, F("repeated_string" ), "315"); + reflection->AddString(message, F("repeated_bytes" ), "316"); + + sub_message = reflection->AddMessage(message, F("repeatedgroup")); + sub_message->GetReflection()->SetInt32(sub_message, repeated_group_a_, 317); + sub_message = reflection->AddMessage(message, F("repeated_nested_message")); + sub_message->GetReflection()->SetInt32(sub_message, nested_b_, 318); + sub_message = reflection->AddMessage(message, F("repeated_foreign_message")); + sub_message->GetReflection()->SetInt32(sub_message, foreign_c_, 319); + sub_message = reflection->AddMessage(message, F("repeated_import_message")); + sub_message->GetReflection()->SetInt32(sub_message, import_d_, 320); + + reflection->AddEnum(message, F("repeated_nested_enum" ), nested_baz_); + reflection->AddEnum(message, F("repeated_foreign_enum"), foreign_baz_); + reflection->AddEnum(message, F("repeated_import_enum" ), import_baz_); + + reflection->AddString(message, F("repeated_string_piece"), "324"); + reflection->AddString(message, F("repeated_cord"), "325"); + + // ----------------------------------------------------------------- + + reflection->SetInt32 (message, F("default_int32" ), 401); + reflection->SetInt64 (message, F("default_int64" ), 402); + reflection->SetUInt32(message, F("default_uint32" ), 403); + reflection->SetUInt64(message, F("default_uint64" ), 404); + reflection->SetInt32 (message, F("default_sint32" ), 405); + reflection->SetInt64 (message, F("default_sint64" ), 406); + reflection->SetUInt32(message, F("default_fixed32" ), 407); + reflection->SetUInt64(message, F("default_fixed64" ), 408); + reflection->SetInt32 (message, F("default_sfixed32"), 409); + reflection->SetInt64 (message, F("default_sfixed64"), 410); + reflection->SetFloat (message, F("default_float" ), 411); + reflection->SetDouble(message, F("default_double" ), 412); + reflection->SetBool (message, F("default_bool" ), false); + reflection->SetString(message, F("default_string" ), "415"); + reflection->SetString(message, F("default_bytes" ), "416"); + + reflection->SetEnum(message, F("default_nested_enum" ), nested_foo_); + reflection->SetEnum(message, F("default_foreign_enum"), foreign_foo_); + reflection->SetEnum(message, F("default_import_enum" ), import_foo_); + + reflection->SetString(message, F("default_string_piece"), "424"); + reflection->SetString(message, F("default_cord"), "425"); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ExpectAllFieldsSetViaReflection( + const Message& message) { + const Reflection* reflection = message.GetReflection(); + string scratch; + const Message* sub_message; + + EXPECT_TRUE(reflection->HasField(message, F("optional_int32" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_int64" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_uint32" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_uint64" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_sint32" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_sint64" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_fixed32" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_fixed64" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_sfixed32"))); + EXPECT_TRUE(reflection->HasField(message, F("optional_sfixed64"))); + EXPECT_TRUE(reflection->HasField(message, F("optional_float" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_double" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_bool" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_string" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_bytes" ))); + + EXPECT_TRUE(reflection->HasField(message, F("optionalgroup" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_nested_message" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_foreign_message"))); + EXPECT_TRUE(reflection->HasField(message, F("optional_import_message" ))); + + sub_message = &reflection->GetMessage(message, F("optionalgroup")); + EXPECT_TRUE(sub_message->GetReflection()->HasField(*sub_message, group_a_)); + sub_message = &reflection->GetMessage(message, F("optional_nested_message")); + EXPECT_TRUE(sub_message->GetReflection()->HasField(*sub_message, nested_b_)); + sub_message = &reflection->GetMessage(message, F("optional_foreign_message")); + EXPECT_TRUE(sub_message->GetReflection()->HasField(*sub_message, foreign_c_)); + sub_message = &reflection->GetMessage(message, F("optional_import_message")); + EXPECT_TRUE(sub_message->GetReflection()->HasField(*sub_message, import_d_)); + + EXPECT_TRUE(reflection->HasField(message, F("optional_nested_enum" ))); + EXPECT_TRUE(reflection->HasField(message, F("optional_foreign_enum"))); + EXPECT_TRUE(reflection->HasField(message, F("optional_import_enum" ))); + + EXPECT_TRUE(reflection->HasField(message, F("optional_string_piece"))); + EXPECT_TRUE(reflection->HasField(message, F("optional_cord"))); + + EXPECT_EQ(101 , reflection->GetInt32 (message, F("optional_int32" ))); + EXPECT_EQ(102 , reflection->GetInt64 (message, F("optional_int64" ))); + EXPECT_EQ(103 , reflection->GetUInt32(message, F("optional_uint32" ))); + EXPECT_EQ(104 , reflection->GetUInt64(message, F("optional_uint64" ))); + EXPECT_EQ(105 , reflection->GetInt32 (message, F("optional_sint32" ))); + EXPECT_EQ(106 , reflection->GetInt64 (message, F("optional_sint64" ))); + EXPECT_EQ(107 , reflection->GetUInt32(message, F("optional_fixed32" ))); + EXPECT_EQ(108 , reflection->GetUInt64(message, F("optional_fixed64" ))); + EXPECT_EQ(109 , reflection->GetInt32 (message, F("optional_sfixed32"))); + EXPECT_EQ(110 , reflection->GetInt64 (message, F("optional_sfixed64"))); + EXPECT_EQ(111 , reflection->GetFloat (message, F("optional_float" ))); + EXPECT_EQ(112 , reflection->GetDouble(message, F("optional_double" ))); + EXPECT_EQ(true , reflection->GetBool (message, F("optional_bool" ))); + EXPECT_EQ("115", reflection->GetString(message, F("optional_string" ))); + EXPECT_EQ("116", reflection->GetString(message, F("optional_bytes" ))); + + EXPECT_EQ("115", reflection->GetStringReference(message, F("optional_string"), &scratch)); + EXPECT_EQ("116", reflection->GetStringReference(message, F("optional_bytes" ), &scratch)); + + sub_message = &reflection->GetMessage(message, F("optionalgroup")); + EXPECT_EQ(117, sub_message->GetReflection()->GetInt32(*sub_message, group_a_)); + sub_message = &reflection->GetMessage(message, F("optional_nested_message")); + EXPECT_EQ(118, sub_message->GetReflection()->GetInt32(*sub_message, nested_b_)); + sub_message = &reflection->GetMessage(message, F("optional_foreign_message")); + EXPECT_EQ(119, sub_message->GetReflection()->GetInt32(*sub_message, foreign_c_)); + sub_message = &reflection->GetMessage(message, F("optional_import_message")); + EXPECT_EQ(120, sub_message->GetReflection()->GetInt32(*sub_message, import_d_)); + + EXPECT_EQ( nested_baz_, reflection->GetEnum(message, F("optional_nested_enum" ))); + EXPECT_EQ(foreign_baz_, reflection->GetEnum(message, F("optional_foreign_enum"))); + EXPECT_EQ( import_baz_, reflection->GetEnum(message, F("optional_import_enum" ))); + + EXPECT_EQ("124", reflection->GetString(message, F("optional_string_piece"))); + EXPECT_EQ("124", reflection->GetStringReference(message, F("optional_string_piece"), &scratch)); + + EXPECT_EQ("125", reflection->GetString(message, F("optional_cord"))); + EXPECT_EQ("125", reflection->GetStringReference(message, F("optional_cord"), &scratch)); + + // ----------------------------------------------------------------- + + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_int32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_int64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_uint32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_uint64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_sint32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_sint64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_fixed32" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_fixed64" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_sfixed32"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_sfixed64"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_float" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_double" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_bool" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_string" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_bytes" ))); + + ASSERT_EQ(2, reflection->FieldSize(message, F("repeatedgroup" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_nested_message" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_foreign_message"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_import_message" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_nested_enum" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_foreign_enum" ))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_import_enum" ))); + + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_string_piece"))); + ASSERT_EQ(2, reflection->FieldSize(message, F("repeated_cord"))); + + EXPECT_EQ(201 , reflection->GetRepeatedInt32 (message, F("repeated_int32" ), 0)); + EXPECT_EQ(202 , reflection->GetRepeatedInt64 (message, F("repeated_int64" ), 0)); + EXPECT_EQ(203 , reflection->GetRepeatedUInt32(message, F("repeated_uint32" ), 0)); + EXPECT_EQ(204 , reflection->GetRepeatedUInt64(message, F("repeated_uint64" ), 0)); + EXPECT_EQ(205 , reflection->GetRepeatedInt32 (message, F("repeated_sint32" ), 0)); + EXPECT_EQ(206 , reflection->GetRepeatedInt64 (message, F("repeated_sint64" ), 0)); + EXPECT_EQ(207 , reflection->GetRepeatedUInt32(message, F("repeated_fixed32" ), 0)); + EXPECT_EQ(208 , reflection->GetRepeatedUInt64(message, F("repeated_fixed64" ), 0)); + EXPECT_EQ(209 , reflection->GetRepeatedInt32 (message, F("repeated_sfixed32"), 0)); + EXPECT_EQ(210 , reflection->GetRepeatedInt64 (message, F("repeated_sfixed64"), 0)); + EXPECT_EQ(211 , reflection->GetRepeatedFloat (message, F("repeated_float" ), 0)); + EXPECT_EQ(212 , reflection->GetRepeatedDouble(message, F("repeated_double" ), 0)); + EXPECT_EQ(true , reflection->GetRepeatedBool (message, F("repeated_bool" ), 0)); + EXPECT_EQ("215", reflection->GetRepeatedString(message, F("repeated_string" ), 0)); + EXPECT_EQ("216", reflection->GetRepeatedString(message, F("repeated_bytes" ), 0)); + + EXPECT_EQ("215", reflection->GetRepeatedStringReference(message, F("repeated_string"), 0, &scratch)); + EXPECT_EQ("216", reflection->GetRepeatedStringReference(message, F("repeated_bytes"), 0, &scratch)); + + sub_message = &reflection->GetRepeatedMessage(message, F("repeatedgroup"), 0); + EXPECT_EQ(217, sub_message->GetReflection()->GetInt32(*sub_message, repeated_group_a_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_nested_message"), 0); + EXPECT_EQ(218, sub_message->GetReflection()->GetInt32(*sub_message, nested_b_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_foreign_message"), 0); + EXPECT_EQ(219, sub_message->GetReflection()->GetInt32(*sub_message, foreign_c_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_import_message"), 0); + EXPECT_EQ(220, sub_message->GetReflection()->GetInt32(*sub_message, import_d_)); + + EXPECT_EQ( nested_bar_, reflection->GetRepeatedEnum(message, F("repeated_nested_enum" ),0)); + EXPECT_EQ(foreign_bar_, reflection->GetRepeatedEnum(message, F("repeated_foreign_enum"),0)); + EXPECT_EQ( import_bar_, reflection->GetRepeatedEnum(message, F("repeated_import_enum" ),0)); + + EXPECT_EQ("224", reflection->GetRepeatedString(message, F("repeated_string_piece"), 0)); + EXPECT_EQ("224", reflection->GetRepeatedStringReference( + message, F("repeated_string_piece"), 0, &scratch)); + + EXPECT_EQ("225", reflection->GetRepeatedString(message, F("repeated_cord"), 0)); + EXPECT_EQ("225", reflection->GetRepeatedStringReference( + message, F("repeated_cord"), 0, &scratch)); + + EXPECT_EQ(301 , reflection->GetRepeatedInt32 (message, F("repeated_int32" ), 1)); + EXPECT_EQ(302 , reflection->GetRepeatedInt64 (message, F("repeated_int64" ), 1)); + EXPECT_EQ(303 , reflection->GetRepeatedUInt32(message, F("repeated_uint32" ), 1)); + EXPECT_EQ(304 , reflection->GetRepeatedUInt64(message, F("repeated_uint64" ), 1)); + EXPECT_EQ(305 , reflection->GetRepeatedInt32 (message, F("repeated_sint32" ), 1)); + EXPECT_EQ(306 , reflection->GetRepeatedInt64 (message, F("repeated_sint64" ), 1)); + EXPECT_EQ(307 , reflection->GetRepeatedUInt32(message, F("repeated_fixed32" ), 1)); + EXPECT_EQ(308 , reflection->GetRepeatedUInt64(message, F("repeated_fixed64" ), 1)); + EXPECT_EQ(309 , reflection->GetRepeatedInt32 (message, F("repeated_sfixed32"), 1)); + EXPECT_EQ(310 , reflection->GetRepeatedInt64 (message, F("repeated_sfixed64"), 1)); + EXPECT_EQ(311 , reflection->GetRepeatedFloat (message, F("repeated_float" ), 1)); + EXPECT_EQ(312 , reflection->GetRepeatedDouble(message, F("repeated_double" ), 1)); + EXPECT_EQ(false, reflection->GetRepeatedBool (message, F("repeated_bool" ), 1)); + EXPECT_EQ("315", reflection->GetRepeatedString(message, F("repeated_string" ), 1)); + EXPECT_EQ("316", reflection->GetRepeatedString(message, F("repeated_bytes" ), 1)); + + EXPECT_EQ("315", reflection->GetRepeatedStringReference(message, F("repeated_string"), + 1, &scratch)); + EXPECT_EQ("316", reflection->GetRepeatedStringReference(message, F("repeated_bytes"), + 1, &scratch)); + + sub_message = &reflection->GetRepeatedMessage(message, F("repeatedgroup"), 1); + EXPECT_EQ(317, sub_message->GetReflection()->GetInt32(*sub_message, repeated_group_a_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_nested_message"), 1); + EXPECT_EQ(318, sub_message->GetReflection()->GetInt32(*sub_message, nested_b_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_foreign_message"), 1); + EXPECT_EQ(319, sub_message->GetReflection()->GetInt32(*sub_message, foreign_c_)); + sub_message = &reflection->GetRepeatedMessage(message, F("repeated_import_message"), 1); + EXPECT_EQ(320, sub_message->GetReflection()->GetInt32(*sub_message, import_d_)); + + EXPECT_EQ( nested_baz_, reflection->GetRepeatedEnum(message, F("repeated_nested_enum" ),1)); + EXPECT_EQ(foreign_baz_, reflection->GetRepeatedEnum(message, F("repeated_foreign_enum"),1)); + EXPECT_EQ( import_baz_, reflection->GetRepeatedEnum(message, F("repeated_import_enum" ),1)); + + EXPECT_EQ("324", reflection->GetRepeatedString(message, F("repeated_string_piece"), 1)); + EXPECT_EQ("324", reflection->GetRepeatedStringReference( + message, F("repeated_string_piece"), 1, &scratch)); + + EXPECT_EQ("325", reflection->GetRepeatedString(message, F("repeated_cord"), 1)); + EXPECT_EQ("325", reflection->GetRepeatedStringReference( + message, F("repeated_cord"), 1, &scratch)); + + // ----------------------------------------------------------------- + + EXPECT_TRUE(reflection->HasField(message, F("default_int32" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_int64" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_uint32" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_uint64" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_sint32" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_sint64" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_fixed32" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_fixed64" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_sfixed32"))); + EXPECT_TRUE(reflection->HasField(message, F("default_sfixed64"))); + EXPECT_TRUE(reflection->HasField(message, F("default_float" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_double" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_bool" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_string" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_bytes" ))); + + EXPECT_TRUE(reflection->HasField(message, F("default_nested_enum" ))); + EXPECT_TRUE(reflection->HasField(message, F("default_foreign_enum"))); + EXPECT_TRUE(reflection->HasField(message, F("default_import_enum" ))); + + EXPECT_TRUE(reflection->HasField(message, F("default_string_piece"))); + EXPECT_TRUE(reflection->HasField(message, F("default_cord"))); + + EXPECT_EQ(401 , reflection->GetInt32 (message, F("default_int32" ))); + EXPECT_EQ(402 , reflection->GetInt64 (message, F("default_int64" ))); + EXPECT_EQ(403 , reflection->GetUInt32(message, F("default_uint32" ))); + EXPECT_EQ(404 , reflection->GetUInt64(message, F("default_uint64" ))); + EXPECT_EQ(405 , reflection->GetInt32 (message, F("default_sint32" ))); + EXPECT_EQ(406 , reflection->GetInt64 (message, F("default_sint64" ))); + EXPECT_EQ(407 , reflection->GetUInt32(message, F("default_fixed32" ))); + EXPECT_EQ(408 , reflection->GetUInt64(message, F("default_fixed64" ))); + EXPECT_EQ(409 , reflection->GetInt32 (message, F("default_sfixed32"))); + EXPECT_EQ(410 , reflection->GetInt64 (message, F("default_sfixed64"))); + EXPECT_EQ(411 , reflection->GetFloat (message, F("default_float" ))); + EXPECT_EQ(412 , reflection->GetDouble(message, F("default_double" ))); + EXPECT_EQ(false, reflection->GetBool (message, F("default_bool" ))); + EXPECT_EQ("415", reflection->GetString(message, F("default_string" ))); + EXPECT_EQ("416", reflection->GetString(message, F("default_bytes" ))); + + EXPECT_EQ("415", reflection->GetStringReference(message, F("default_string"), &scratch)); + EXPECT_EQ("416", reflection->GetStringReference(message, F("default_bytes" ), &scratch)); + + EXPECT_EQ( nested_foo_, reflection->GetEnum(message, F("default_nested_enum" ))); + EXPECT_EQ(foreign_foo_, reflection->GetEnum(message, F("default_foreign_enum"))); + EXPECT_EQ( import_foo_, reflection->GetEnum(message, F("default_import_enum" ))); + + EXPECT_EQ("424", reflection->GetString(message, F("default_string_piece"))); + EXPECT_EQ("424", reflection->GetStringReference(message, F("default_string_piece"), + &scratch)); + + EXPECT_EQ("425", reflection->GetString(message, F("default_cord"))); + EXPECT_EQ("425", reflection->GetStringReference(message, F("default_cord"), &scratch)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ExpectClearViaReflection( + const Message& message) { + const Reflection* reflection = message.GetReflection(); + string scratch; + const Message* sub_message; + + // has_blah() should initially be false for all optional fields. + EXPECT_FALSE(reflection->HasField(message, F("optional_int32" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_int64" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_uint32" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_uint64" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_sint32" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_sint64" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_fixed32" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_fixed64" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_sfixed32"))); + EXPECT_FALSE(reflection->HasField(message, F("optional_sfixed64"))); + EXPECT_FALSE(reflection->HasField(message, F("optional_float" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_double" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_bool" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_string" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_bytes" ))); + + EXPECT_FALSE(reflection->HasField(message, F("optionalgroup" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_nested_message" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_foreign_message"))); + EXPECT_FALSE(reflection->HasField(message, F("optional_import_message" ))); + + EXPECT_FALSE(reflection->HasField(message, F("optional_nested_enum" ))); + EXPECT_FALSE(reflection->HasField(message, F("optional_foreign_enum"))); + EXPECT_FALSE(reflection->HasField(message, F("optional_import_enum" ))); + + EXPECT_FALSE(reflection->HasField(message, F("optional_string_piece"))); + EXPECT_FALSE(reflection->HasField(message, F("optional_cord"))); + + // Optional fields without defaults are set to zero or something like it. + EXPECT_EQ(0 , reflection->GetInt32 (message, F("optional_int32" ))); + EXPECT_EQ(0 , reflection->GetInt64 (message, F("optional_int64" ))); + EXPECT_EQ(0 , reflection->GetUInt32(message, F("optional_uint32" ))); + EXPECT_EQ(0 , reflection->GetUInt64(message, F("optional_uint64" ))); + EXPECT_EQ(0 , reflection->GetInt32 (message, F("optional_sint32" ))); + EXPECT_EQ(0 , reflection->GetInt64 (message, F("optional_sint64" ))); + EXPECT_EQ(0 , reflection->GetUInt32(message, F("optional_fixed32" ))); + EXPECT_EQ(0 , reflection->GetUInt64(message, F("optional_fixed64" ))); + EXPECT_EQ(0 , reflection->GetInt32 (message, F("optional_sfixed32"))); + EXPECT_EQ(0 , reflection->GetInt64 (message, F("optional_sfixed64"))); + EXPECT_EQ(0 , reflection->GetFloat (message, F("optional_float" ))); + EXPECT_EQ(0 , reflection->GetDouble(message, F("optional_double" ))); + EXPECT_EQ(false, reflection->GetBool (message, F("optional_bool" ))); + EXPECT_EQ("" , reflection->GetString(message, F("optional_string" ))); + EXPECT_EQ("" , reflection->GetString(message, F("optional_bytes" ))); + + EXPECT_EQ("", reflection->GetStringReference(message, F("optional_string"), &scratch)); + EXPECT_EQ("", reflection->GetStringReference(message, F("optional_bytes" ), &scratch)); + + // Embedded messages should also be clear. + sub_message = &reflection->GetMessage(message, F("optionalgroup")); + EXPECT_FALSE(sub_message->GetReflection()->HasField(*sub_message, group_a_)); + EXPECT_EQ(0, sub_message->GetReflection()->GetInt32(*sub_message, group_a_)); + sub_message = &reflection->GetMessage(message, F("optional_nested_message")); + EXPECT_FALSE(sub_message->GetReflection()->HasField(*sub_message, nested_b_)); + EXPECT_EQ(0, sub_message->GetReflection()->GetInt32(*sub_message, nested_b_)); + sub_message = &reflection->GetMessage(message, F("optional_foreign_message")); + EXPECT_FALSE(sub_message->GetReflection()->HasField(*sub_message, foreign_c_)); + EXPECT_EQ(0, sub_message->GetReflection()->GetInt32(*sub_message, foreign_c_)); + sub_message = &reflection->GetMessage(message, F("optional_import_message")); + EXPECT_FALSE(sub_message->GetReflection()->HasField(*sub_message, import_d_)); + EXPECT_EQ(0, sub_message->GetReflection()->GetInt32(*sub_message, import_d_)); + + // Enums without defaults are set to the first value in the enum. + EXPECT_EQ( nested_foo_, reflection->GetEnum(message, F("optional_nested_enum" ))); + EXPECT_EQ(foreign_foo_, reflection->GetEnum(message, F("optional_foreign_enum"))); + EXPECT_EQ( import_foo_, reflection->GetEnum(message, F("optional_import_enum" ))); + + EXPECT_EQ("", reflection->GetString(message, F("optional_string_piece"))); + EXPECT_EQ("", reflection->GetStringReference(message, F("optional_string_piece"), &scratch)); + + EXPECT_EQ("", reflection->GetString(message, F("optional_cord"))); + EXPECT_EQ("", reflection->GetStringReference(message, F("optional_cord"), &scratch)); + + // Repeated fields are empty. + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_int32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_int64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_uint32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_uint64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_sint32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_sint64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_fixed32" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_fixed64" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_sfixed32"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_sfixed64"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_float" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_double" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_bool" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_string" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_bytes" ))); + + EXPECT_EQ(0, reflection->FieldSize(message, F("repeatedgroup" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_nested_message" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_foreign_message"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_import_message" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_nested_enum" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_foreign_enum" ))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_import_enum" ))); + + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_string_piece"))); + EXPECT_EQ(0, reflection->FieldSize(message, F("repeated_cord"))); + + // has_blah() should also be false for all default fields. + EXPECT_FALSE(reflection->HasField(message, F("default_int32" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_int64" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_uint32" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_uint64" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_sint32" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_sint64" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_fixed32" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_fixed64" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_sfixed32"))); + EXPECT_FALSE(reflection->HasField(message, F("default_sfixed64"))); + EXPECT_FALSE(reflection->HasField(message, F("default_float" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_double" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_bool" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_string" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_bytes" ))); + + EXPECT_FALSE(reflection->HasField(message, F("default_nested_enum" ))); + EXPECT_FALSE(reflection->HasField(message, F("default_foreign_enum"))); + EXPECT_FALSE(reflection->HasField(message, F("default_import_enum" ))); + + EXPECT_FALSE(reflection->HasField(message, F("default_string_piece"))); + EXPECT_FALSE(reflection->HasField(message, F("default_cord"))); + + // Fields with defaults have their default values (duh). + EXPECT_EQ( 41 , reflection->GetInt32 (message, F("default_int32" ))); + EXPECT_EQ( 42 , reflection->GetInt64 (message, F("default_int64" ))); + EXPECT_EQ( 43 , reflection->GetUInt32(message, F("default_uint32" ))); + EXPECT_EQ( 44 , reflection->GetUInt64(message, F("default_uint64" ))); + EXPECT_EQ(-45 , reflection->GetInt32 (message, F("default_sint32" ))); + EXPECT_EQ( 46 , reflection->GetInt64 (message, F("default_sint64" ))); + EXPECT_EQ( 47 , reflection->GetUInt32(message, F("default_fixed32" ))); + EXPECT_EQ( 48 , reflection->GetUInt64(message, F("default_fixed64" ))); + EXPECT_EQ( 49 , reflection->GetInt32 (message, F("default_sfixed32"))); + EXPECT_EQ(-50 , reflection->GetInt64 (message, F("default_sfixed64"))); + EXPECT_EQ( 51.5 , reflection->GetFloat (message, F("default_float" ))); + EXPECT_EQ( 52e3 , reflection->GetDouble(message, F("default_double" ))); + EXPECT_EQ(true , reflection->GetBool (message, F("default_bool" ))); + EXPECT_EQ("hello", reflection->GetString(message, F("default_string" ))); + EXPECT_EQ("world", reflection->GetString(message, F("default_bytes" ))); + + EXPECT_EQ("hello", reflection->GetStringReference(message, F("default_string"), &scratch)); + EXPECT_EQ("world", reflection->GetStringReference(message, F("default_bytes" ), &scratch)); + + EXPECT_EQ( nested_bar_, reflection->GetEnum(message, F("default_nested_enum" ))); + EXPECT_EQ(foreign_bar_, reflection->GetEnum(message, F("default_foreign_enum"))); + EXPECT_EQ( import_bar_, reflection->GetEnum(message, F("default_import_enum" ))); + + EXPECT_EQ("abc", reflection->GetString(message, F("default_string_piece"))); + EXPECT_EQ("abc", reflection->GetStringReference(message, F("default_string_piece"), &scratch)); + + EXPECT_EQ("123", reflection->GetString(message, F("default_cord"))); + EXPECT_EQ("123", reflection->GetStringReference(message, F("default_cord"), &scratch)); +} + +// ------------------------------------------------------------------- + +void TestUtil::ReflectionTester::ModifyRepeatedFieldsViaReflection( + Message* message) { + const Reflection* reflection = message->GetReflection(); + Message* sub_message; + + reflection->SetRepeatedInt32 (message, F("repeated_int32" ), 1, 501); + reflection->SetRepeatedInt64 (message, F("repeated_int64" ), 1, 502); + reflection->SetRepeatedUInt32(message, F("repeated_uint32" ), 1, 503); + reflection->SetRepeatedUInt64(message, F("repeated_uint64" ), 1, 504); + reflection->SetRepeatedInt32 (message, F("repeated_sint32" ), 1, 505); + reflection->SetRepeatedInt64 (message, F("repeated_sint64" ), 1, 506); + reflection->SetRepeatedUInt32(message, F("repeated_fixed32" ), 1, 507); + reflection->SetRepeatedUInt64(message, F("repeated_fixed64" ), 1, 508); + reflection->SetRepeatedInt32 (message, F("repeated_sfixed32"), 1, 509); + reflection->SetRepeatedInt64 (message, F("repeated_sfixed64"), 1, 510); + reflection->SetRepeatedFloat (message, F("repeated_float" ), 1, 511); + reflection->SetRepeatedDouble(message, F("repeated_double" ), 1, 512); + reflection->SetRepeatedBool (message, F("repeated_bool" ), 1, true); + reflection->SetRepeatedString(message, F("repeated_string" ), 1, "515"); + reflection->SetRepeatedString(message, F("repeated_bytes" ), 1, "516"); + + sub_message = reflection->MutableRepeatedMessage(message, F("repeatedgroup"), 1); + sub_message->GetReflection()->SetInt32(sub_message, repeated_group_a_, 517); + sub_message = reflection->MutableRepeatedMessage(message, F("repeated_nested_message"), 1); + sub_message->GetReflection()->SetInt32(sub_message, nested_b_, 518); + sub_message = reflection->MutableRepeatedMessage(message, F("repeated_foreign_message"), 1); + sub_message->GetReflection()->SetInt32(sub_message, foreign_c_, 519); + sub_message = reflection->MutableRepeatedMessage(message, F("repeated_import_message"), 1); + sub_message->GetReflection()->SetInt32(sub_message, import_d_, 520); + + reflection->SetRepeatedEnum(message, F("repeated_nested_enum" ), 1, nested_foo_); + reflection->SetRepeatedEnum(message, F("repeated_foreign_enum"), 1, foreign_foo_); + reflection->SetRepeatedEnum(message, F("repeated_import_enum" ), 1, import_foo_); + + reflection->SetRepeatedString(message, F("repeated_string_piece"), 1, "524"); + reflection->SetRepeatedString(message, F("repeated_cord"), 1, "525"); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/test_util.h b/src/google/protobuf/test_util.h new file mode 100644 index 00000000..4c578bcd --- /dev/null +++ b/src/google/protobuf/test_util.h @@ -0,0 +1,117 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_TEST_UTIL_H__ +#define GOOGLE_PROTOBUF_TEST_UTIL_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace unittest = protobuf_unittest; +namespace unittest_import = protobuf_unittest_import; + +class TestUtil { + public: + // Set every field in the message to a unique value. + static void SetAllFields(unittest::TestAllTypes* message); + static void SetAllExtensions(unittest::TestAllExtensions* message); + static void SetAllFieldsAndExtensions(unittest::TestFieldOrderings* message); + + // Use the repeated versions of the set_*() accessors to modify all the + // repeated fields of the messsage (which should already have been + // initialized with SetAllFields()). SetAllFields() itself only tests + // the add_*() accessors. + static void ModifyRepeatedFields(unittest::TestAllTypes* message); + static void ModifyRepeatedExtensions(unittest::TestAllExtensions* message); + + // Check that all fields have the values that they should have after + // SetAllFields() is called. + static void ExpectAllFieldsSet(const unittest::TestAllTypes& message); + static void ExpectAllExtensionsSet( + const unittest::TestAllExtensions& message); + + // Expect that the message is modified as would be expected from + // ModifyRepeatedFields(). + static void ExpectRepeatedFieldsModified( + const unittest::TestAllTypes& message); + static void ExpectRepeatedExtensionsModified( + const unittest::TestAllExtensions& message); + + // Check that all fields have their default values. + static void ExpectClear(const unittest::TestAllTypes& message); + static void ExpectExtensionsClear(const unittest::TestAllExtensions& message); + + // Check that the passed-in serialization is the canonical serialization we + // expect for a TestFieldOrderings message filled in by + // SetAllFieldsAndExtensions(). + static void ExpectAllFieldsAndExtensionsInOrder(const string& serialized); + + // Like above, but use the reflection interface. + class ReflectionTester { + public: + // base_descriptor must be a descriptor for TestAllTypes or + // TestAllExtensions. In the former case, ReflectionTester fetches from + // it the FieldDescriptors needed to use the reflection interface. In + // the latter case, ReflectionTester searches for extension fields in + // its file. + explicit ReflectionTester(const Descriptor* base_descriptor); + + void SetAllFieldsViaReflection(Message* message); + void ModifyRepeatedFieldsViaReflection(Message* message); + void ExpectAllFieldsSetViaReflection(const Message& message); + void ExpectClearViaReflection(const Message& message); + + private: + const FieldDescriptor* F(const string& name); + + const Descriptor* base_descriptor_; + + const FieldDescriptor* group_a_; + const FieldDescriptor* repeated_group_a_; + const FieldDescriptor* nested_b_; + const FieldDescriptor* foreign_c_; + const FieldDescriptor* import_d_; + + const EnumValueDescriptor* nested_foo_; + const EnumValueDescriptor* nested_bar_; + const EnumValueDescriptor* nested_baz_; + const EnumValueDescriptor* foreign_foo_; + const EnumValueDescriptor* foreign_bar_; + const EnumValueDescriptor* foreign_baz_; + const EnumValueDescriptor* import_foo_; + const EnumValueDescriptor* import_bar_; + const EnumValueDescriptor* import_baz_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ReflectionTester); + }; + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TestUtil); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_TEST_UTIL_H__ diff --git a/src/google/protobuf/testdata/golden_message b/src/google/protobuf/testdata/golden_message new file mode 100644 index 00000000..94898e49 Binary files /dev/null and b/src/google/protobuf/testdata/golden_message differ diff --git a/src/google/protobuf/testdata/text_format_unittest_data.txt b/src/google/protobuf/testdata/text_format_unittest_data.txt new file mode 100644 index 00000000..feea8f7b --- /dev/null +++ b/src/google/protobuf/testdata/text_format_unittest_data.txt @@ -0,0 +1,116 @@ +optional_int32: 101 +optional_int64: 102 +optional_uint32: 103 +optional_uint64: 104 +optional_sint32: 105 +optional_sint64: 106 +optional_fixed32: 107 +optional_fixed64: 108 +optional_sfixed32: 109 +optional_sfixed64: 110 +optional_float: 111 +optional_double: 112 +optional_bool: true +optional_string: "115" +optional_bytes: "116" +OptionalGroup { + a: 117 +} +optional_nested_message { + bb: 118 +} +optional_foreign_message { + c: 119 +} +optional_import_message { + d: 120 +} +optional_nested_enum: BAZ +optional_foreign_enum: FOREIGN_BAZ +optional_import_enum: IMPORT_BAZ +optional_string_piece: "124" +optional_cord: "125" +repeated_int32: 201 +repeated_int32: 301 +repeated_int64: 202 +repeated_int64: 302 +repeated_uint32: 203 +repeated_uint32: 303 +repeated_uint64: 204 +repeated_uint64: 304 +repeated_sint32: 205 +repeated_sint32: 305 +repeated_sint64: 206 +repeated_sint64: 306 +repeated_fixed32: 207 +repeated_fixed32: 307 +repeated_fixed64: 208 +repeated_fixed64: 308 +repeated_sfixed32: 209 +repeated_sfixed32: 309 +repeated_sfixed64: 210 +repeated_sfixed64: 310 +repeated_float: 211 +repeated_float: 311 +repeated_double: 212 +repeated_double: 312 +repeated_bool: true +repeated_bool: false +repeated_string: "215" +repeated_string: "315" +repeated_bytes: "216" +repeated_bytes: "316" +RepeatedGroup { + a: 217 +} +RepeatedGroup { + a: 317 +} +repeated_nested_message { + bb: 218 +} +repeated_nested_message { + bb: 318 +} +repeated_foreign_message { + c: 219 +} +repeated_foreign_message { + c: 319 +} +repeated_import_message { + d: 220 +} +repeated_import_message { + d: 320 +} +repeated_nested_enum: BAR +repeated_nested_enum: BAZ +repeated_foreign_enum: FOREIGN_BAR +repeated_foreign_enum: FOREIGN_BAZ +repeated_import_enum: IMPORT_BAR +repeated_import_enum: IMPORT_BAZ +repeated_string_piece: "224" +repeated_string_piece: "324" +repeated_cord: "225" +repeated_cord: "325" +default_int32: 401 +default_int64: 402 +default_uint32: 403 +default_uint64: 404 +default_sint32: 405 +default_sint64: 406 +default_fixed32: 407 +default_fixed64: 408 +default_sfixed32: 409 +default_sfixed64: 410 +default_float: 411 +default_double: 412 +default_bool: false +default_string: "415" +default_bytes: "416" +default_nested_enum: FOO +default_foreign_enum: FOREIGN_FOO +default_import_enum: IMPORT_FOO +default_string_piece: "424" +default_cord: "425" diff --git a/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt b/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt new file mode 100644 index 00000000..057beae8 --- /dev/null +++ b/src/google/protobuf/testdata/text_format_unittest_extensions_data.txt @@ -0,0 +1,116 @@ +[protobuf_unittest.optional_int32_extension]: 101 +[protobuf_unittest.optional_int64_extension]: 102 +[protobuf_unittest.optional_uint32_extension]: 103 +[protobuf_unittest.optional_uint64_extension]: 104 +[protobuf_unittest.optional_sint32_extension]: 105 +[protobuf_unittest.optional_sint64_extension]: 106 +[protobuf_unittest.optional_fixed32_extension]: 107 +[protobuf_unittest.optional_fixed64_extension]: 108 +[protobuf_unittest.optional_sfixed32_extension]: 109 +[protobuf_unittest.optional_sfixed64_extension]: 110 +[protobuf_unittest.optional_float_extension]: 111 +[protobuf_unittest.optional_double_extension]: 112 +[protobuf_unittest.optional_bool_extension]: true +[protobuf_unittest.optional_string_extension]: "115" +[protobuf_unittest.optional_bytes_extension]: "116" +[protobuf_unittest.optionalgroup_extension] { + a: 117 +} +[protobuf_unittest.optional_nested_message_extension] { + bb: 118 +} +[protobuf_unittest.optional_foreign_message_extension] { + c: 119 +} +[protobuf_unittest.optional_import_message_extension] { + d: 120 +} +[protobuf_unittest.optional_nested_enum_extension]: BAZ +[protobuf_unittest.optional_foreign_enum_extension]: FOREIGN_BAZ +[protobuf_unittest.optional_import_enum_extension]: IMPORT_BAZ +[protobuf_unittest.optional_string_piece_extension]: "124" +[protobuf_unittest.optional_cord_extension]: "125" +[protobuf_unittest.repeated_int32_extension]: 201 +[protobuf_unittest.repeated_int32_extension]: 301 +[protobuf_unittest.repeated_int64_extension]: 202 +[protobuf_unittest.repeated_int64_extension]: 302 +[protobuf_unittest.repeated_uint32_extension]: 203 +[protobuf_unittest.repeated_uint32_extension]: 303 +[protobuf_unittest.repeated_uint64_extension]: 204 +[protobuf_unittest.repeated_uint64_extension]: 304 +[protobuf_unittest.repeated_sint32_extension]: 205 +[protobuf_unittest.repeated_sint32_extension]: 305 +[protobuf_unittest.repeated_sint64_extension]: 206 +[protobuf_unittest.repeated_sint64_extension]: 306 +[protobuf_unittest.repeated_fixed32_extension]: 207 +[protobuf_unittest.repeated_fixed32_extension]: 307 +[protobuf_unittest.repeated_fixed64_extension]: 208 +[protobuf_unittest.repeated_fixed64_extension]: 308 +[protobuf_unittest.repeated_sfixed32_extension]: 209 +[protobuf_unittest.repeated_sfixed32_extension]: 309 +[protobuf_unittest.repeated_sfixed64_extension]: 210 +[protobuf_unittest.repeated_sfixed64_extension]: 310 +[protobuf_unittest.repeated_float_extension]: 211 +[protobuf_unittest.repeated_float_extension]: 311 +[protobuf_unittest.repeated_double_extension]: 212 +[protobuf_unittest.repeated_double_extension]: 312 +[protobuf_unittest.repeated_bool_extension]: true +[protobuf_unittest.repeated_bool_extension]: false +[protobuf_unittest.repeated_string_extension]: "215" +[protobuf_unittest.repeated_string_extension]: "315" +[protobuf_unittest.repeated_bytes_extension]: "216" +[protobuf_unittest.repeated_bytes_extension]: "316" +[protobuf_unittest.repeatedgroup_extension] { + a: 217 +} +[protobuf_unittest.repeatedgroup_extension] { + a: 317 +} +[protobuf_unittest.repeated_nested_message_extension] { + bb: 218 +} +[protobuf_unittest.repeated_nested_message_extension] { + bb: 318 +} +[protobuf_unittest.repeated_foreign_message_extension] { + c: 219 +} +[protobuf_unittest.repeated_foreign_message_extension] { + c: 319 +} +[protobuf_unittest.repeated_import_message_extension] { + d: 220 +} +[protobuf_unittest.repeated_import_message_extension] { + d: 320 +} +[protobuf_unittest.repeated_nested_enum_extension]: BAR +[protobuf_unittest.repeated_nested_enum_extension]: BAZ +[protobuf_unittest.repeated_foreign_enum_extension]: FOREIGN_BAR +[protobuf_unittest.repeated_foreign_enum_extension]: FOREIGN_BAZ +[protobuf_unittest.repeated_import_enum_extension]: IMPORT_BAR +[protobuf_unittest.repeated_import_enum_extension]: IMPORT_BAZ +[protobuf_unittest.repeated_string_piece_extension]: "224" +[protobuf_unittest.repeated_string_piece_extension]: "324" +[protobuf_unittest.repeated_cord_extension]: "225" +[protobuf_unittest.repeated_cord_extension]: "325" +[protobuf_unittest.default_int32_extension]: 401 +[protobuf_unittest.default_int64_extension]: 402 +[protobuf_unittest.default_uint32_extension]: 403 +[protobuf_unittest.default_uint64_extension]: 404 +[protobuf_unittest.default_sint32_extension]: 405 +[protobuf_unittest.default_sint64_extension]: 406 +[protobuf_unittest.default_fixed32_extension]: 407 +[protobuf_unittest.default_fixed64_extension]: 408 +[protobuf_unittest.default_sfixed32_extension]: 409 +[protobuf_unittest.default_sfixed64_extension]: 410 +[protobuf_unittest.default_float_extension]: 411 +[protobuf_unittest.default_double_extension]: 412 +[protobuf_unittest.default_bool_extension]: false +[protobuf_unittest.default_string_extension]: "415" +[protobuf_unittest.default_bytes_extension]: "416" +[protobuf_unittest.default_nested_enum_extension]: FOO +[protobuf_unittest.default_foreign_enum_extension]: FOREIGN_FOO +[protobuf_unittest.default_import_enum_extension]: IMPORT_FOO +[protobuf_unittest.default_string_piece_extension]: "424" +[protobuf_unittest.default_cord_extension]: "425" diff --git a/src/google/protobuf/testing/file.cc b/src/google/protobuf/testing/file.cc new file mode 100644 index 00000000..473d6919 --- /dev/null +++ b/src/google/protobuf/testing/file.cc @@ -0,0 +1,157 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/file/base/file.cc + +#include +#include +#include +#include +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN // yeah, right +#include // Find*File(). :( +#include +#include +#else +#include +#include +#endif +#include + +namespace google { +namespace protobuf { + +#ifdef _WIN32 +#define mkdir(name, mode) mkdir(name) +// Windows doesn't have symbolic links. +#define lstat stat +#ifndef F_OK +#define F_OK 00 // not defined by MSVC for whatever reason +#endif +#endif + +bool File::Exists(const string& name) { + return access(name.c_str(), F_OK) == 0; +} + +bool File::ReadFileToString(const string& name, string* output) { + char buffer[1024]; + FILE* file = fopen(name.c_str(), "rb"); + if (file == NULL) return false; + + while (true) { + size_t n = fread(buffer, 1, sizeof(buffer), file); + if (n <= 0) break; + output->append(buffer, n); + } + + int error = ferror(file); + if (fclose(file) != 0) return false; + return error == 0; +} + +void File::ReadFileToStringOrDie(const string& name, string* output) { + GOOGLE_CHECK(ReadFileToString(name, output)) << "Could not read: " << name; +} + +void File::WriteStringToFileOrDie(const string& contents, const string& name) { + FILE* file = fopen(name.c_str(), "wb"); + GOOGLE_CHECK(file != NULL); + GOOGLE_CHECK_EQ(fwrite(contents.data(), 1, contents.size(), file), + contents.size()); + GOOGLE_CHECK(fclose(file) == 0); +} + +bool File::CreateDir(const string& name, int mode) { + return mkdir(name.c_str(), mode) == 0; +} + +bool File::RecursivelyCreateDir(const string& path, int mode) { + if (CreateDir(path, mode)) return true; + + // Try creating the parent. + string::size_type slashpos = path.find_first_of('/'); + if (slashpos == string::npos) { + // No parent given. + return false; + } + + return RecursivelyCreateDir(path.substr(0, slashpos), mode) && + CreateDir(path, mode); +} + +void File::DeleteRecursively(const string& name, + void* dummy1, void* dummy2) { + // We don't care too much about error checking here since this is only used + // in tests to delete temporary directories that are under /tmp anyway. + +#ifdef _MSC_VER + // This interface is so weird. + WIN32_FIND_DATA find_data; + HANDLE find_handle = FindFirstFile((name + "/*").c_str(), &find_data); + if (find_handle == INVALID_HANDLE_VALUE) { + // Just delete it, whatever it is. + DeleteFile(name.c_str()); + RemoveDirectory(name.c_str()); + return; + } + + do { + string entry_name = find_data.cFileName; + if (entry_name != "." && entry_name != "..") { + string path = name + "/" + entry_name; + if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + DeleteRecursively(path, NULL, NULL); + RemoveDirectory(path.c_str()); + } else { + DeleteFile(path.c_str()); + } + } + } while(FindNextFile(find_handle, &find_data)); + FindClose(find_handle); + + RemoveDirectory(name.c_str()); +#else + // Use opendir()! Yay! + // lstat = Don't follow symbolic links. + struct stat stats; + if (lstat(name.c_str(), &stats) != 0) return; + + if (S_ISDIR(stats.st_mode)) { + DIR* dir = opendir(name.c_str()); + if (dir != NULL) { + while (true) { + struct dirent* entry = readdir(dir); + if (entry == NULL) break; + string entry_name = entry->d_name; + if (entry_name != "." && entry_name != "..") { + DeleteRecursively(name + "/" + entry_name, NULL, NULL); + } + } + } + + closedir(dir); + rmdir(name.c_str()); + + } else if (S_ISREG(stats.st_mode)) { + remove(name.c_str()); + } +#endif +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/testing/file.h b/src/google/protobuf/testing/file.h new file mode 100644 index 00000000..93335f1a --- /dev/null +++ b/src/google/protobuf/testing/file.h @@ -0,0 +1,69 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/file/base/file.h + +#ifndef GOOGLE_PROTOBUF_TESTING_FILE_H__ +#define GOOGLE_PROTOBUF_TESTING_FILE_H__ + +#include + +namespace google { +namespace protobuf { + +const int DEFAULT_FILE_MODE = 0777; + +// Protocol buffer code only uses a couple static methods of File, and only +// in tests. +class File { + public: + // Check if the file exists. + static bool Exists(const string& name); + + // Read an entire file to a string. Return true if successful, false + // otherwise. + static bool ReadFileToString(const string& name, string* output); + + // Same as above, but crash on failure. + static void ReadFileToStringOrDie(const string& name, string* output); + + // Create a file and write a string to it. + static void WriteStringToFileOrDie(const string& contents, + const string& name); + + // Create a directory. + static bool CreateDir(const string& name, int mode); + + // Create a directory and all parent directories if necessary. + static bool RecursivelyCreateDir(const string& path, int mode); + + // If "name" is a file, we delete it. If it is a directory, we + // call DeleteRecursively() for each file or directory (other than + // dot and double-dot) within it, and then delete the directory itself. + // The "dummy" parameters have a meaning in the original version of this + // method but they are not used anywhere in protocol buffers. + static void DeleteRecursively(const string& name, + void* dummy1, void* dummy2); + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(File); +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_TESTING_FILE_H__ diff --git a/src/google/protobuf/testing/googletest.cc b/src/google/protobuf/testing/googletest.cc new file mode 100644 index 00000000..65c1154f --- /dev/null +++ b/src/google/protobuf/testing/googletest.cc @@ -0,0 +1,223 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/testing/base/public/googletest.cc + +#include +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#include +#else +#include +#endif +#include +#include + +namespace google { +namespace protobuf { + +#ifdef _WIN32 +#define mkdir(name, mode) mkdir(name) +#endif + +#ifndef O_BINARY +#ifdef _O_BINARY +#define O_BINARY _O_BINARY +#else +#define O_BINARY 0 // If this isn't defined, the platform doesn't need it. +#endif +#endif + +string TestSourceDir() { +#ifdef _MSC_VER + // Look for the "src" directory. + string prefix = "."; + + while (!File::Exists(prefix + "/src/google/protobuf")) { + if (!File::Exists(prefix)) { + GOOGLE_LOG(FATAL) + << "Could not find protobuf source code. Please run tests from " + "somewhere within the protobuf source package."; + } + prefix += "/.."; + } + return prefix + "/src"; +#else + // automake sets the "srcdir" environment variable. + char* result = getenv("srcdir"); + if (result == NULL) { + // Otherwise, the test must be run from the source directory. + return "."; + } else { + return result; + } +#endif +} + +namespace { + +string GetTemporaryDirectoryName() { + // tmpnam() is generally not considered safe but we're only using it for + // testing. We cannot use tmpfile() or mkstemp() since we're creating a + // directory. + string result = tmpnam(NULL); +#ifdef _WIN32 + // On Win32, tmpnam() returns a file prefixed with '\', but which is supposed + // to be used in the current working directory. WTF? + if (HasPrefixString(result, "\\")) { + result.erase(0, 1); + } +#endif // _WIN32 + return result; +} + +// Creates a temporary directory on demand and deletes it when the process +// quits. +class TempDirDeleter { + public: + TempDirDeleter() {} + ~TempDirDeleter() { + if (!name_.empty()) { + File::DeleteRecursively(name_, NULL, NULL); + } + } + + string GetTempDir() { + if (name_.empty()) { + name_ = GetTemporaryDirectoryName(); + GOOGLE_CHECK(mkdir(name_.c_str(), 0777) == 0) << strerror(errno); + + // Stick a file in the directory that tells people what this is, in case + // we abort and don't get a chance to delete it. + File::WriteStringToFileOrDie("", name_ + "/TEMP_DIR_FOR_PROTOBUF_TESTS"); + } + return name_; + } + + private: + string name_; +}; + +TempDirDeleter temp_dir_deleter_; + +} // namespace + +string TestTempDir() { + return temp_dir_deleter_.GetTempDir(); +} + +// TODO(kenton): Share duplicated code below. Too busy/lazy for now. + +static string stdout_capture_filename_; +static string stderr_capture_filename_; +static int original_stdout_ = -1; +static int original_stderr_ = -1; + +void CaptureTestStdout() { + GOOGLE_CHECK_EQ(original_stdout_, -1) << "Already capturing."; + + stdout_capture_filename_ = TestTempDir() + "/captured_stdout"; + + int fd = open(stdout_capture_filename_.c_str(), + O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777); + GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno); + + original_stdout_ = dup(1); + close(1); + dup2(fd, 1); + close(fd); +} + +void CaptureTestStderr() { + GOOGLE_CHECK_EQ(original_stderr_, -1) << "Already capturing."; + + stderr_capture_filename_ = TestTempDir() + "/captured_stderr"; + + int fd = open(stderr_capture_filename_.c_str(), + O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0777); + GOOGLE_CHECK(fd >= 0) << "open: " << strerror(errno); + + original_stderr_ = dup(2); + close(2); + dup2(fd, 2); + close(fd); +} + +string GetCapturedTestStdout() { + GOOGLE_CHECK_NE(original_stdout_, -1) << "Not capturing."; + + close(1); + dup2(original_stdout_, 1); + original_stdout_ = -1; + + string result; + File::ReadFileToStringOrDie(stdout_capture_filename_, &result); + + remove(stdout_capture_filename_.c_str()); + + return result; +} + +string GetCapturedTestStderr() { + GOOGLE_CHECK_NE(original_stderr_, -1) << "Not capturing."; + + close(2); + dup2(original_stderr_, 2); + original_stderr_ = -1; + + string result; + File::ReadFileToStringOrDie(stderr_capture_filename_, &result); + + remove(stderr_capture_filename_.c_str()); + + return result; +} + +ScopedMemoryLog* ScopedMemoryLog::active_log_ = NULL; + +ScopedMemoryLog::ScopedMemoryLog() { + GOOGLE_CHECK(active_log_ == NULL); + active_log_ = this; + old_handler_ = SetLogHandler(&HandleLog); +} + +ScopedMemoryLog::~ScopedMemoryLog() { + SetLogHandler(old_handler_); + active_log_ = NULL; +} + +const vector& ScopedMemoryLog::GetMessages(LogLevel dummy) const { + GOOGLE_CHECK_EQ(dummy, ERROR); + return messages_; +} + +void ScopedMemoryLog::HandleLog(LogLevel level, const char* filename, + int line, const string& message) { + GOOGLE_CHECK(active_log_ != NULL); + if (level == ERROR) { + active_log_->messages_.push_back(message); + } +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/testing/googletest.h b/src/google/protobuf/testing/googletest.h new file mode 100644 index 00000000..bb4aafab --- /dev/null +++ b/src/google/protobuf/testing/googletest.h @@ -0,0 +1,83 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// emulates google3/testing/base/public/googletest.h + +#ifndef GOOGLE_PROTOBUF_GOOGLETEST_H__ +#define GOOGLE_PROTOBUF_GOOGLETEST_H__ + +#include +#include + +namespace google { +namespace protobuf { + +// When running unittests, get the directory containing the source code. +string TestSourceDir(); + +// When running unittests, get a directory where temporary files may be +// placed. +string TestTempDir(); + +// Capture all text written to stdout or stderr. +void CaptureTestStdout(); +void CaptureTestStderr(); + +// Stop capturing stdout or stderr and return the text captured. +string GetCapturedTestStdout(); +string GetCapturedTestStderr(); + +// For use with ScopedMemoryLog::GetMessages(). Inside Google the LogLevel +// constants don't have the LOGLEVEL_ prefix, so the code that used +// ScopedMemoryLog refers to LOGLEVEL_ERROR as just ERROR. +static const LogLevel ERROR = LOGLEVEL_ERROR; + +// Receives copies of all LOG(ERROR) messages while in scope. Sample usage: +// { +// ScopedMemoryLog log; // constructor registers object as a log sink +// SomeRoutineThatMayLogMessages(); +// const vector& warnings = log.GetMessages(ERROR); +// } // destructor unregisters object as a log sink +// This is a dummy implementation which covers only what is used by protocol +// buffer unit tests. +class ScopedMemoryLog { + public: + ScopedMemoryLog(); + virtual ~ScopedMemoryLog(); + + // Fetches all messages logged. The internal version of this class + // would only fetch messages at the given security level, but the protobuf + // open source version ignores the argument since we always pass ERROR + // anyway. + const vector& GetMessages(LogLevel dummy) const; + + private: + vector messages_; + LogHandler* old_handler_; + + static void HandleLog(LogLevel level, const char* filename, int line, + const string& message); + + static ScopedMemoryLog* active_log_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ScopedMemoryLog); +}; + +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_GOOGLETEST_H__ diff --git a/src/google/protobuf/text_format.cc b/src/google/protobuf/text_format.cc new file mode 100644 index 00000000..a2318678 --- /dev/null +++ b/src/google/protobuf/text_format.cc @@ -0,0 +1,979 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +string Message::DebugString() const { + string debug_string; + io::StringOutputStream output_stream(&debug_string); + + TextFormat::Print(*this, &output_stream); + + return debug_string; +} + +string Message::ShortDebugString() const { + // TODO(kenton): Make TextFormat support this natively instead of using + // DebugString() and munging the result. + string result = DebugString(); + + // Replace each contiguous range of whitespace (including newlines) with a + // single space. + for (int i = 0; i < result.size(); i++) { + int pos = i; + while (isspace(result[pos])) ++pos; + if (pos > i) result.replace(i, pos - i, " "); + } + + return result; +} + +void Message::PrintDebugString() const { + printf("%s", DebugString().c_str()); +} + +// =========================================================================== +// Internal class for parsing an ASCII representation of a Protocol Message. +// This class makes use of the Protocol Message compiler's tokenizer found +// in //google/protobuf/io/tokenizer.h. Note that class's Parse +// method is *not* thread-safe and should only be used in a single thread at +// a time. + +// Makes code slightly more readable. The meaning of "DO(foo)" is +// "Execute foo and fail if it fails.", where failure is indicated by +// returning false. Borrowed from parser.cc (Thanks Kenton!). +#define DO(STATEMENT) if (STATEMENT) {} else return false + +class TextFormat::ParserImpl { + public: + ParserImpl(io::ZeroCopyInputStream* input_stream, + io::ErrorCollector* error_collector) + : error_collector_(error_collector), + tokenizer_error_collector_(this), + tokenizer_(input_stream, &tokenizer_error_collector_), + root_message_type_(NULL) { + // For backwards-compatibility with proto1, we need to allow the 'f' suffix + // for floats. + tokenizer_.set_allow_f_after_float(true); + + // '#' starts a comment. + tokenizer_.set_comment_style(io::Tokenizer::SH_COMMENT_STYLE); + + // Consume the starting token. + tokenizer_.Next(); + } + ~ParserImpl() { } + + // Parses the ASCII representation specified in input and saves the + // information into the output pointer (a Message). Returns + // false if an error occurs (an error will also be logged to + // GOOGLE_LOG(ERROR)). + bool Parse(Message* output) { + root_message_type_ = output->GetDescriptor(); + + // Consume fields until we cannot do so anymore. + while(true) { + if (LookingAtType(io::Tokenizer::TYPE_END)) { + return true; + } + + DO(ConsumeField(output)); + } + } + + void ReportError(int line, int col, const string& message) { + if (error_collector_ == NULL) { + if (line >= 0) { + GOOGLE_LOG(ERROR) << "Error parsing text-format " + << root_message_type_->full_name() + << ": " << (line + 1) << ":" + << (col + 1) << ": " << message; + } else { + GOOGLE_LOG(ERROR) << "Error parsing text-format " + << root_message_type_->full_name() + << ": " << message; + } + } else { + error_collector_->AddError(line, col, message); + } + } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserImpl); + + // Reports an error with the given message with information indicating + // the position (as derived from the current token). + void ReportError(const string& message) { + ReportError(tokenizer_.current().line, tokenizer_.current().column, + message); + } + + // Consumes the specified message with the given starting delimeter. + // This method checks to see that the end delimeter at the conclusion of + // the consumption matches the starting delimeter passed in here. + bool ConsumeMessage(Message* message, const string delimeter) { + while (!LookingAt(">") && !LookingAt("}")) { + DO(ConsumeField(message)); + } + + // Confirm that we have a valid ending delimeter. + DO(Consume(delimeter)); + + return true; + } + + // Consumes the current field (as returned by the tokenizer) on the + // passed in message. + bool ConsumeField(Message* message) { + const Reflection* reflection = message->GetReflection(); + const Descriptor* descriptor = message->GetDescriptor(); + + string field_name; + + const FieldDescriptor* field = NULL; + + if (TryConsume("[")) { + // Extension. + DO(ConsumeIdentifier(&field_name)); + while (TryConsume(".")) { + string part; + DO(ConsumeIdentifier(&part)); + field_name += "."; + field_name += part; + } + DO(Consume("]")); + + field = reflection->FindKnownExtensionByName(field_name); + + if (field == NULL) { + ReportError("Extension \"" + field_name + "\" is not defined or " + "is not an extension of \"" + + descriptor->full_name() + "\"."); + return false; + } + } else { + DO(ConsumeIdentifier(&field_name)); + + field = descriptor->FindFieldByName(field_name); + // Group names are expected to be capitalized as they appear in the + // .proto file, which actually matches their type names, not their field + // names. + if (field == NULL) { + string lower_field_name = field_name; + LowerString(&lower_field_name); + field = descriptor->FindFieldByName(lower_field_name); + // If the case-insensitive match worked but the field is NOT a group, + if (field != NULL && field->type() != FieldDescriptor::TYPE_GROUP) { + field = NULL; + } + } + // Again, special-case group names as described above. + if (field != NULL && field->type() == FieldDescriptor::TYPE_GROUP + && field->message_type()->name() != field_name) { + field = NULL; + } + + if (field == NULL) { + ReportError("Message type \"" + descriptor->full_name() + + "\" has no field named \"" + field_name + "\"."); + return false; + } + } + + // Perform special handling for embedded message types. + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + string delimeter; + + // ':' is optional here. + TryConsume(":"); + + if (TryConsume("<")) { + delimeter = ">"; + } else { + DO(Consume("{")); + delimeter = "}"; + } + + if (field->is_repeated()) { + DO(ConsumeMessage(reflection->AddMessage(message, field), delimeter)); + } else { + DO(ConsumeMessage(reflection->MutableMessage(message, field), + delimeter)); + } + } else { + DO(Consume(":")); + DO(ConsumeFieldValue(message, reflection, field)); + } + + return true; + } + + bool ConsumeFieldValue(Message* message, + const Reflection* reflection, + const FieldDescriptor* field) { + +// Define an easy to use macro for setting fields. This macro checks +// to see if the field is repeated (in which case we need to use the Add +// methods or not (in which case we need to use the Set methods). +#define SET_FIELD(CPPTYPE, VALUE) \ + if (field->is_repeated()) { \ + reflection->Add##CPPTYPE(message, field, VALUE); \ + } else { \ + reflection->Set##CPPTYPE(message, field, VALUE); \ + } \ + + switch(field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: { + int64 value; + DO(ConsumeSignedInteger(&value, kint32max)); + SET_FIELD(Int32, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_UINT32: { + uint64 value; + DO(ConsumeUnsignedInteger(&value, kuint32max)); + SET_FIELD(UInt32, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_INT64: { + int64 value; + DO(ConsumeSignedInteger(&value, kint64max)); + SET_FIELD(Int64, value); + break; + } + + case FieldDescriptor::CPPTYPE_UINT64: { + uint64 value; + DO(ConsumeUnsignedInteger(&value, kuint64max)); + SET_FIELD(UInt64, value); + break; + } + + case FieldDescriptor::CPPTYPE_FLOAT: { + double value; + DO(ConsumeDouble(&value)); + SET_FIELD(Float, static_cast(value)); + break; + } + + case FieldDescriptor::CPPTYPE_DOUBLE: { + double value; + DO(ConsumeDouble(&value)); + SET_FIELD(Double, value); + break; + } + + case FieldDescriptor::CPPTYPE_STRING: { + string value; + DO(ConsumeString(&value)); + SET_FIELD(String, value); + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: { + string value; + DO(ConsumeIdentifier(&value)); + + if (value == "true") { + SET_FIELD(Bool, true); + } else if (value == "false") { + SET_FIELD(Bool, false); + } else { + ReportError("Invalid value for boolean field \"" + field->name() + + "\". Value: \"" + value + "\"."); + return false; + } + break; + } + + case FieldDescriptor::CPPTYPE_ENUM: { + string value; + DO(ConsumeIdentifier(&value)); + + // Find the enumeration value. + const EnumDescriptor* enum_type = field->enum_type(); + const EnumValueDescriptor* enum_value + = enum_type->FindValueByName(value); + + if (enum_value == NULL) { + ReportError("Unknown enumeration value of \"" + value + "\" for " + "field \"" + field->name() + "\"."); + return false; + } + + SET_FIELD(Enum, enum_value); + break; + } + + case FieldDescriptor::CPPTYPE_MESSAGE: { + // We should never get here. Put here instead of a default + // so that if new types are added, we get a nice compiler warning. + GOOGLE_LOG(FATAL) << "Reached an unintended state: CPPTYPE_MESSAGE"; + break; + } + } +#undef SET_FIELD + return true; + } + + // Returns true if the current token's text is equal to that specified. + bool LookingAt(const string& text) { + return tokenizer_.current().text == text; + } + + // Returns true if the current token's type is equal to that specified. + bool LookingAtType(io::Tokenizer::TokenType token_type) { + return tokenizer_.current().type == token_type; + } + + // Consumes an identifier and saves its value in the identifier parameter. + // Returns false if the token is not of type IDENTFIER. + bool ConsumeIdentifier(string* identifier) { + if (!LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + ReportError("Expected identifier."); + return false; + } + + *identifier = tokenizer_.current().text; + + tokenizer_.Next(); + return true; + } + + // Consumes a string and saves its value in the text parameter. + // Returns false if the token is not of type STRING. + bool ConsumeString(string* text) { + if (!LookingAtType(io::Tokenizer::TYPE_STRING)) { + ReportError("Expected string."); + return false; + } + + io::Tokenizer::ParseString(tokenizer_.current().text, text); + + tokenizer_.Next(); + return true; + } + + // Consumes a uint64 and saves its value in the value parameter. + // Returns false if the token is not of type INTEGER. + bool ConsumeUnsignedInteger(uint64* value, uint64 max_value) { + if (!LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + ReportError("Expected integer."); + return false; + } + + if (!io::Tokenizer::ParseInteger(tokenizer_.current().text, + max_value, value)) { + ReportError("Integer out of range."); + return false; + } + + tokenizer_.Next(); + return true; + } + + // Consumes an int64 and saves its value in the value parameter. + // Note that since the tokenizer does not support negative numbers, + // we actually may consume an additional token (for the minus sign) in this + // method. Returns false if the token is not an integer + // (signed or otherwise). + bool ConsumeSignedInteger(int64* value, uint64 max_value) { + bool negative = false; + + if (TryConsume("-")) { + negative = true; + // Two's complement always allows one more negative integer than + // positive. + ++max_value; + } + + uint64 unsigned_value; + + DO(ConsumeUnsignedInteger(&unsigned_value, max_value)); + + *value = static_cast(unsigned_value); + + if (negative) { + *value = -*value; + } + + return true; + } + + // Consumes a double and saves its value in the value parameter. + // Note that since the tokenizer does not support negative numbers, + // we actually may consume an additional token (for the minus sign) in this + // method. Returns false if the token is not a double + // (signed or otherwise). + bool ConsumeDouble(double* value) { + bool negative = false; + + if (TryConsume("-")) { + negative = true; + } + + // A double can actually be an integer, according to the tokenizer. + // Therefore, we must check both cases here. + if (LookingAtType(io::Tokenizer::TYPE_INTEGER)) { + // We have found an integer value for the double. + uint64 integer_value; + DO(ConsumeUnsignedInteger(&integer_value, kuint64max)); + + *value = static_cast(integer_value); + } else if (LookingAtType(io::Tokenizer::TYPE_FLOAT)) { + // We have found a float value for the double. + *value = io::Tokenizer::ParseFloat(tokenizer_.current().text); + + // Mark the current token as consumed. + tokenizer_.Next(); + } else if (LookingAtType(io::Tokenizer::TYPE_IDENTIFIER)) { + string text = tokenizer_.current().text; + LowerString(&text); + if (text == "inf" || text == "infinity") { + *value = std::numeric_limits::infinity(); + tokenizer_.Next(); + } else if (text == "nan") { + *value = std::numeric_limits::quiet_NaN(); + tokenizer_.Next(); + } else { + ReportError("Expected double."); + return false; + } + } else { + ReportError("Expected double."); + return false; + } + + if (negative) { + *value = -*value; + } + + return true; + } + + // Consumes a token and confirms that it matches that specified in the + // value parameter. Returns false if the token found does not match that + // which was specified. + bool Consume(const string& value) { + const string& current_value = tokenizer_.current().text; + + if (current_value != value) { + ReportError("Expected \"" + value + "\", found \"" + current_value + + "\"."); + return false; + } + + tokenizer_.Next(); + + return true; + } + + // Attempts to consume the supplied value. Returns false if a the + // token found does not match the value specified. + bool TryConsume(const string& value) { + if (tokenizer_.current().text == value) { + tokenizer_.Next(); + return true; + } else { + return false; + } + } + + // An internal instance of the Tokenizer's error collector, used to + // collect any base-level parse errors and feed them to the ParserImpl. + class ParserErrorCollector : public io::ErrorCollector { + public: + explicit ParserErrorCollector(TextFormat::ParserImpl* parser) : + parser_(parser) { } + + virtual ~ParserErrorCollector() { }; + + virtual void AddError(int line, int column, const string& message) { + parser_->ReportError(line, column, message); + } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ParserErrorCollector); + TextFormat::ParserImpl* parser_; + }; + + io::ErrorCollector* error_collector_; + ParserErrorCollector tokenizer_error_collector_; + io::Tokenizer tokenizer_; + const Descriptor* root_message_type_; +}; + +#undef DO + +// =========================================================================== +// Internal class for writing text to the io::ZeroCopyOutputStream. Adapted +// from the Printer found in //google/protobuf/io/printer.h +class TextFormat::TextGenerator { + public: + explicit TextGenerator(io::ZeroCopyOutputStream* output) + : output_(output), + buffer_(NULL), + buffer_size_(0), + at_start_of_line_(true), + failed_(false), + indent_("") { + } + + ~TextGenerator() { + // Only BackUp() if we're sure we've successfully called Next() at least + // once. + if (buffer_size_ > 0) { + output_->BackUp(buffer_size_); + } + } + + // Indent text by two spaces. After calling Indent(), two spaces will be + // inserted at the beginning of each line of text. Indent() may be called + // multiple times to produce deeper indents. + void Indent() { + indent_ += " "; + } + + // Reduces the current indent level by two spaces, or crashes if the indent + // level is zero. + void Outdent() { + if (indent_.empty()) { + GOOGLE_LOG(DFATAL) << " Outdent() without matching Indent()."; + return; + } + + indent_.resize(indent_.size() - 2); + } + + // Print text to the output stream. + void Print(const string& str) { + Print(str.c_str()); + } + + // Print text to the output stream. + void Print(const char* text) { + int size = strlen(text); + int pos = 0; // The number of bytes we've written so far. + + for (int i = 0; i < size; i++) { + if (text[i] == '\n') { + // Saw newline. If there is more text, we may need to insert an indent + // here. So, write what we have so far, including the '\n'. + Write(text + pos, i - pos + 1); + pos = i + 1; + + // Setting this true will cause the next Write() to insert an indent + // first. + at_start_of_line_ = true; + } + } + + // Write the rest. + Write(text + pos, size - pos); + } + + // True if any write to the underlying stream failed. (We don't just + // crash in this case because this is an I/O failure, not a programming + // error.) + bool failed() const { return failed_; } + + private: + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextGenerator); + + void Write(const char* data, int size) { + if (failed_) return; + if (size == 0) return; + + if (at_start_of_line_) { + // Insert an indent. + at_start_of_line_ = false; + Write(indent_.data(), indent_.size()); + if (failed_) return; + } + + while (size > buffer_size_) { + // Data exceeds space in the buffer. Copy what we can and request a + // new buffer. + memcpy(buffer_, data, buffer_size_); + data += buffer_size_; + size -= buffer_size_; + void* void_buffer; + failed_ = !output_->Next(&void_buffer, &buffer_size_); + if (failed_) return; + buffer_ = reinterpret_cast(void_buffer); + } + + // Buffer is big enough to receive the data; copy it. + memcpy(buffer_, data, size); + buffer_ += size; + buffer_size_ -= size; + } + + io::ZeroCopyOutputStream* const output_; + char* buffer_; + int buffer_size_; + bool at_start_of_line_; + bool failed_; + + string indent_; +}; + +// =========================================================================== + +TextFormat::Parser::Parser() + : error_collector_(NULL), + allow_partial_(false) {} + +TextFormat::Parser::~Parser() {} + +bool TextFormat::Parser::Parse(io::ZeroCopyInputStream* input, + Message* output) { + output->Clear(); + return Merge(input, output); +} + +bool TextFormat::Parser::ParseFromString(const string& input, + Message* output) { + io::ArrayInputStream input_stream(input.data(), input.size()); + return Parse(&input_stream, output); +} + +bool TextFormat::Parser::Merge(io::ZeroCopyInputStream* input, + Message* output) { + ParserImpl parser(input, error_collector_); + if (!parser.Parse(output)) return false; + if (!allow_partial_ && !output->IsInitialized()) { + vector missing_fields; + output->FindInitializationErrors(&missing_fields); + parser.ReportError(-1, 0, "Message missing required fields: " + + JoinStrings(missing_fields, ", ")); + return false; + } + return true; +} + +bool TextFormat::Parser::MergeFromString(const string& input, + Message* output) { + io::ArrayInputStream input_stream(input.data(), input.size()); + return Merge(&input_stream, output); +} + + +/* static */ bool TextFormat::Parse(io::ZeroCopyInputStream* input, + Message* output) { + return Parser().Parse(input, output); +} + +/* static */ bool TextFormat::Merge(io::ZeroCopyInputStream* input, + Message* output) { + return Parser().Merge(input, output); +} + +/* static */ bool TextFormat::ParseFromString(const string& input, + Message* output) { + return Parser().ParseFromString(input, output); +} + +/* static */ bool TextFormat::MergeFromString(const string& input, + Message* output) { + return Parser().MergeFromString(input, output); +} + +/* static */ bool TextFormat::PrintToString(const Message& message, + string* output) { + GOOGLE_DCHECK(output) << "output specified is NULL"; + + output->clear(); + io::StringOutputStream output_stream(output); + + bool result = Print(message, &output_stream); + + return result; +} + +/* static */ bool TextFormat::PrintUnknownFieldsToString( + const UnknownFieldSet& unknown_fields, + string* output) { + GOOGLE_DCHECK(output) << "output specified is NULL"; + + output->clear(); + io::StringOutputStream output_stream(output); + return PrintUnknownFields(unknown_fields, &output_stream); +} + +/* static */ bool TextFormat::Print(const Message& message, + io::ZeroCopyOutputStream* output) { + TextGenerator generator(output); + + Print(message, generator); + + // Output false if the generator failed internally. + return !generator.failed(); +} + +/* static */ bool TextFormat::PrintUnknownFields( + const UnknownFieldSet& unknown_fields, + io::ZeroCopyOutputStream* output) { + TextGenerator generator(output); + + PrintUnknownFields(unknown_fields, generator); + + // Output false if the generator failed internally. + return !generator.failed(); +} + +/* static */ void TextFormat::Print(const Message& message, + TextGenerator& generator) { + const Reflection* reflection = message.GetReflection(); + vector fields; + reflection->ListFields(message, &fields); + for (int i = 0; i < fields.size(); i++) { + PrintField(message, reflection, fields[i], generator); + } + PrintUnknownFields(reflection->GetUnknownFields(message), generator); +} + +/* static */ void TextFormat::PrintFieldValueToString( + const Message& message, + const FieldDescriptor* field, + int index, + string* output) { + + GOOGLE_DCHECK(output) << "output specified is NULL"; + + output->clear(); + io::StringOutputStream output_stream(output); + TextGenerator generator(&output_stream); + + PrintFieldValue(message, message.GetReflection(), field, index, generator); +} + +/* static */ void TextFormat::PrintField(const Message& message, + const Reflection* reflection, + const FieldDescriptor* field, + TextGenerator& generator) { + int count = 0; + + if (field->is_repeated()) { + count = reflection->FieldSize(message, field); + } else if (reflection->HasField(message, field)) { + count = 1; + } + + for (int j = 0; j < count; ++j) { + if (field->is_extension()) { + generator.Print("["); + // We special-case MessageSet elements for compatibility with proto1. + if (field->containing_type()->options().message_set_wire_format() + && field->type() == FieldDescriptor::TYPE_MESSAGE + && field->is_optional() + && field->extension_scope() == field->message_type()) { + generator.Print(field->message_type()->full_name()); + } else { + generator.Print(field->full_name()); + } + generator.Print("]"); + } else { + if (field->type() == FieldDescriptor::TYPE_GROUP) { + // Groups must be serialized with their original capitalization. + generator.Print(field->message_type()->name()); + } else { + generator.Print(field->name()); + } + } + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + generator.Print(" {\n"); + generator.Indent(); + } else { + generator.Print(": "); + } + + // Write the field value. + int field_index = j; + if (!field->is_repeated()) { + field_index = -1; + } + + PrintFieldValue(message, reflection, field, field_index, generator); + + if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + generator.Outdent(); + generator.Print("}"); + } + + generator.Print("\n"); + } +} + +/* static */ void TextFormat::PrintFieldValue( + const Message& message, + const Reflection* reflection, + const FieldDescriptor* field, + int index, + TextGenerator& generator) { + GOOGLE_DCHECK(field->is_repeated() || (index == -1)) + << "Index must be -1 for non-repeated fields"; + + switch (field->cpp_type()) { +#define OUTPUT_FIELD(CPPTYPE, METHOD, TO_STRING) \ + case FieldDescriptor::CPPTYPE_##CPPTYPE: \ + generator.Print(TO_STRING(field->is_repeated() ? \ + reflection->GetRepeated##METHOD(message, field, index) : \ + reflection->Get##METHOD(message, field))); \ + break; \ + + OUTPUT_FIELD( INT32, Int32, SimpleItoa); + OUTPUT_FIELD( INT64, Int64, SimpleItoa); + OUTPUT_FIELD(UINT32, UInt32, SimpleItoa); + OUTPUT_FIELD(UINT64, UInt64, SimpleItoa); + OUTPUT_FIELD( FLOAT, Float, SimpleFtoa); + OUTPUT_FIELD(DOUBLE, Double, SimpleDtoa); +#undef OUTPUT_FIELD + + case FieldDescriptor::CPPTYPE_STRING: { + string scratch; + const string& value = field->is_repeated() ? + reflection->GetRepeatedStringReference( + message, field, index, &scratch) : + reflection->GetStringReference(message, field, &scratch); + + generator.Print("\""); + generator.Print(CEscape(value)); + generator.Print("\""); + + break; + } + + case FieldDescriptor::CPPTYPE_BOOL: + if (field->is_repeated()) { + generator.Print(reflection->GetRepeatedBool(message, field, index) + ? "true" : "false"); + } else { + generator.Print(reflection->GetBool(message, field) + ? "true" : "false"); + } + break; + + case FieldDescriptor::CPPTYPE_ENUM: + generator.Print(field->is_repeated() ? + reflection->GetRepeatedEnum(message, field, index)->name() : + reflection->GetEnum(message, field)->name()); + break; + + case FieldDescriptor::CPPTYPE_MESSAGE: + Print(field->is_repeated() ? + reflection->GetRepeatedMessage(message, field, index) : + reflection->GetMessage(message, field), + generator); + break; + } +} + +// Prints an integer as hex with a fixed number of digits dependent on the +// integer type. +template +static string PaddedHex(IntType value) { + string result; + result.reserve(sizeof(value) * 2); + for (int i = sizeof(value) * 2 - 1; i >= 0; i--) { + result.push_back(int_to_hex_digit(value >> (i*4) & 0x0F)); + } + return result; +} + +/* static */ void TextFormat::PrintUnknownFields( + const UnknownFieldSet& unknown_fields, TextGenerator& generator) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + string field_number = SimpleItoa(field.number()); + + for (int j = 0; j < field.varint_size(); j++) { + generator.Print(field_number); + generator.Print(": "); + generator.Print(SimpleItoa(field.varint(j))); + generator.Print("\n"); + } + for (int j = 0; j < field.fixed32_size(); j++) { + generator.Print(field_number); + generator.Print(": 0x"); + char buffer[kFastToBufferSize]; + generator.Print(FastHex32ToBuffer(field.fixed32(j), buffer)); + generator.Print("\n"); + } + for (int j = 0; j < field.fixed64_size(); j++) { + generator.Print(field_number); + generator.Print(": 0x"); + char buffer[kFastToBufferSize]; + generator.Print(FastHex64ToBuffer(field.fixed64(j), buffer)); + generator.Print("\n"); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + generator.Print(field_number); + const string& value = field.length_delimited(j); + UnknownFieldSet embedded_unknown_fields; + if (!value.empty() && embedded_unknown_fields.ParseFromString(value)) { + // This field is parseable as a Message. + // So it is probably an embedded message. + generator.Print(" {\n"); + generator.Indent(); + PrintUnknownFields(embedded_unknown_fields, generator); + generator.Outdent(); + generator.Print("}\n"); + } else { + // This field is not parseable as a Message. + // So it is probably just a plain string. + generator.Print(": \""); + generator.Print(CEscape(value)); + generator.Print("\"\n"); + } + } + for (int j = 0; j < field.group_size(); j++) { + generator.Print(field_number); + generator.Print(" {\n"); + generator.Indent(); + PrintUnknownFields(field.group(j), generator); + generator.Outdent(); + generator.Print("}\n"); + } + } +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/text_format.h b/src/google/protobuf/text_format.h new file mode 100644 index 00000000..cd93c14a --- /dev/null +++ b/src/google/protobuf/text_format.h @@ -0,0 +1,156 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Utilities for printing and parsing protocol messages in a human-readable, +// text-based format. + +#ifndef GOOGLE_PROTOBUF_TEXT_FORMAT_H__ +#define GOOGLE_PROTOBUF_TEXT_FORMAT_H__ + +#include +#include +#include + +namespace google { +namespace protobuf { + +namespace io { + class ErrorCollector; // tokenizer.h +} + +// This class implements protocol buffer text format. Printing and parsing +// protocol messages in text format is useful for debugging and human editing +// of messages. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT TextFormat { + public: + // Outputs a textual representation of the given message to the given + // output stream. + static bool Print(const Message& message, io::ZeroCopyOutputStream* output); + + // Print the fields in an UnknownFieldSet. They are printed by tag number + // only. Embedded messages are heuristically identified by attempting to + // parse them. + static bool PrintUnknownFields(const UnknownFieldSet& unknown_fields, + io::ZeroCopyOutputStream* output); + + // Like Print(), but outputs directly to a string. + static bool PrintToString(const Message& message, string* output); + + // Like PrintUnknownFields(), but outputs directly to a string. + static bool PrintUnknownFieldsToString(const UnknownFieldSet& unknown_fields, + string* output); + + // Outputs a textual representation of the value of the field supplied on + // the message supplied. For non-repeated fields, an index of -1 must + // be supplied. Note that this method will print the default value for a + // field if it is not set. + static void PrintFieldValueToString(const Message& message, + const FieldDescriptor* field, + int index, + string* output); + + // Parses a text-format protocol message from the given input stream to + // the given message object. This function parses the format written + // by Print(). + static bool Parse(io::ZeroCopyInputStream* input, Message* output); + // Like Parse(), but reads directly from a string. + static bool ParseFromString(const string& input, Message* output); + + // Like Parse(), but the data is merged into the given message, as if + // using Message::MergeFrom(). + static bool Merge(io::ZeroCopyInputStream* input, Message* output); + // Like Merge(), but reads directly from a string. + static bool MergeFromString(const string& input, Message* output); + + // For more control over parsing, use this class. + class LIBPROTOBUF_EXPORT Parser { + public: + Parser(); + ~Parser(); + + // Like TextFormat::Parse(). + bool Parse(io::ZeroCopyInputStream* input, Message* output); + // Like TextFormat::ParseFromString(). + bool ParseFromString(const string& input, Message* output); + // Like TextFormat::Merge(). + bool Merge(io::ZeroCopyInputStream* input, Message* output); + // Like TextFormat::MergeFromString(). + bool MergeFromString(const string& input, Message* output); + + // Set where to report parse errors. If NULL (the default), errors will + // be printed to stderr. + void RecordErrorsTo(io::ErrorCollector* error_collector) { + error_collector_ = error_collector; + } + + // Normally parsing fails if, after parsing, output->IsInitialized() + // returns false. Call AllowPartialMessage(true) to skip this check. + void AllowPartialMessage(bool allow) { + allow_partial_ = allow; + } + + private: + io::ErrorCollector* error_collector_; + bool allow_partial_; + }; + + private: + // Forward declaration of an internal class used to print the text + // output to the OutputStream (see text_format.cc for implementation). + class TextGenerator; + + // Forward declaration of an internal class used to parse text + // representations (see text_format.cc for implementation). + class ParserImpl; + + // Internal Print method, used for writing to the OutputStream via + // the TextGenerator class. + static void Print(const Message& message, + TextGenerator& generator); + + // Print a single field. + static void PrintField(const Message& message, + const Reflection* reflection, + const FieldDescriptor* field, + TextGenerator& generator); + + // Outputs a textual representation of the value of the field supplied on + // the message supplied or the default value if not set. + static void PrintFieldValue(const Message& message, + const Reflection* reflection, + const FieldDescriptor* field, + int index, + TextGenerator& generator); + + // Print the fields in an UnknownFieldSet. They are printed by tag number + // only. Embedded messages are heuristically identified by attempting to + // parse them. + static void PrintUnknownFields(const UnknownFieldSet& unknown_fields, + TextGenerator& generator); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(TextFormat); +}; + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_TEXT_FORMAT_H__ diff --git a/src/google/protobuf/text_format_unittest.cc b/src/google/protobuf/text_format_unittest.cc new file mode 100644 index 00000000..63a0c331 --- /dev/null +++ b/src/google/protobuf/text_format_unittest.cc @@ -0,0 +1,742 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: jschorr@google.com (Joseph Schorr) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace { + +inline bool IsNaN(double value) { + // NaN is never equal to anything, even itself. + return value != value; +} + +// A basic string with different escapable characters for testing. +const string kEscapeTestString = + "\"A string with ' characters \n and \r newlines and \t tabs and \001 " + "slashes \\"; + +// A representation of the above string with all the characters escaped. +const string kEscapeTestStringEscaped = + "\"\\\"A string with \\' characters \\n and \\r newlines " + "and \\t tabs and \\001 slashes \\\\\""; + +class TextFormatTest : public testing::Test { + public: + static void SetUpTestCase() { + File::ReadFileToStringOrDie( + TestSourceDir() + + "/google/protobuf/testdata/text_format_unittest_data.txt", + &static_proto_debug_string_); + } + + TextFormatTest() : proto_debug_string_(static_proto_debug_string_) {} + + protected: + // Debug string read from text_format_unittest_data.txt. + const string proto_debug_string_; + unittest::TestAllTypes proto_; + + private: + static string static_proto_debug_string_; +}; +string TextFormatTest::static_proto_debug_string_; + +class TextFormatExtensionsTest : public testing::Test { + public: + static void SetUpTestCase() { + File::ReadFileToStringOrDie( + TestSourceDir() + + "/google/protobuf/testdata/" + "text_format_unittest_extensions_data.txt", + &static_proto_debug_string_); + } + + TextFormatExtensionsTest() + : proto_debug_string_(static_proto_debug_string_) {} + + protected: + // Debug string read from text_format_unittest_data.txt. + const string proto_debug_string_; + unittest::TestAllExtensions proto_; + + private: + static string static_proto_debug_string_; +}; +string TextFormatExtensionsTest::static_proto_debug_string_; + + +TEST_F(TextFormatTest, Basic) { + TestUtil::SetAllFields(&proto_); + EXPECT_EQ(proto_debug_string_, proto_.DebugString()); +} + +TEST_F(TextFormatExtensionsTest, Extensions) { + TestUtil::SetAllExtensions(&proto_); + EXPECT_EQ(proto_debug_string_, proto_.DebugString()); +} + +TEST_F(TextFormatTest, StringEscape) { + // Set the string value to test. + proto_.set_optional_string(kEscapeTestString); + + // Get the DebugString from the proto. + string debug_string = proto_.DebugString(); + + // Hardcode a correct value to test against. + string correct_string = "optional_string: " + + kEscapeTestStringEscaped + + "\n"; + + // Compare. + EXPECT_EQ(correct_string, debug_string); +} + +TEST_F(TextFormatTest, PrintUnknownFields) { + // Test printing of unknown fields in a message. + + unittest::TestEmptyMessage message; + UnknownFieldSet* unknown_fields = message.mutable_unknown_fields(); + UnknownField* field5 = unknown_fields->AddField(5); + + field5->add_varint(1); + field5->add_fixed32(2); + field5->add_fixed64(3); + field5->add_length_delimited("4"); + field5->add_group()->AddField(10)->add_varint(5); + + UnknownField* field8 = unknown_fields->AddField(8); + field8->add_varint(1); + field8->add_varint(2); + field8->add_varint(3); + + EXPECT_EQ( + "5: 1\n" + "5: 0x00000002\n" + "5: 0x0000000000000003\n" + "5: \"4\"\n" + "5 {\n" + " 10: 5\n" + "}\n" + "8: 1\n" + "8: 2\n" + "8: 3\n", + message.DebugString()); +} + +TEST_F(TextFormatTest, PrintUnknownMessage) { + // Test heuristic printing of messages in an UnknownFieldSet. + + protobuf_unittest::TestAllTypes message; + + // Cases which should not be interpreted as sub-messages. + + // 'a' is a valid FIXED64 tag, so for the string to be parseable as a message + // it should be followed by 8 bytes. Since this string only has two + // subsequent bytes, it should be treated as a string. + message.add_repeated_string("abc"); + + // 'd' happens to be a valid ENDGROUP tag. So, + // UnknownFieldSet::MergeFromCodedStream() will successfully parse "def", but + // the ConsumedEntireMessage() check should fail. + message.add_repeated_string("def"); + + // A zero-length string should never be interpreted as a message even though + // it is technically valid as one. + message.add_repeated_string(""); + + // Case which should be interpreted as a sub-message. + + // An actual nested message with content should always be interpreted as a + // nested message. + message.add_repeated_nested_message()->set_bb(123); + + string data; + message.SerializeToString(&data); + + string text; + UnknownFieldSet unknown_fields; + EXPECT_TRUE(unknown_fields.ParseFromString(data)); + EXPECT_TRUE(TextFormat::PrintUnknownFieldsToString(unknown_fields, &text)); + EXPECT_EQ( + "44: \"abc\"\n" + "44: \"def\"\n" + "44: \"\"\n" + "48 {\n" + " 1: 123\n" + "}\n", + text); +} + +TEST_F(TextFormatTest, ParseBasic) { + io::ArrayInputStream input_stream(proto_debug_string_.data(), + proto_debug_string_.size()); + TextFormat::Parse(&input_stream, &proto_); + TestUtil::ExpectAllFieldsSet(proto_); +} + +TEST_F(TextFormatExtensionsTest, ParseExtensions) { + io::ArrayInputStream input_stream(proto_debug_string_.data(), + proto_debug_string_.size()); + TextFormat::Parse(&input_stream, &proto_); + TestUtil::ExpectAllExtensionsSet(proto_); +} + +TEST_F(TextFormatTest, ParseStringEscape) { + // Create a parse string with escpaed characters in it. + string parse_string = "optional_string: " + + kEscapeTestStringEscaped + + "\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(kEscapeTestString, proto_.optional_string()); +} + +TEST_F(TextFormatTest, ParseFloatWithSuffix) { + // Test that we can parse a floating-point value with 'f' appended to the + // end. This is needed for backwards-compatibility with proto1. + + // Have it parse a float with the 'f' suffix. + string parse_string = "optional_float: 1.0f\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(1.0, proto_.optional_float()); +} + +TEST_F(TextFormatTest, Comments) { + // Test that comments are ignored. + + string parse_string = "optional_int32: 1 # a comment\n" + "optional_int64: 2 # another comment"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_EQ(1, proto_.optional_int32()); + EXPECT_EQ(2, proto_.optional_int64()); +} + +TEST_F(TextFormatTest, OptionalColon) { + // Test that we can place a ':' after the field name of a nested message, + // even though we don't have to. + + string parse_string = "optional_nested_message: { bb: 1}\n"; + + io::ArrayInputStream input_stream(parse_string.data(), + parse_string.size()); + + TextFormat::Parse(&input_stream, &proto_); + + // Compare. + EXPECT_TRUE(proto_.has_optional_nested_message()); + EXPECT_EQ(1, proto_.optional_nested_message().bb()); +} + +// Some platforms (e.g. Windows) insist on padding the exponent to three +// digits when one or two would be just fine. +static string RemoveRedundantZeros(string text) { + text = StringReplace(text, "e+0", "e+", true); + text = StringReplace(text, "e-0", "e-", true); + return text; +} + +TEST_F(TextFormatTest, PrintExotic) { + unittest::TestAllTypes message; + + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and + // 9223372036854775808 is outside the range of int64. However, it is not + // outside the range of uint64. Confusingly, this means that everything + // works if we make the literal unsigned, even though we are negating it. + message.add_repeated_int64(-GOOGLE_ULONGLONG(9223372036854775808)); + message.add_repeated_uint64(GOOGLE_ULONGLONG(18446744073709551615)); + message.add_repeated_double(123.456); + message.add_repeated_double(1.23e21); + message.add_repeated_double(1.23e-18); + message.add_repeated_double(std::numeric_limits::infinity()); + message.add_repeated_double(-std::numeric_limits::infinity()); + message.add_repeated_double(std::numeric_limits::quiet_NaN()); + message.add_repeated_string(string("\000\001\a\b\f\n\r\t\v\\\'\"", 12)); + + // Fun story: We used to use 1.23e22 instead of 1.23e21 above, but this + // seemed to trigger an odd case on MinGW/GCC 3.4.5 where GCC's parsing of + // the value differed from strtod()'s parsing. That is to say, the + // following assertion fails on MinGW: + // assert(1.23e22 == strtod("1.23e22", NULL)); + // As a result, SimpleDtoa() would print the value as + // "1.2300000000000001e+22" to make sure strtod() produce the exact same + // result. Our goal is to test runtime parsing, not compile-time parsing, + // so this wasn't our problem. It was found that using 1.23e21 did not + // have this problem, so we switched to that instead. + + EXPECT_EQ( + "repeated_int64: -9223372036854775808\n" + "repeated_uint64: 18446744073709551615\n" + "repeated_double: 123.456\n" + "repeated_double: 1.23e+21\n" + "repeated_double: 1.23e-18\n" + "repeated_double: inf\n" + "repeated_double: -inf\n" + "repeated_double: nan\n" + "repeated_string: \"\\000\\001\\007\\010\\014\\n\\r\\t\\013\\\\\\'\\\"\"\n", + RemoveRedundantZeros(message.DebugString())); +} + +TEST_F(TextFormatTest, PrintFloatPrecision) { + unittest::TestAllTypes message; + + message.add_repeated_float(1.2); + message.add_repeated_float(1.23); + message.add_repeated_float(1.234); + message.add_repeated_float(1.2345); + message.add_repeated_float(1.23456); + message.add_repeated_float(1.2e10); + message.add_repeated_float(1.23e10); + message.add_repeated_float(1.234e10); + message.add_repeated_float(1.2345e10); + message.add_repeated_float(1.23456e10); + message.add_repeated_double(1.2); + message.add_repeated_double(1.23); + message.add_repeated_double(1.234); + message.add_repeated_double(1.2345); + message.add_repeated_double(1.23456); + message.add_repeated_double(1.234567); + message.add_repeated_double(1.2345678); + message.add_repeated_double(1.23456789); + message.add_repeated_double(1.234567898); + message.add_repeated_double(1.2345678987); + message.add_repeated_double(1.23456789876); + message.add_repeated_double(1.234567898765); + message.add_repeated_double(1.2345678987654); + message.add_repeated_double(1.23456789876543); + message.add_repeated_double(1.2e100); + message.add_repeated_double(1.23e100); + message.add_repeated_double(1.234e100); + message.add_repeated_double(1.2345e100); + message.add_repeated_double(1.23456e100); + message.add_repeated_double(1.234567e100); + message.add_repeated_double(1.2345678e100); + message.add_repeated_double(1.23456789e100); + message.add_repeated_double(1.234567898e100); + message.add_repeated_double(1.2345678987e100); + message.add_repeated_double(1.23456789876e100); + message.add_repeated_double(1.234567898765e100); + message.add_repeated_double(1.2345678987654e100); + message.add_repeated_double(1.23456789876543e100); + + EXPECT_EQ( + "repeated_float: 1.2\n" + "repeated_float: 1.23\n" + "repeated_float: 1.234\n" + "repeated_float: 1.2345\n" + "repeated_float: 1.23456\n" + "repeated_float: 1.2e+10\n" + "repeated_float: 1.23e+10\n" + "repeated_float: 1.234e+10\n" + "repeated_float: 1.2345e+10\n" + "repeated_float: 1.23456e+10\n" + "repeated_double: 1.2\n" + "repeated_double: 1.23\n" + "repeated_double: 1.234\n" + "repeated_double: 1.2345\n" + "repeated_double: 1.23456\n" + "repeated_double: 1.234567\n" + "repeated_double: 1.2345678\n" + "repeated_double: 1.23456789\n" + "repeated_double: 1.234567898\n" + "repeated_double: 1.2345678987\n" + "repeated_double: 1.23456789876\n" + "repeated_double: 1.234567898765\n" + "repeated_double: 1.2345678987654\n" + "repeated_double: 1.23456789876543\n" + "repeated_double: 1.2e+100\n" + "repeated_double: 1.23e+100\n" + "repeated_double: 1.234e+100\n" + "repeated_double: 1.2345e+100\n" + "repeated_double: 1.23456e+100\n" + "repeated_double: 1.234567e+100\n" + "repeated_double: 1.2345678e+100\n" + "repeated_double: 1.23456789e+100\n" + "repeated_double: 1.234567898e+100\n" + "repeated_double: 1.2345678987e+100\n" + "repeated_double: 1.23456789876e+100\n" + "repeated_double: 1.234567898765e+100\n" + "repeated_double: 1.2345678987654e+100\n" + "repeated_double: 1.23456789876543e+100\n", + RemoveRedundantZeros(message.DebugString())); +} + + +TEST_F(TextFormatTest, AllowPartial) { + unittest::TestRequired message; + TextFormat::Parser parser; + parser.AllowPartialMessage(true); + EXPECT_TRUE(parser.ParseFromString("a: 1", &message)); + EXPECT_EQ(1, message.a()); + EXPECT_FALSE(message.has_b()); + EXPECT_FALSE(message.has_c()); +} + +TEST_F(TextFormatTest, ParseExotic) { + unittest::TestAllTypes message; + ASSERT_TRUE(TextFormat::ParseFromString( + "repeated_int32: -1\n" + "repeated_int32: -2147483648\n" + "repeated_int64: -1\n" + "repeated_int64: -9223372036854775808\n" + "repeated_uint32: 4294967295\n" + "repeated_uint32: 2147483648\n" + "repeated_uint64: 18446744073709551615\n" + "repeated_uint64: 9223372036854775808\n" + "repeated_double: 123.0\n" + "repeated_double: 123.5\n" + "repeated_double: 0.125\n" + "repeated_double: 1.23E17\n" + "repeated_double: 1.235E+22\n" + "repeated_double: 1.235e-18\n" + "repeated_double: 123.456789\n" + "repeated_double: inf\n" + "repeated_double: Infinity\n" + "repeated_double: -inf\n" + "repeated_double: -Infinity\n" + "repeated_double: nan\n" + "repeated_double: NaN\n" + "repeated_string: \"\\000\\001\\a\\b\\f\\n\\r\\t\\v\\\\\\'\\\"\"\n", + &message)); + + ASSERT_EQ(2, message.repeated_int32_size()); + EXPECT_EQ(-1, message.repeated_int32(0)); + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and 2147483648 is + // outside the range of int32. However, it is not outside the range of + // uint32. Confusingly, this means that everything works if we make the + // literal unsigned, even though we are negating it. + EXPECT_EQ(-2147483648u, message.repeated_int32(1)); + + ASSERT_EQ(2, message.repeated_int64_size()); + EXPECT_EQ(-1, message.repeated_int64(0)); + // Note: In C, a negative integer literal is actually the unary negation + // operator being applied to a positive integer literal, and + // 9223372036854775808 is outside the range of int64. However, it is not + // outside the range of uint64. Confusingly, this means that everything + // works if we make the literal unsigned, even though we are negating it. + EXPECT_EQ(-GOOGLE_ULONGLONG(9223372036854775808), message.repeated_int64(1)); + + ASSERT_EQ(2, message.repeated_uint32_size()); + EXPECT_EQ(4294967295u, message.repeated_uint32(0)); + EXPECT_EQ(2147483648u, message.repeated_uint32(1)); + + ASSERT_EQ(2, message.repeated_uint64_size()); + EXPECT_EQ(GOOGLE_ULONGLONG(18446744073709551615), message.repeated_uint64(0)); + EXPECT_EQ(GOOGLE_ULONGLONG(9223372036854775808), message.repeated_uint64(1)); + + ASSERT_EQ(13, message.repeated_double_size()); + EXPECT_EQ(123.0 , message.repeated_double(0)); + EXPECT_EQ(123.5 , message.repeated_double(1)); + EXPECT_EQ(0.125 , message.repeated_double(2)); + EXPECT_EQ(1.23E17 , message.repeated_double(3)); + EXPECT_EQ(1.235E22 , message.repeated_double(4)); + EXPECT_EQ(1.235E-18 , message.repeated_double(5)); + EXPECT_EQ(123.456789, message.repeated_double(6)); + EXPECT_EQ(message.repeated_double(7), numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(8), numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(9), -numeric_limits::infinity()); + EXPECT_EQ(message.repeated_double(10), -numeric_limits::infinity()); + EXPECT_TRUE(IsNaN(message.repeated_double(11))); + EXPECT_TRUE(IsNaN(message.repeated_double(12))); + + // Note: Since these string literals have \0's in them, we must explicitly + // pass their sizes to string's constructor. + ASSERT_EQ(1, message.repeated_string_size()); + EXPECT_EQ(string("\000\001\a\b\f\n\r\t\v\\\'\"", 12), + message.repeated_string(0)); +} + +class TextFormatParserTest : public testing::Test { + protected: + void ExpectFailure(const string& input, const string& message, int line, + int col) { + unittest::TestAllTypes proto; + ExpectFailure(input, message, line, col, &proto); + } + + void ExpectFailure(const string& input, const string& message, int line, + int col, Message* proto) { + TextFormat::Parser parser; + MockErrorCollector error_collector; + parser.RecordErrorsTo(&error_collector); + EXPECT_FALSE(parser.ParseFromString(input, proto)); + EXPECT_EQ(SimpleItoa(line) + ":" + SimpleItoa(col) + ": " + message + "\n", + error_collector.text_); + } + + // An error collector which simply concatenates all its errors into a big + // block of text which can be checked. + class MockErrorCollector : public io::ErrorCollector { + public: + MockErrorCollector() {} + ~MockErrorCollector() {} + + string text_; + + // implements ErrorCollector ------------------------------------- + void AddError(int line, int column, const string& message) { + strings::SubstituteAndAppend(&text_, "$0:$1: $2\n", + line + 1, column + 1, message); + } + }; +}; + +TEST_F(TextFormatParserTest, InvalidToken) { + ExpectFailure("optional_bool: true\n-5\n", "Expected identifier.", + 2, 1); + + ExpectFailure("optional_bool: true;\n", "Expected identifier.", 1, 20); + ExpectFailure("\"some string\"", "Expected identifier.", 1, 1); +} + +TEST_F(TextFormatParserTest, InvalidFieldName) { + ExpectFailure( + "invalid_field: somevalue\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"invalid_field\".", + 1, 14); +} + +TEST_F(TextFormatParserTest, InvalidCapitalization) { + // We require that group names be exactly as they appear in the .proto. + ExpectFailure( + "optionalgroup {\na: 15\n}\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"optionalgroup\".", + 1, 15); + ExpectFailure( + "OPTIONALgroup {\na: 15\n}\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"OPTIONALgroup\".", + 1, 15); + ExpectFailure( + "Optional_Double: 10.0\n", + "Message type \"protobuf_unittest.TestAllTypes\" has no field named " + "\"Optional_Double\".", + 1, 16); +} + +TEST_F(TextFormatParserTest, InvalidFieldValues) { + // Invalid values for a double/float field. + ExpectFailure("optional_double: \"hello\"\n", "Expected double.", 1, 18); + ExpectFailure("optional_double: true\n", "Expected double.", 1, 18); + ExpectFailure("optional_double: !\n", "Expected double.", 1, 18); + ExpectFailure("optional_double {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + + // Invalid values for a signed integer field. + ExpectFailure("optional_int32: \"hello\"\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: true\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: 4.5\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32: !\n", "Expected integer.", 1, 17); + ExpectFailure("optional_int32 {\n \n}\n", "Expected \":\", found \"{\".", + 1, 16); + ExpectFailure("optional_int32: 0x80000000\n", + "Integer out of range.", 1, 17); + ExpectFailure("optional_int32: -0x80000001\n", + "Integer out of range.", 1, 18); + ExpectFailure("optional_int64: 0x8000000000000000\n", + "Integer out of range.", 1, 17); + ExpectFailure("optional_int64: -0x8000000000000001\n", + "Integer out of range.", 1, 18); + + // Invalid values for an unsigned integer field. + ExpectFailure("optional_uint64: \"hello\"\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: true\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: 4.5\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: -5\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64: !\n", "Expected integer.", 1, 18); + ExpectFailure("optional_uint64 {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + ExpectFailure("optional_uint32: 0x100000000\n", + "Integer out of range.", 1, 18); + ExpectFailure("optional_uint64: 0x10000000000000000\n", + "Integer out of range.", 1, 18); + + // Invalid values for a boolean field. + ExpectFailure("optional_bool: \"hello\"\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: 5\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: -7.5\n", "Expected identifier.", 1, 16); + ExpectFailure("optional_bool: !\n", "Expected identifier.", 1, 16); + + ExpectFailure( + "optional_bool: meh\n", + "Invalid value for boolean field \"optional_bool\". Value: \"meh\".", + 2, 1); + + ExpectFailure("optional_bool {\n \n}\n", "Expected \":\", found \"{\".", + 1, 15); + + // Invalid values for a string field. + ExpectFailure("optional_string: true\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: 5\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: -7.5\n", "Expected string.", 1, 18); + ExpectFailure("optional_string: !\n", "Expected string.", 1, 18); + ExpectFailure("optional_string {\n \n}\n", "Expected \":\", found \"{\".", + 1, 17); + + // Invalid values for an enumeration field. + ExpectFailure("optional_nested_enum: \"hello\"\n", "Expected identifier.", + 1, 23); + + ExpectFailure("optional_nested_enum: 5\n", "Expected identifier.", 1, 23); + ExpectFailure("optional_nested_enum: -7.5\n", "Expected identifier.", 1, 23); + ExpectFailure("optional_nested_enum: !\n", "Expected identifier.", 1, 23); + + ExpectFailure( + "optional_nested_enum: grah\n", + "Unknown enumeration value of \"grah\" for field " + "\"optional_nested_enum\".", 2, 1); + + ExpectFailure( + "optional_nested_enum {\n \n}\n", + "Expected \":\", found \"{\".", 1, 22); +} + +TEST_F(TextFormatParserTest, MessageDelimeters) { + // Non-matching delimeters. + ExpectFailure("OptionalGroup <\n \n}\n", "Expected \">\", found \"}\".", + 3, 1); + + // Invalid delimeters. + ExpectFailure("OptionalGroup [\n \n]\n", "Expected \"{\", found \"[\".", + 1, 15); + + // Unending message. + ExpectFailure("optional_nested_message {\n \nbb: 118\n", + "Expected identifier.", + 4, 1); +} + +TEST_F(TextFormatParserTest, UnknownExtension) { + // Non-matching delimeters. + ExpectFailure("[blahblah]: 123", + "Extension \"blahblah\" is not defined or is not an " + "extension of \"protobuf_unittest.TestAllTypes\".", + 1, 11); +} + +TEST_F(TextFormatParserTest, MissingRequired) { + unittest::TestRequired message; + ExpectFailure("a: 1", + "Message missing required fields: b, c", + 0, 1, &message); +} + +TEST_F(TextFormatParserTest, PrintErrorsToStderr) { + vector errors; + + { + ScopedMemoryLog log; + unittest::TestAllTypes proto; + EXPECT_FALSE(TextFormat::ParseFromString("no_such_field: 1", &proto)); + errors = log.GetMessages(ERROR); + } + + ASSERT_EQ(1, errors.size()); + EXPECT_EQ("Error parsing text-format protobuf_unittest.TestAllTypes: " + "1:14: Message type \"protobuf_unittest.TestAllTypes\" has no field " + "named \"no_such_field\".", + errors[0]); +} + + +class TextFormatMessageSetTest : public testing::Test { + protected: + static const char proto_debug_string_[]; +}; +const char TextFormatMessageSetTest::proto_debug_string_[] = +"message_set {\n" +" [protobuf_unittest.TestMessageSetExtension1] {\n" +" i: 23\n" +" }\n" +" [protobuf_unittest.TestMessageSetExtension2] {\n" +" str: \"foo\"\n" +" }\n" +"}\n"; + + +TEST_F(TextFormatMessageSetTest, Serialize) { + protobuf_unittest::TestMessageSetContainer proto; + protobuf_unittest::TestMessageSetExtension1* item_a = + proto.mutable_message_set()->MutableExtension( + protobuf_unittest::TestMessageSetExtension1::message_set_extension); + item_a->set_i(23); + protobuf_unittest::TestMessageSetExtension2* item_b = + proto.mutable_message_set()->MutableExtension( + protobuf_unittest::TestMessageSetExtension2::message_set_extension); + item_b->set_str("foo"); + EXPECT_EQ(proto_debug_string_, proto.DebugString()); +} + +TEST_F(TextFormatMessageSetTest, Deserialize) { + protobuf_unittest::TestMessageSetContainer proto; + ASSERT_TRUE(TextFormat::ParseFromString(proto_debug_string_, &proto)); + EXPECT_EQ(23, proto.message_set().GetExtension( + protobuf_unittest::TestMessageSetExtension1::message_set_extension).i()); + EXPECT_EQ("foo", proto.message_set().GetExtension( + protobuf_unittest::TestMessageSetExtension2::message_set_extension).str()); + + // Ensure that these are the only entries present. + vector descriptors; + proto.message_set().GetReflection()->ListFields( + proto.message_set(), &descriptors); + EXPECT_EQ(2, descriptors.size()); +} + +} // namespace +} // namespace protobuf + +} // namespace google diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto new file mode 100644 index 00000000..f65c4318 --- /dev/null +++ b/src/google/protobuf/unittest.proto @@ -0,0 +1,452 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file we will use for unit testing. + + +import "google/protobuf/unittest_import.proto"; + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do "using namespace unittest = protobuf_unittest". +package protobuf_unittest; + +// Protos optimized for SPEED use a strict superset of the generated code +// of equivalent ones optimized for CODE_SIZE, so we should optimize all our +// tests for speed unless explicitly testing code size optimization. +option optimize_for = SPEED; + +option java_outer_classname = "UnittestProto"; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + 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 ForeignMessage optional_foreign_message = 19; + optional protobuf_unittest_import.ImportMessage optional_import_message = 20; + + optional NestedEnum optional_nested_enum = 21; + optional ForeignEnum optional_foreign_enum = 22; + optional protobuf_unittest_import.ImportEnum 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 ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // 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 NestedEnum default_nested_enum = 81 [default = BAR ]; + optional ForeignEnum default_foreign_enum = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum = 83 [default = IMPORT_BAR]; + + optional string default_string_piece = 84 [ctype=STRING_PIECE,default="abc"]; + optional string default_cord = 85 [ctype=CORD,default="123"]; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + optional int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +message TestAllExtensions { + extensions 1 to max; +} + +extend TestAllExtensions { + // Singular + optional int32 optional_int32_extension = 1; + optional int64 optional_int64_extension = 2; + optional uint32 optional_uint32_extension = 3; + optional uint64 optional_uint64_extension = 4; + optional sint32 optional_sint32_extension = 5; + optional sint64 optional_sint64_extension = 6; + optional fixed32 optional_fixed32_extension = 7; + optional fixed64 optional_fixed64_extension = 8; + optional sfixed32 optional_sfixed32_extension = 9; + optional sfixed64 optional_sfixed64_extension = 10; + optional float optional_float_extension = 11; + optional double optional_double_extension = 12; + optional bool optional_bool_extension = 13; + optional string optional_string_extension = 14; + optional bytes optional_bytes_extension = 15; + + optional group OptionalGroup_extension = 16 { + optional int32 a = 17; + } + + optional TestAllTypes.NestedMessage optional_nested_message_extension = 18; + optional ForeignMessage optional_foreign_message_extension = 19; + optional protobuf_unittest_import.ImportMessage + optional_import_message_extension = 20; + + optional TestAllTypes.NestedEnum optional_nested_enum_extension = 21; + optional ForeignEnum optional_foreign_enum_extension = 22; + optional protobuf_unittest_import.ImportEnum + optional_import_enum_extension = 23; + + optional string optional_string_piece_extension = 24 [ctype=STRING_PIECE]; + optional string optional_cord_extension = 25 [ctype=CORD]; + + // Repeated + repeated int32 repeated_int32_extension = 31; + repeated int64 repeated_int64_extension = 32; + repeated uint32 repeated_uint32_extension = 33; + repeated uint64 repeated_uint64_extension = 34; + repeated sint32 repeated_sint32_extension = 35; + repeated sint64 repeated_sint64_extension = 36; + repeated fixed32 repeated_fixed32_extension = 37; + repeated fixed64 repeated_fixed64_extension = 38; + repeated sfixed32 repeated_sfixed32_extension = 39; + repeated sfixed64 repeated_sfixed64_extension = 40; + repeated float repeated_float_extension = 41; + repeated double repeated_double_extension = 42; + repeated bool repeated_bool_extension = 43; + repeated string repeated_string_extension = 44; + repeated bytes repeated_bytes_extension = 45; + + repeated group RepeatedGroup_extension = 46 { + optional int32 a = 47; + } + + repeated TestAllTypes.NestedMessage repeated_nested_message_extension = 48; + repeated ForeignMessage repeated_foreign_message_extension = 49; + repeated protobuf_unittest_import.ImportMessage + repeated_import_message_extension = 50; + + repeated TestAllTypes.NestedEnum repeated_nested_enum_extension = 51; + repeated ForeignEnum repeated_foreign_enum_extension = 52; + repeated protobuf_unittest_import.ImportEnum + repeated_import_enum_extension = 53; + + repeated string repeated_string_piece_extension = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord_extension = 55 [ctype=CORD]; + + // Singular with defaults + optional int32 default_int32_extension = 61 [default = 41 ]; + optional int64 default_int64_extension = 62 [default = 42 ]; + optional uint32 default_uint32_extension = 63 [default = 43 ]; + optional uint64 default_uint64_extension = 64 [default = 44 ]; + optional sint32 default_sint32_extension = 65 [default = -45 ]; + optional sint64 default_sint64_extension = 66 [default = 46 ]; + optional fixed32 default_fixed32_extension = 67 [default = 47 ]; + optional fixed64 default_fixed64_extension = 68 [default = 48 ]; + optional sfixed32 default_sfixed32_extension = 69 [default = 49 ]; + optional sfixed64 default_sfixed64_extension = 70 [default = -50 ]; + optional float default_float_extension = 71 [default = 51.5 ]; + optional double default_double_extension = 72 [default = 52e3 ]; + optional bool default_bool_extension = 73 [default = true ]; + optional string default_string_extension = 74 [default = "hello"]; + optional bytes default_bytes_extension = 75 [default = "world"]; + + optional TestAllTypes.NestedEnum + default_nested_enum_extension = 81 [default = BAR]; + optional ForeignEnum + default_foreign_enum_extension = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum_extension = 83 [default = IMPORT_BAR]; + + optional string default_string_piece_extension = 84 [ctype=STRING_PIECE, + default="abc"]; + optional string default_cord_extension = 85 [ctype=CORD, default="123"]; +} + +// We have separate messages for testing required fields because it's +// annoying to have to fill in required fields in TestProto in order to +// do anything with it. Note that we don't need to test every type of +// required filed because the code output is basically identical to +// optional fields for all types. +message TestRequired { + required int32 a = 1; + optional int32 dummy2 = 2; + required int32 b = 3; + + extend TestAllExtensions { + optional TestRequired single = 1000; + repeated TestRequired multi = 1001; + } + + // Pad the field count to 32 so that we can test that IsInitialized() + // properly checks multiple elements of has_bits_. + optional int32 dummy4 = 4; + optional int32 dummy5 = 5; + optional int32 dummy6 = 6; + optional int32 dummy7 = 7; + optional int32 dummy8 = 8; + optional int32 dummy9 = 9; + optional int32 dummy10 = 10; + optional int32 dummy11 = 11; + optional int32 dummy12 = 12; + optional int32 dummy13 = 13; + optional int32 dummy14 = 14; + optional int32 dummy15 = 15; + optional int32 dummy16 = 16; + optional int32 dummy17 = 17; + optional int32 dummy18 = 18; + optional int32 dummy19 = 19; + optional int32 dummy20 = 20; + optional int32 dummy21 = 21; + optional int32 dummy22 = 22; + optional int32 dummy23 = 23; + optional int32 dummy24 = 24; + optional int32 dummy25 = 25; + optional int32 dummy26 = 26; + optional int32 dummy27 = 27; + optional int32 dummy28 = 28; + optional int32 dummy29 = 29; + optional int32 dummy30 = 30; + optional int32 dummy31 = 31; + optional int32 dummy32 = 32; + + required int32 c = 33; +} + +message TestRequiredForeign { + optional TestRequired optional_message = 1; + repeated TestRequired repeated_message = 2; + optional int32 dummy = 3; +} + +// Test that we can use NestedMessage from outside TestAllTypes. +message TestForeignNested { + optional TestAllTypes.NestedMessage foreign_nested = 1; +} + +// TestEmptyMessage is used to test unknown field support. +message TestEmptyMessage { +} + +// Like above, but declare all field numbers as potential extensions. No +// actual extensions should ever be defined for this type. +message TestEmptyMessageWithExtensions { + extensions 1 to max; +} + +// Test that really large tag numbers don't break anything. +message TestReallyLargeTagNumber { + // The largest possible tag number is 2^28 - 1, since the wire format uses + // three bits to communicate wire type. + optional int32 a = 1; + optional int32 bb = 268435455; +} + +message TestRecursiveMessage { + optional TestRecursiveMessage a = 1; + optional int32 i = 2; +} + +// Test that mutual recursion works. +message TestMutualRecursionA { + optional TestMutualRecursionB bb = 1; +} + +message TestMutualRecursionB { + optional TestMutualRecursionA a = 1; + optional int32 optional_int32 = 2; +} + +// Test that groups have disjoint field numbers from their siblings and +// parents. This is NOT possible in proto1; only proto2. When outputting +// proto1, the dup fields should be dropped. +message TestDupFieldNumber { + optional int32 a = 1; + optional group Foo = 2 { optional int32 a = 1; } + optional group Bar = 3 { optional int32 a = 1; } +} + + +// Needed for a Python test. +message TestNestedMessageHasBits { + message NestedMessage { + repeated int32 nestedmessage_repeated_int32 = 1; + repeated ForeignMessage nestedmessage_repeated_foreignmessage = 2; + } + optional NestedMessage optional_nested_message = 1; +} + + +// Test an enum that has multiple values with the same number. +enum TestEnumWithDupValue { + FOO1 = 1; + BAR1 = 2; + BAZ = 3; + FOO2 = 1; + BAR2 = 2; +} + +// Test an enum with large, unordered values. +enum TestSparseEnum { + SPARSE_A = 123; + SPARSE_B = 62374; + SPARSE_C = 12589234; + SPARSE_D = -15; + SPARSE_E = -53452; + SPARSE_F = 0; + SPARSE_G = 2; +} + +// Test message with CamelCase field names. This violates Protocol Buffer +// standard style. +message TestCamelCaseFieldNames { + optional int32 PrimitiveField = 1; + optional string StringField = 2; + optional ForeignEnum EnumField = 3; + optional ForeignMessage MessageField = 4; + optional string StringPieceField = 5 [ctype=STRING_PIECE]; + optional string CordField = 6 [ctype=CORD]; + + repeated int32 RepeatedPrimitiveField = 7; + repeated string RepeatedStringField = 8; + repeated ForeignEnum RepeatedEnumField = 9; + repeated ForeignMessage RepeatedMessageField = 10; + repeated string RepeatedStringPieceField = 11 [ctype=STRING_PIECE]; + repeated string RepeatedCordField = 12 [ctype=CORD]; +} + + +// We list fields out of order, to ensure that we're using field number and not +// field index to determine serialization order. +message TestFieldOrderings { + optional string my_string = 11; + extensions 2 to 10; + optional int64 my_int = 1; + extensions 12 to 100; + optional float my_float = 101; +} + + +extend TestFieldOrderings { + optional string my_extension_string = 50; + optional int32 my_extension_int = 5; +} + + +message TestExtremeDefaultValues { + optional bytes escaped_bytes = 1 [default = "\0\001\a\b\f\n\r\t\v\\\'\"\xfe"]; + optional uint32 large_uint32 = 2 [default = 0xFFFFFFFF]; + optional uint64 large_uint64 = 3 [default = 0xFFFFFFFFFFFFFFFF]; + optional int32 small_int32 = 4 [default = -0x7FFFFFFF]; + optional int64 small_int64 = 5 [default = -0x7FFFFFFFFFFFFFFF]; + + // The default value here is UTF-8 for "\u1234". (We could also just type + // the UTF-8 text directly into this text file rather than escape it, but + // lots of people use editors that would be confused by this.) + optional string utf8_string = 6 [default = "\341\210\264"]; +} + +// Test that RPC services work. +message FooRequest {} +message FooResponse {} + +service TestService { + rpc Foo(FooRequest) returns (FooResponse); + rpc Bar(BarRequest) returns (BarResponse); +} + + +message BarRequest {} +message BarResponse {} diff --git a/src/google/protobuf/unittest_embed_optimize_for.proto b/src/google/protobuf/unittest_embed_optimize_for.proto new file mode 100644 index 00000000..c600d9fc --- /dev/null +++ b/src/google/protobuf/unittest_embed_optimize_for.proto @@ -0,0 +1,36 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which imports a proto file that uses optimize_for = CODE_SIZE. + +import "google/protobuf/unittest_optimize_for.proto"; + +package protobuf_unittest; + +// We optimize for speed here, but we are importing a proto that is optimized +// for code size. +option optimize_for = SPEED; + +message TestEmbedOptimizedForSize { + // Test that embedding a message which has optimize_for = CODE_SIZE into + // one optimized for speed works. + optional TestOptimizedForSize optional_message = 1; + repeated TestOptimizedForSize repeated_message = 2; +} diff --git a/src/google/protobuf/unittest_import.proto b/src/google/protobuf/unittest_import.proto new file mode 100644 index 00000000..58ce42c3 --- /dev/null +++ b/src/google/protobuf/unittest_import.proto @@ -0,0 +1,47 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which is imported by unittest.proto to test importing. + + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do +// "using namespace unittest_import = protobuf_unittest_import". +package protobuf_unittest_import; + +option optimize_for = SPEED; + +// Excercise the java_package option. +option java_package = "com.google.protobuf.test"; + +// Do not set a java_outer_classname here to verify that Proto2 works without +// one. + +message ImportMessage { + optional int32 d = 1; +} + +enum ImportEnum { + IMPORT_FOO = 7; + IMPORT_BAR = 8; + IMPORT_BAZ = 9; +} + diff --git a/src/google/protobuf/unittest_mset.proto b/src/google/protobuf/unittest_mset.proto new file mode 100644 index 00000000..455086d2 --- /dev/null +++ b/src/google/protobuf/unittest_mset.proto @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains messages for testing message_set_wire_format. + +package protobuf_unittest; + +option optimize_for = SPEED; + +// A message with message_set_wire_format. +message TestMessageSet { + option message_set_wire_format = true; + extensions 4 to max; +} + +message TestMessageSetContainer { + optional TestMessageSet message_set = 1; +} + +message TestMessageSetExtension1 { + extend TestMessageSet { + optional TestMessageSetExtension1 message_set_extension = 1545008; + } + optional int32 i = 15; +} + +message TestMessageSetExtension2 { + extend TestMessageSet { + optional TestMessageSetExtension2 message_set_extension = 1547769; + } + optional string str = 25; +} + +// MessageSet wire format is equivalent to this. +message RawMessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required bytes message = 3; + } +} + diff --git a/src/google/protobuf/unittest_optimize_for.proto b/src/google/protobuf/unittest_optimize_for.proto new file mode 100644 index 00000000..6154e9c5 --- /dev/null +++ b/src/google/protobuf/unittest_optimize_for.proto @@ -0,0 +1,38 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which uses optimize_for = CODE_SIZE. + +import "google/protobuf/unittest.proto"; + +package protobuf_unittest; + +option optimize_for = CODE_SIZE; + +message TestOptimizedForSize { + optional int32 i = 1; + optional ForeignMessage msg = 19; + + extensions 1000 to max; + + extend TestOptimizedForSize { + optional int32 test_extension = 1234; + } +} diff --git a/src/google/protobuf/unknown_field_set.cc b/src/google/protobuf/unknown_field_set.cc new file mode 100644 index 00000000..3d45002e --- /dev/null +++ b/src/google/protobuf/unknown_field_set.cc @@ -0,0 +1,144 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +UnknownFieldSet::UnknownFieldSet() + : internal_(NULL) {} + +UnknownFieldSet::~UnknownFieldSet() { + if (internal_ != NULL) { + STLDeleteValues(&internal_->fields_); + delete internal_; + } +} + +void UnknownFieldSet::Clear() { + if (internal_ == NULL) return; + + if (internal_->fields_.size() > kMaxInactiveFields) { + STLDeleteValues(&internal_->fields_); + } else { + // Don't delete the UnknownField objects. Just remove them from the active + // set. + for (int i = 0; i < internal_->active_fields_.size(); i++) { + internal_->active_fields_[i]->Clear(); + internal_->active_fields_[i]->index_ = -1; + } + } + + internal_->active_fields_.clear(); +} + +void UnknownFieldSet::MergeFrom(const UnknownFieldSet& other) { + for (int i = 0; i < other.field_count(); i++) { + AddField(other.field(i).number())->MergeFrom(other.field(i)); + } +} + +bool UnknownFieldSet::MergeFromCodedStream(io::CodedInputStream* input) { + + UnknownFieldSet other; + if (internal::WireFormat::SkipMessage(input, &other) && + input->ConsumedEntireMessage()) { + MergeFrom(other); + return true; + } else { + return false; + } +} + +bool UnknownFieldSet::ParseFromCodedStream(io::CodedInputStream* input) { + Clear(); + return MergeFromCodedStream(input); +} + +bool UnknownFieldSet::ParseFromZeroCopyStream(io::ZeroCopyInputStream* input) { + io::CodedInputStream coded_input(input); + return ParseFromCodedStream(&coded_input) && + coded_input.ConsumedEntireMessage(); +} + +bool UnknownFieldSet::ParseFromArray(const void* data, int size) { + io::ArrayInputStream input(data, size); + return ParseFromZeroCopyStream(&input); +} + +const UnknownField* UnknownFieldSet::FindFieldByNumber(int number) const { + if (internal_ == NULL) return NULL; + + map::iterator iter = internal_->fields_.find(number); + if (iter != internal_->fields_.end() && iter->second->index() != -1) { + return iter->second; + } else { + return NULL; + } +} + +UnknownField* UnknownFieldSet::AddField(int number) { + if (internal_ == NULL) internal_ = new Internal; + + UnknownField** map_slot = &internal_->fields_[number]; + if (*map_slot == NULL) { + *map_slot = new UnknownField(number); + } + + UnknownField* field = *map_slot; + if (field->index() == -1) { + field->index_ = internal_->active_fields_.size(); + internal_->active_fields_.push_back(field); + } + return field; +} + +UnknownField::UnknownField(int number) + : number_(number), + index_(-1) { +} + +UnknownField::~UnknownField() { +} + +void UnknownField::Clear() { + clear_varint(); + clear_fixed32(); + clear_fixed64(); + clear_length_delimited(); + clear_group(); +} + +void UnknownField::MergeFrom(const UnknownField& other) { + varint_ .MergeFrom(other.varint_ ); + fixed32_ .MergeFrom(other.fixed32_ ); + fixed64_ .MergeFrom(other.fixed64_ ); + length_delimited_.MergeFrom(other.length_delimited_); + group_ .MergeFrom(other.group_ ); +} + +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/unknown_field_set.h b/src/google/protobuf/unknown_field_set.h new file mode 100644 index 00000000..a3dce33d --- /dev/null +++ b/src/google/protobuf/unknown_field_set.h @@ -0,0 +1,333 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// Contains classes used to keep track of unrecognized fields seen while +// parsing a protocol message. + +#ifndef GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ +#define GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ + +#include +#include +#include +#include + +namespace google { +namespace protobuf { + +class Message; // message.h +class UnknownField; // below + +// An UnknownFieldSet contains fields that were encountered while parsing a +// message but were not defined by its type. Keeping track of these can be +// useful, especially in that they may be written if the message is serialized +// again without being cleared in between. This means that software which +// simply receives messages and forwards them to other servers does not need +// to be updated every time a new field is added to the message definition. +// +// To get the UnknownFieldSet attached to any message, call +// Reflection::GetUnknownFields(). +// +// This class is necessarily tied to the protocol buffer wire format, unlike +// the Reflection interface which is independent of any serialization scheme. +class LIBPROTOBUF_EXPORT UnknownFieldSet { + public: + UnknownFieldSet(); + ~UnknownFieldSet(); + + // Remove all fields. + void Clear(); + + // Is this set empty? + inline bool empty() const; + + // Merge the contents of some other UnknownFieldSet with this one. + void MergeFrom(const UnknownFieldSet& other); + + // Returns the number of fields present in the UnknownFieldSet. + inline int field_count() const; + // Get a field in the set, where 0 <= index < field_count(). The fields + // appear in arbitrary order. + inline const UnknownField& field(int index) const; + // Get a mutable pointer to a field in the set, where + // 0 <= index < field_count(). The fields appear in arbitrary order. + inline UnknownField* mutable_field(int index); + + // Find a field by field number. Returns NULL if not found. + const UnknownField* FindFieldByNumber(int number) const; + + // Add a field by field number. If the field number already exists, returns + // the existing UnknownField. + UnknownField* AddField(int number); + + // Parsing helpers ------------------------------------------------- + // These work exactly like the similarly-named methods of Message. + + bool MergeFromCodedStream(io::CodedInputStream* input); + bool ParseFromCodedStream(io::CodedInputStream* input); + bool ParseFromZeroCopyStream(io::ZeroCopyInputStream* input); + bool ParseFromArray(const void* data, int size); + inline bool ParseFromString(const string& data) { + return ParseFromArray(data.data(), data.size()); + } + + private: + // "Active" fields are ones which have been added since the last time Clear() + // was called. Inactive fields are objects we are keeping around incase + // they become active again. + + struct Internal { + // Contains all UnknownFields that have been allocated for this + // UnknownFieldSet, including ones not currently active. Keyed by + // field number. We intentionally try to reuse UnknownField objects for + // the same field number they were used for originally because this makes + // it more likely that the previously-allocated memory will have the right + // layout. + map fields_; + + // Contains the fields from fields_ that are currently active. + vector active_fields_; + }; + + // We want an UnknownFieldSet to use no more space than a single pointer + // until the first field is added. + Internal* internal_; + + // Don't keep more inactive fields than this. + static const int kMaxInactiveFields = 100; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownFieldSet); +}; + +// Represents one field in an UnknownFieldSet. +// +// UnknownFiled's accessors are similar to those that would be produced by the +// protocol compiler for the fields: +// repeated uint64 varint; +// repeated fixed32 fixed32; +// repeated fixed64 fixed64; +// repeated bytes length_delimited; +// repeated UnknownFieldSet group; +// (OK, so the last one isn't actually a valid field type but you get the +// idea.) +class LIBPROTOBUF_EXPORT UnknownField { + public: + ~UnknownField(); + + // Clears all fields. + void Clear(); + + // Merge the contents of some other UnknownField with this one. For each + // wire type, the values are simply concatenated. + void MergeFrom(const UnknownField& other); + + // The field's tag number, as seen on the wire. + inline int number() const; + + // The index of this UnknownField within the UknownFieldSet (e.g. + // set.field(field.index()) == field). + inline int index() const; + + inline int varint_size () const; + inline int fixed32_size () const; + inline int fixed64_size () const; + inline int length_delimited_size() const; + inline int group_size () const; + + inline uint64 varint (int index) const; + inline uint32 fixed32(int index) const; + inline uint64 fixed64(int index) const; + inline const string& length_delimited(int index) const; + inline const UnknownFieldSet& group(int index) const; + + inline void set_varint (int index, uint64 value); + inline void set_fixed32(int index, uint32 value); + inline void set_fixed64(int index, uint64 value); + inline void set_length_delimited(int index, const string& value); + inline string* mutable_length_delimited(int index); + inline UnknownFieldSet* mutable_group(int index); + + inline void add_varint (uint64 value); + inline void add_fixed32(uint32 value); + inline void add_fixed64(uint64 value); + inline void add_length_delimited(const string& value); + inline string* add_length_delimited(); + inline UnknownFieldSet* add_group(); + + inline void clear_varint (); + inline void clear_fixed32(); + inline void clear_fixed64(); + inline void clear_length_delimited(); + inline void clear_group(); + + inline const RepeatedField & varint () const; + inline const RepeatedField & fixed32 () const; + inline const RepeatedField & fixed64 () const; + inline const RepeatedPtrField& length_delimited() const; + inline const RepeatedPtrField& group () const; + + inline RepeatedField * mutable_varint (); + inline RepeatedField * mutable_fixed32 (); + inline RepeatedField * mutable_fixed64 (); + inline RepeatedPtrField* mutable_length_delimited(); + inline RepeatedPtrField* mutable_group (); + + private: + friend class UnknownFieldSet; + UnknownField(int number); + + int number_; + int index_; + + RepeatedField varint_; + RepeatedField fixed32_; + RepeatedField fixed64_; + RepeatedPtrField length_delimited_; + RepeatedPtrField group_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(UnknownField); +}; + +// =================================================================== +// inline implementations + +inline bool UnknownFieldSet::empty() const { + return internal_ == NULL || internal_->active_fields_.empty(); +} + +inline int UnknownFieldSet::field_count() const { + return (internal_ == NULL) ? 0 : internal_->active_fields_.size(); +} +inline const UnknownField& UnknownFieldSet::field(int index) const { + return *(internal_->active_fields_[index]); +} +inline UnknownField* UnknownFieldSet::mutable_field(int index) { + return internal_->active_fields_[index]; +} + +inline int UnknownField::number() const { return number_; } +inline int UnknownField::index () const { return index_; } + +inline int UnknownField::varint_size () const {return varint_.size();} +inline int UnknownField::fixed32_size () const {return fixed32_.size();} +inline int UnknownField::fixed64_size () const {return fixed64_.size();} +inline int UnknownField::length_delimited_size() const { + return length_delimited_.size(); +} +inline int UnknownField::group_size () const {return group_.size();} + +inline uint64 UnknownField::varint (int index) const { + return varint_.Get(index); +} +inline uint32 UnknownField::fixed32(int index) const { + return fixed32_.Get(index); +} +inline uint64 UnknownField::fixed64(int index) const { + return fixed64_.Get(index); +} +inline const string& UnknownField::length_delimited(int index) const { + return length_delimited_.Get(index); +} +inline const UnknownFieldSet& UnknownField::group(int index) const { + return group_.Get(index); +} + +inline void UnknownField::set_varint (int index, uint64 value) { + varint_.Set(index, value); +} +inline void UnknownField::set_fixed32(int index, uint32 value) { + fixed32_.Set(index, value); +} +inline void UnknownField::set_fixed64(int index, uint64 value) { + fixed64_.Set(index, value); +} +inline void UnknownField::set_length_delimited(int index, const string& value) { + length_delimited_.Mutable(index)->assign(value); +} +inline string* UnknownField::mutable_length_delimited(int index) { + return length_delimited_.Mutable(index); +} +inline UnknownFieldSet* UnknownField::mutable_group(int index) { + return group_.Mutable(index); +} + +inline void UnknownField::add_varint (uint64 value) { + varint_.Add(value); +} +inline void UnknownField::add_fixed32(uint32 value) { + fixed32_.Add(value); +} +inline void UnknownField::add_fixed64(uint64 value) { + fixed64_.Add(value); +} +inline void UnknownField::add_length_delimited(const string& value) { + length_delimited_.Add()->assign(value); +} +inline string* UnknownField::add_length_delimited() { + return length_delimited_.Add(); +} +inline UnknownFieldSet* UnknownField::add_group() { + return group_.Add(); +} + +inline void UnknownField::clear_varint () { varint_.Clear(); } +inline void UnknownField::clear_fixed32() { varint_.Clear(); } +inline void UnknownField::clear_fixed64() { varint_.Clear(); } +inline void UnknownField::clear_length_delimited() { + length_delimited_.Clear(); +} +inline void UnknownField::clear_group() { group_.Clear(); } + +inline const RepeatedField& UnknownField::varint () const { + return varint_; +} +inline const RepeatedField& UnknownField::fixed32() const { + return fixed32_; +} +inline const RepeatedField& UnknownField::fixed64() const { + return fixed64_; +} +inline const RepeatedPtrField& UnknownField::length_delimited() const { + return length_delimited_; +} +inline const RepeatedPtrField& UnknownField::group() const { + return group_; +} + +inline RepeatedField* UnknownField::mutable_varint () { + return &varint_; +} +inline RepeatedField* UnknownField::mutable_fixed32() { + return &fixed32_; +} +inline RepeatedField* UnknownField::mutable_fixed64() { + return &fixed64_; +} +inline RepeatedPtrField* UnknownField::mutable_length_delimited() { + return &length_delimited_; +} +inline RepeatedPtrField* UnknownField::mutable_group() { + return &group_; +} + +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_UNKNOWN_FIELD_SET_H__ diff --git a/src/google/protobuf/unknown_field_set_unittest.cc b/src/google/protobuf/unknown_field_set_unittest.cc new file mode 100644 index 00000000..63f1dffd --- /dev/null +++ b/src/google/protobuf/unknown_field_set_unittest.cc @@ -0,0 +1,417 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This test is testing a lot more than just the UnknownFieldSet class. It +// tests handling of unknown fields throughout the system. + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { + +using internal::WireFormat; + +namespace { + +class UnknownFieldSetTest : public testing::Test { + protected: + virtual void SetUp() { + descriptor_ = unittest::TestAllTypes::descriptor(); + TestUtil::SetAllFields(&all_fields_); + all_fields_.SerializeToString(&all_fields_data_); + ASSERT_TRUE(empty_message_.ParseFromString(all_fields_data_)); + unknown_fields_ = empty_message_.mutable_unknown_fields(); + } + + const UnknownField* GetField(const string& name) { + const FieldDescriptor* field = descriptor_->FindFieldByName(name); + if (field == NULL) return NULL; + return unknown_fields_->FindFieldByNumber(field->number()); + } + + // Constructs a protocol buffer which contains fields with all the same + // numbers as all_fields_data_ except that each field is some other wire + // type. + string GetBizarroData() { + unittest::TestEmptyMessage bizarro_message; + UnknownFieldSet* bizarro_unknown_fields = + bizarro_message.mutable_unknown_fields(); + for (int i = 0; i < unknown_fields_->field_count(); i++) { + const UnknownField& unknown_field = unknown_fields_->field(i); + UnknownField* bizarro_field = + bizarro_unknown_fields->AddField(unknown_field.number()); + if (unknown_field.varint_size() == 0) { + bizarro_field->add_varint(1); + } else { + bizarro_field->add_fixed32(1); + } + } + + string data; + EXPECT_TRUE(bizarro_message.SerializeToString(&data)); + return data; + } + + const Descriptor* descriptor_; + unittest::TestAllTypes all_fields_; + string all_fields_data_; + + // An empty message that has been parsed from all_fields_data_. So, it has + // unknown fields of every type. + unittest::TestEmptyMessage empty_message_; + UnknownFieldSet* unknown_fields_; +}; + +TEST_F(UnknownFieldSetTest, Index) { + for (int i = 0; i < unknown_fields_->field_count(); i++) { + EXPECT_EQ(i, unknown_fields_->field(i).index()); + } +} + +TEST_F(UnknownFieldSetTest, FindFieldByNumber) { + // All fields of TestAllTypes should be present. Fields that are not valid + // field numbers of TestAllTypes should NOT be present. + + for (int i = 0; i < 1000; i++) { + if (descriptor_->FindFieldByNumber(i) == NULL) { + EXPECT_TRUE(unknown_fields_->FindFieldByNumber(i) == NULL); + } else { + EXPECT_TRUE(unknown_fields_->FindFieldByNumber(i) != NULL); + } + } +} + +TEST_F(UnknownFieldSetTest, Varint) { + const UnknownField* field = GetField("optional_int32"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->varint_size()); + EXPECT_EQ(all_fields_.optional_int32(), field->varint(0)); +} + +TEST_F(UnknownFieldSetTest, Fixed32) { + const UnknownField* field = GetField("optional_fixed32"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->fixed32_size()); + EXPECT_EQ(all_fields_.optional_fixed32(), field->fixed32(0)); +} + +TEST_F(UnknownFieldSetTest, Fixed64) { + const UnknownField* field = GetField("optional_fixed64"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->fixed64_size()); + EXPECT_EQ(all_fields_.optional_fixed64(), field->fixed64(0)); +} + +TEST_F(UnknownFieldSetTest, LengthDelimited) { + const UnknownField* field = GetField("optional_string"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->length_delimited_size()); + EXPECT_EQ(all_fields_.optional_string(), field->length_delimited(0)); +} + +TEST_F(UnknownFieldSetTest, Group) { + const UnknownField* field = GetField("optionalgroup"); + ASSERT_TRUE(field != NULL); + + ASSERT_EQ(1, field->group_size()); + EXPECT_EQ(1, field->group(0).field_count()); + + const UnknownField& nested_field = field->group(0).field(0); + const FieldDescriptor* nested_field_descriptor = + unittest::TestAllTypes::OptionalGroup::descriptor()->FindFieldByName("a"); + ASSERT_TRUE(nested_field_descriptor != NULL); + + EXPECT_EQ(nested_field_descriptor->number(), nested_field.number()); + EXPECT_EQ(all_fields_.optionalgroup().a(), nested_field.varint(0)); +} + +TEST_F(UnknownFieldSetTest, Serialize) { + // Check that serializing the UnknownFieldSet produces the original data + // again. + + string data; + empty_message_.SerializeToString(&data); + + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(data == all_fields_data_); +} + +TEST_F(UnknownFieldSetTest, ParseViaReflection) { + // Make sure fields are properly parsed to the UnknownFieldSet when parsing + // via reflection. + + unittest::TestEmptyMessage message; + io::ArrayInputStream raw_input(all_fields_data_.data(), + all_fields_data_.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &message)); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, SerializeViaReflection) { + // Make sure fields are properly written from the UnknownFieldSet when + // serializing via reflection. + + string data; + + { + io::StringOutputStream raw_output(&data); + io::CodedOutputStream output(&raw_output); + int size = WireFormat::ByteSize(empty_message_); + ASSERT_TRUE( + WireFormat::SerializeWithCachedSizes(empty_message_, size, &output)); + } + + // Don't use EXPECT_EQ because we don't want to dump raw binary data to + // stdout. + EXPECT_TRUE(data == all_fields_data_); +} + +TEST_F(UnknownFieldSetTest, CopyFrom) { + unittest::TestEmptyMessage message; + + message.CopyFrom(empty_message_); + + EXPECT_EQ(empty_message_.DebugString(), message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, MergeFrom) { + unittest::TestEmptyMessage source, destination; + + destination.mutable_unknown_fields()->AddField(1)->add_varint(1); + destination.mutable_unknown_fields()->AddField(3)->add_varint(2); + source.mutable_unknown_fields()->AddField(2)->add_varint(3); + source.mutable_unknown_fields()->AddField(3)->add_varint(4); + + destination.MergeFrom(source); + + EXPECT_EQ( + // Note: The ordering of fields here depends on the ordering of adds + // and merging, above. + "1: 1\n" + "3: 2\n" + "3: 4\n" + "2: 3\n", + destination.DebugString()); +} + +TEST_F(UnknownFieldSetTest, Clear) { + // Get a pointer to a contained field object. + const UnknownField* field = GetField("optional_int32"); + ASSERT_TRUE(field != NULL); + ASSERT_EQ(1, field->varint_size()); + int number = field->number(); + + // Clear the set. + empty_message_.Clear(); + EXPECT_EQ(0, unknown_fields_->field_count()); + + // If we add that field again we should get the same object. + ASSERT_EQ(field, unknown_fields_->AddField(number)); + + // But it should be cleared. + EXPECT_EQ(0, field->varint_size()); +} + +TEST_F(UnknownFieldSetTest, ParseKnownAndUnknown) { + // Test mixing known and unknown fields when parsing. + + unittest::TestEmptyMessage source; + source.mutable_unknown_fields()->AddField(123456)->add_varint(654321); + string data; + ASSERT_TRUE(source.SerializeToString(&data)); + + unittest::TestAllTypes destination; + ASSERT_TRUE(destination.ParseFromString(all_fields_data_ + data)); + + TestUtil::ExpectAllFieldsSet(destination); + ASSERT_EQ(1, destination.unknown_fields().field_count()); + ASSERT_EQ(1, destination.unknown_fields().field(0).varint_size()); + EXPECT_EQ(654321, destination.unknown_fields().field(0).varint(0)); +} + +TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknown) { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing. + + unittest::TestAllTypes all_types_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + ASSERT_TRUE(all_types_message.ParseFromString(bizarro_data)); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, WrongTypeTreatedAsUnknownViaReflection) { + // Same as WrongTypeTreatedAsUnknown but via the reflection interface. + + unittest::TestAllTypes all_types_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + io::ArrayInputStream raw_input(bizarro_data.data(), bizarro_data.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &all_types_message)); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + EXPECT_EQ(empty_message.DebugString(), all_types_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownExtensions) { + // Make sure fields are properly parsed to the UnknownFieldSet even when + // they are declared as extension numbers. + + unittest::TestEmptyMessageWithExtensions message; + ASSERT_TRUE(message.ParseFromString(all_fields_data_)); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownExtensionsReflection) { + // Same as UnknownExtensions except parsing via reflection. + + unittest::TestEmptyMessageWithExtensions message; + io::ArrayInputStream raw_input(all_fields_data_.data(), + all_fields_data_.size()); + io::CodedInputStream input(&raw_input); + ASSERT_TRUE(WireFormat::ParseAndMergePartial(&input, &message)); + + EXPECT_EQ(message.DebugString(), empty_message_.DebugString()); +} + +TEST_F(UnknownFieldSetTest, WrongExtensionTypeTreatedAsUnknown) { + // Test that fields of the wrong wire type are treated like unknown fields + // when parsing extensions. + + unittest::TestAllExtensions all_extensions_message; + unittest::TestEmptyMessage empty_message; + string bizarro_data = GetBizarroData(); + ASSERT_TRUE(all_extensions_message.ParseFromString(bizarro_data)); + ASSERT_TRUE(empty_message.ParseFromString(bizarro_data)); + + // All fields should have been interpreted as unknown, so the debug strings + // should be the same. + EXPECT_EQ(empty_message.DebugString(), all_extensions_message.DebugString()); +} + +TEST_F(UnknownFieldSetTest, UnknownEnumValue) { + using unittest::TestAllTypes; + using unittest::TestAllExtensions; + using unittest::TestEmptyMessage; + + const FieldDescriptor* singular_field = + TestAllTypes::descriptor()->FindFieldByName("optional_nested_enum"); + const FieldDescriptor* repeated_field = + TestAllTypes::descriptor()->FindFieldByName("repeated_nested_enum"); + ASSERT_TRUE(singular_field != NULL); + ASSERT_TRUE(repeated_field != NULL); + + string data; + + { + TestEmptyMessage empty_message; + UnknownFieldSet* unknown_fields = empty_message.mutable_unknown_fields(); + UnknownField* singular_unknown_field = + unknown_fields->AddField(singular_field->number()); + singular_unknown_field->add_varint(TestAllTypes::BAR); + singular_unknown_field->add_varint(5); // not valid + UnknownField* repeated_unknown_field = + unknown_fields->AddField(repeated_field->number()); + repeated_unknown_field->add_varint(TestAllTypes::FOO); + repeated_unknown_field->add_varint(4); // not valid + repeated_unknown_field->add_varint(TestAllTypes::BAZ); + repeated_unknown_field->add_varint(6); // not valid + empty_message.SerializeToString(&data); + } + + { + TestAllTypes message; + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(TestAllTypes::BAR, message.optional_nested_enum()); + ASSERT_EQ(2, message.repeated_nested_enum_size()); + EXPECT_EQ(TestAllTypes::FOO, message.repeated_nested_enum(0)); + EXPECT_EQ(TestAllTypes::BAZ, message.repeated_nested_enum(1)); + + const UnknownFieldSet& unknown_fields = message.unknown_fields(); + ASSERT_EQ(2, unknown_fields.field_count()); + + const UnknownField& singular_unknown_field = unknown_fields.field(0); + ASSERT_EQ(singular_field->number(), singular_unknown_field.number()); + ASSERT_EQ(1, singular_unknown_field.varint_size()); + EXPECT_EQ(5, singular_unknown_field.varint(0)); + + const UnknownField& repeated_unknown_field = unknown_fields.field(1); + ASSERT_EQ(repeated_field->number(), repeated_unknown_field.number()); + ASSERT_EQ(2, repeated_unknown_field.varint_size()); + EXPECT_EQ(4, repeated_unknown_field.varint(0)); + EXPECT_EQ(6, repeated_unknown_field.varint(1)); + } + + { + using unittest::optional_nested_enum_extension; + using unittest::repeated_nested_enum_extension; + + TestAllExtensions message; + ASSERT_TRUE(message.ParseFromString(data)); + EXPECT_EQ(TestAllTypes::BAR, + message.GetExtension(optional_nested_enum_extension)); + ASSERT_EQ(2, message.ExtensionSize(repeated_nested_enum_extension)); + EXPECT_EQ(TestAllTypes::FOO, + message.GetExtension(repeated_nested_enum_extension, 0)); + EXPECT_EQ(TestAllTypes::BAZ, + message.GetExtension(repeated_nested_enum_extension, 1)); + + const UnknownFieldSet& unknown_fields = message.unknown_fields(); + ASSERT_EQ(2, unknown_fields.field_count()); + + const UnknownField& singular_unknown_field = unknown_fields.field(0); + ASSERT_EQ(singular_field->number(), singular_unknown_field.number()); + ASSERT_EQ(1, singular_unknown_field.varint_size()); + EXPECT_EQ(5, singular_unknown_field.varint(0)); + + const UnknownField& repeated_unknown_field = unknown_fields.field(1); + ASSERT_EQ(repeated_field->number(), repeated_unknown_field.number()); + ASSERT_EQ(2, repeated_unknown_field.varint_size()); + EXPECT_EQ(4, repeated_unknown_field.varint(0)); + EXPECT_EQ(6, repeated_unknown_field.varint(1)); + } +} + +} // namespace +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc new file mode 100644 index 00000000..5f87c103 --- /dev/null +++ b/src/google/protobuf/wire_format.cc @@ -0,0 +1,824 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { + +namespace { + +// This function turns out to be convenient when using some macros later. +inline int GetEnumNumber(const EnumValueDescriptor* descriptor) { + return descriptor->number(); +} + +// These are the tags for the old MessageSet format, which was defined as: +// message MessageSet { +// repeated group Item = 1 { +// required int32 type_id = 2; +// required string message = 3; +// } +// } +const int kMessageSetItemStartTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(1, WireFormat::WIRETYPE_START_GROUP); +const int kMessageSetItemEndTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(1, WireFormat::WIRETYPE_END_GROUP); +const int kMessageSetTypeIdTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(2, WireFormat::WIRETYPE_VARINT); +const int kMessageSetMessageTag = + GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(3, WireFormat::WIRETYPE_LENGTH_DELIMITED); + +// Byte size of all tags of a MessageSet::Item combined. +static const int kMessageSetItemTagsSize = + io::CodedOutputStream::VarintSize32(kMessageSetItemStartTag) + + io::CodedOutputStream::VarintSize32(kMessageSetItemEndTag) + + io::CodedOutputStream::VarintSize32(kMessageSetTypeIdTag) + + io::CodedOutputStream::VarintSize32(kMessageSetMessageTag); + +} // anonymous namespace + +const WireFormat::WireType +WireFormat::kWireTypeForFieldType[FieldDescriptor::MAX_TYPE + 1] = { + static_cast(-1), // invalid + WIRETYPE_FIXED64, // TYPE_DOUBLE + WIRETYPE_FIXED32, // TYPE_FLOAT + WIRETYPE_VARINT, // TYPE_INT64 + WIRETYPE_VARINT, // TYPE_UINT64 + WIRETYPE_VARINT, // TYPE_INT32 + WIRETYPE_FIXED64, // TYPE_FIXED64 + WIRETYPE_FIXED32, // TYPE_FIXED32 + WIRETYPE_VARINT, // TYPE_BOOL + WIRETYPE_LENGTH_DELIMITED, // TYPE_STRING + WIRETYPE_START_GROUP, // TYPE_GROUP + WIRETYPE_LENGTH_DELIMITED, // TYPE_MESSAGE + WIRETYPE_LENGTH_DELIMITED, // TYPE_BYTES + WIRETYPE_VARINT, // TYPE_UINT32 + WIRETYPE_VARINT, // TYPE_ENUM + WIRETYPE_FIXED32, // TYPE_SFIXED32 + WIRETYPE_FIXED64, // TYPE_SFIXED64 + WIRETYPE_VARINT, // TYPE_SINT32 + WIRETYPE_VARINT, // TYPE_SINT64 +}; + +// =================================================================== + +bool WireFormat::SkipField(io::CodedInputStream* input, uint32 tag, + UnknownFieldSet* unknown_fields) { + UnknownField* field = (unknown_fields == NULL) ? NULL : + unknown_fields->AddField(GetTagFieldNumber(tag)); + + switch (GetTagWireType(tag)) { + case WIRETYPE_VARINT: { + uint64 value; + if (!input->ReadVarint64(&value)) return false; + if (field != NULL) field->add_varint(value); + return true; + } + case WIRETYPE_FIXED64: { + uint64 value; + if (!input->ReadLittleEndian64(&value)) return false; + if (field != NULL) field->add_fixed64(value); + return true; + } + case WIRETYPE_LENGTH_DELIMITED: { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (field == NULL) { + if (!input->Skip(length)) return false; + } else { + if (!input->ReadString(field->add_length_delimited(), length)) { + return false; + } + } + return true; + } + case WIRETYPE_START_GROUP: { + if (!input->IncrementRecursionDepth()) return false; + if (!SkipMessage(input, (field == NULL) ? NULL : field->add_group())) { + return false; + } + input->DecrementRecursionDepth(); + // Check that the ending tag matched the starting tag. + if (!input->LastTagWas( + MakeTag(GetTagFieldNumber(tag), WIRETYPE_END_GROUP))) { + return false; + } + return true; + } + case WIRETYPE_END_GROUP: { + return false; + } + case WIRETYPE_FIXED32: { + uint32 value; + if (!input->ReadLittleEndian32(&value)) return false; + if (field != NULL) field->add_fixed32(value); + return true; + } + default: { + return false; + } + } +} + +bool WireFormat::SkipMessage(io::CodedInputStream* input, + UnknownFieldSet* unknown_fields) { + while(true) { + uint32 tag = input->ReadTag(); + if (tag == 0) { + // End of input. This is a valid place to end, so return true. + return true; + } + + WireType wire_type = GetTagWireType(tag); + + if (wire_type == WIRETYPE_END_GROUP) { + // Must be the end of the message. + return true; + } + + if (!SkipField(input, tag, unknown_fields)) return false; + } +} + +bool WireFormat::SerializeUnknownFields(const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + +#define DO(EXPRESSION) if (!(EXPRESSION)) return false + for (int j = 0; j < field.varint_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_VARINT))); + DO(output->WriteVarint64(field.varint(j))); + } + for (int j = 0; j < field.fixed32_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_FIXED32))); + DO(output->WriteLittleEndian32(field.fixed32(j))); + } + for (int j = 0; j < field.fixed64_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_FIXED64))); + DO(output->WriteLittleEndian64(field.fixed64(j))); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + DO(output->WriteVarint32( + MakeTag(field.number(), WIRETYPE_LENGTH_DELIMITED))); + DO(output->WriteVarint32(field.length_delimited(j).size())); + DO(output->WriteString(field.length_delimited(j))); + } + for (int j = 0; j < field.group_size(); j++) { + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_START_GROUP))); + DO(SerializeUnknownFields(field.group(j), output)); + DO(output->WriteVarint32(MakeTag(field.number(), WIRETYPE_END_GROUP))); + } +#undef DO + } + + return true; +} + +bool WireFormat::SerializeUnknownMessageSetItems( + const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output) { + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + +#define DO(EXPRESSION) if (!(EXPRESSION)) return false + // The only unknown fields that are allowed to exist in a MessageSet are + // messages, which are length-delimited. + for (int j = 0; j < field.length_delimited_size(); j++) { + const string& data = field.length_delimited(j); + + // Start group. + DO(output->WriteVarint32(kMessageSetItemStartTag)); + + // Write type ID. + DO(output->WriteVarint32(kMessageSetTypeIdTag)); + DO(output->WriteVarint32(field.number())); + + // Write message. + DO(output->WriteVarint32(kMessageSetMessageTag)); + DO(output->WriteVarint32(data.size())); + DO(output->WriteString(data)); + + // End group. + DO(output->WriteVarint32(kMessageSetItemEndTag)); + } +#undef DO + } + + return true; +} + +int WireFormat::ComputeUnknownFieldsSize( + const UnknownFieldSet& unknown_fields) { + int size = 0; + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + + for (int j = 0; j < field.varint_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_VARINT)); + size += io::CodedOutputStream::VarintSize64(field.varint(j)); + } + for (int j = 0; j < field.fixed32_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_FIXED32)); + size += sizeof(int32); + } + for (int j = 0; j < field.fixed64_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_FIXED64)); + size += sizeof(int64); + } + for (int j = 0; j < field.length_delimited_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_LENGTH_DELIMITED)); + size += io::CodedOutputStream::VarintSize32( + field.length_delimited(j).size()); + size += field.length_delimited(j).size(); + } + for (int j = 0; j < field.group_size(); j++) { + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_START_GROUP)); + size += ComputeUnknownFieldsSize(field.group(j)); + size += io::CodedOutputStream::VarintSize32( + MakeTag(field.number(), WIRETYPE_END_GROUP)); + } + } + + return size; +} + +int WireFormat::ComputeUnknownMessageSetItemsSize( + const UnknownFieldSet& unknown_fields) { + int size = 0; + for (int i = 0; i < unknown_fields.field_count(); i++) { + const UnknownField& field = unknown_fields.field(i); + + // The only unknown fields that are allowed to exist in a MessageSet are + // messages, which are length-delimited. + for (int j = 0; j < field.length_delimited_size(); j++) { + size += kMessageSetItemTagsSize; + size += io::CodedOutputStream::VarintSize32(field.number()); + size += io::CodedOutputStream::VarintSize32( + field.length_delimited(j).size()); + size += field.length_delimited(j).size(); + } + } + + return size; +} + +// =================================================================== + +bool WireFormat::ParseAndMergePartial(io::CodedInputStream* input, + Message* message) { + const Descriptor* descriptor = message->GetDescriptor(); + const Reflection* message_reflection = message->GetReflection(); + + while(true) { + uint32 tag = input->ReadTag(); + if (tag == 0) { + // End of input. This is a valid place to end, so return true. + return true; + } + + if (GetTagWireType(tag) == WIRETYPE_END_GROUP) { + // Must be the end of the message. + return true; + } + + const FieldDescriptor* field = NULL; + + if (descriptor != NULL) { + int field_number = GetTagFieldNumber(tag); + field = descriptor->FindFieldByNumber(field_number); + + // If that failed, check if the field is an extension. + if (field == NULL && descriptor->IsExtensionNumber(field_number)) { + field = message_reflection->FindKnownExtensionByNumber(field_number); + } + + // If that failed, but we're a MessageSet, and this is the tag for a + // MessageSet item, then parse that. + if (field == NULL && + descriptor->options().message_set_wire_format() && + tag == kMessageSetItemStartTag) { + if (!ParseAndMergeMessageSetItem(input, message)) { + return false; + } + continue; // Skip ParseAndMergeField(); already taken care of. + } + } + + if (!ParseAndMergeField(tag, field, message, input)) { + return false; + } + } +} + +bool WireFormat::ParseAndMergeField( + uint32 tag, + const FieldDescriptor* field, // May be NULL for unknown + Message* message, + io::CodedInputStream* input) { + const Reflection* message_reflection = message->GetReflection(); + + if (field == NULL || + GetTagWireType(tag) != WireTypeForFieldType(field->type())) { + // We don't recognize this field. Either the field number is unknown + // or the wire type doesn't match. Put it in our unknown field set. + return SkipField(input, tag, + message_reflection->MutableUnknownFields(message)); + } + + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: { \ + CPPTYPE value; \ + if (!Read##TYPE_METHOD(input, &value)) return false; \ + if (field->is_repeated()) { \ + message_reflection->Add##CPPTYPE_METHOD(message, field, value); \ + } else { \ + message_reflection->Set##CPPTYPE_METHOD(message, field, value); \ + } \ + break; \ + } + + HANDLE_TYPE( INT32, Int32, int32, Int32) + HANDLE_TYPE( INT64, Int64, int64, Int64) + HANDLE_TYPE(SINT32, SInt32, int32, Int32) + HANDLE_TYPE(SINT64, SInt64, int64, Int64) + HANDLE_TYPE(UINT32, UInt32, uint32, UInt32) + HANDLE_TYPE(UINT64, UInt64, uint64, UInt64) + + HANDLE_TYPE( FIXED32, Fixed32, uint32, UInt32) + HANDLE_TYPE( FIXED64, Fixed64, uint64, UInt64) + HANDLE_TYPE(SFIXED32, SFixed32, int32, Int32) + HANDLE_TYPE(SFIXED64, SFixed64, int64, Int64) + + HANDLE_TYPE(FLOAT , Float , float , Float ) + HANDLE_TYPE(DOUBLE, Double, double, Double) + + HANDLE_TYPE(BOOL, Bool, bool, Bool) + + HANDLE_TYPE(STRING, String, string, String) + HANDLE_TYPE(BYTES, Bytes, string, String) + +#undef HANDLE_TYPE + + case FieldDescriptor::TYPE_ENUM: { + int value; + if (!ReadEnum(input, &value)) return false; + const EnumValueDescriptor* enum_value = + field->enum_type()->FindValueByNumber(value); + if (enum_value != NULL) { + if (field->is_repeated()) { + message_reflection->AddEnum(message, field, enum_value); + } else { + message_reflection->SetEnum(message, field, enum_value); + } + } else { + // The enum value is not one of the known values. Add it to the + // UnknownFieldSet. + int64 sign_extended_value = static_cast(value); + message_reflection->MutableUnknownFields(message) + ->AddField(GetTagFieldNumber(tag)) + ->add_varint(sign_extended_value); + } + break; + } + + + case FieldDescriptor::TYPE_GROUP: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(message, field); + } else { + sub_message = message_reflection->MutableMessage(message, field); + } + + if (!ReadGroup(GetTagFieldNumber(tag), input, sub_message)) return false; + break; + } + + case FieldDescriptor::TYPE_MESSAGE: { + Message* sub_message; + if (field->is_repeated()) { + sub_message = message_reflection->AddMessage(message, field); + } else { + sub_message = message_reflection->MutableMessage(message, field); + } + + if (!ReadMessage(input, sub_message)) return false; + break; + } + } + + return true; +} + +bool WireFormat::ParseAndMergeMessageSetItem( + io::CodedInputStream* input, + Message* message) { + const Reflection* message_reflection = message->GetReflection(); + + // This method parses a group which should contain two fields: + // required int32 type_id = 2; + // required data message = 3; + + // Once we see a type_id, we'll construct a fake tag for this extension + // which is the tag it would have had under the proto2 extensions wire + // format. + uint32 fake_tag = 0; + + // Once we see a type_id, we'll look up the FieldDescriptor for the + // extension. + const FieldDescriptor* field = NULL; + + // If we see message data before the type_id, we'll append it to this so + // we can parse it later. This will probably never happen in practice, + // as no MessageSet encoder I know of writes the message before the type ID. + // But, it's technically valid so we should allow it. + // TODO(kenton): Use a Cord instead? Do I care? + string message_data; + + while (true) { + uint32 tag = input->ReadTag(); + if (tag == 0) return false; + + switch (tag) { + case kMessageSetTypeIdTag: { + uint32 type_id; + if (!input->ReadVarint32(&type_id)) return false; + fake_tag = MakeTag(type_id, WIRETYPE_LENGTH_DELIMITED); + field = message_reflection->FindKnownExtensionByNumber(type_id); + + if (!message_data.empty()) { + // We saw some message data before the type_id. Have to parse it + // now. + io::ArrayInputStream raw_input(message_data.data(), + message_data.size()); + io::CodedInputStream sub_input(&raw_input); + if (!ParseAndMergeField(fake_tag, field, message, + &sub_input)) { + return false; + } + message_data.clear(); + } + + break; + } + + case kMessageSetMessageTag: { + if (fake_tag == 0) { + // We haven't seen a type_id yet. Append this data to message_data. + string temp; + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->ReadString(&temp, length)) return false; + message_data.append(temp); + } else { + // Already saw type_id, so we can parse this directly. + if (!ParseAndMergeField(fake_tag, field, message, input)) { + return false; + } + } + + break; + } + + case kMessageSetItemEndTag: { + return true; + } + + default: { + if (!SkipField(input, tag, NULL)) return false; + } + } + } +} + +// =================================================================== + +bool WireFormat::SerializeWithCachedSizes( + const Message& message, + int size, io::CodedOutputStream* output) { + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* message_reflection = message.GetReflection(); + int expected_endpoint = output->ByteCount() + size; + + vector fields; + message_reflection->ListFields(message, &fields); + for (int i = 0; i < fields.size(); i++) { + if (!SerializeFieldWithCachedSizes(fields[i], message, output)) { + return false; + } + } + + if (descriptor->options().message_set_wire_format()) { + if (!SerializeUnknownMessageSetItems( + message_reflection->GetUnknownFields(message), output)) { + return false; + } + } else { + if (!SerializeUnknownFields( + message_reflection->GetUnknownFields(message), output)) { + return false; + } + } + + GOOGLE_CHECK_EQ(output->ByteCount(), expected_endpoint) + << ": Protocol message serialized to a size different from what was " + "originally expected. Perhaps it was modified by another thread " + "during serialization?"; + + return true; +} + +bool WireFormat::SerializeFieldWithCachedSizes( + const FieldDescriptor* field, + const Message& message, + io::CodedOutputStream* output) { + const Reflection* message_reflection = message.GetReflection(); + + if (field->is_extension() && + field->containing_type()->options().message_set_wire_format() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !field->is_repeated()) { + return SerializeMessageSetItemWithCachedSizes( + field, message, output); + } + + int count = 0; + + if (field->is_repeated()) { + count = message_reflection->FieldSize(message, field); + } else if (message_reflection->HasField(message, field)) { + count = 1; + } + + for (int j = 0; j < count; j++) { + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + if (!Write##TYPE_METHOD( \ + field->number(), \ + field->is_repeated() ? \ + message_reflection->GetRepeated##CPPTYPE_METHOD( \ + message, field, j) : \ + message_reflection->Get##CPPTYPE_METHOD(message, field), \ + output)) { \ + return false; \ + } \ + break; + + HANDLE_TYPE( INT32, Int32, Int32) + HANDLE_TYPE( INT64, Int64, Int64) + HANDLE_TYPE(SINT32, SInt32, Int32) + HANDLE_TYPE(SINT64, SInt64, Int64) + HANDLE_TYPE(UINT32, UInt32, UInt32) + HANDLE_TYPE(UINT64, UInt64, UInt64) + + HANDLE_TYPE( FIXED32, Fixed32, UInt32) + HANDLE_TYPE( FIXED64, Fixed64, UInt64) + HANDLE_TYPE(SFIXED32, SFixed32, Int32) + HANDLE_TYPE(SFIXED64, SFixed64, Int64) + + HANDLE_TYPE(FLOAT , Float , Float ) + HANDLE_TYPE(DOUBLE, Double, Double) + + HANDLE_TYPE(BOOL, Bool, Bool) + + HANDLE_TYPE(GROUP , Group , Message) + HANDLE_TYPE(MESSAGE, Message, Message) +#undef HANDLE_TYPE + + case FieldDescriptor::TYPE_ENUM: { + const EnumValueDescriptor* value = field->is_repeated() ? + message_reflection->GetRepeatedEnum(message, field, j) : + message_reflection->GetEnum(message, field); + if (!WriteEnum(field->number(), value->number(), output)) return false; + break; + } + + // Handle strings separately so that we can get string references + // instead of copying. + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: { + string scratch; + const string& value = field->is_repeated() ? + message_reflection->GetRepeatedStringReference( + message, field, j, &scratch) : + message_reflection->GetStringReference(message, field, &scratch); + if (!WriteString(field->number(), value, output)) return false; + break; + } + } + } + + return true; +} + +bool WireFormat::SerializeMessageSetItemWithCachedSizes( + const FieldDescriptor* field, + const Message& message, + io::CodedOutputStream* output) { + const Reflection* message_reflection = message.GetReflection(); + + // Start group. + if (!output->WriteVarint32(kMessageSetItemStartTag)) return false; + + // Write type ID. + if (!output->WriteVarint32(kMessageSetTypeIdTag)) return false; + if (!output->WriteVarint32(field->number())) return false; + + // Write message. + if (!output->WriteVarint32(kMessageSetMessageTag)) return false; + + const Message& sub_message = message_reflection->GetMessage(message, field); + if (!output->WriteVarint32(sub_message.GetCachedSize())) return false; + if (!sub_message.SerializeWithCachedSizes(output)) return false; + + // End group. + if (!output->WriteVarint32(kMessageSetItemEndTag)) return false; + + return true; +} + +// =================================================================== + +int WireFormat::ByteSize(const Message& message) { + const Descriptor* descriptor = message.GetDescriptor(); + const Reflection* message_reflection = message.GetReflection(); + + int our_size = 0; + + vector fields; + message_reflection->ListFields(message, &fields); + for (int i = 0; i < fields.size(); i++) { + our_size += FieldByteSize(fields[i], message); + } + + if (descriptor->options().message_set_wire_format()) { + our_size += ComputeUnknownMessageSetItemsSize( + message_reflection->GetUnknownFields(message)); + } else { + our_size += ComputeUnknownFieldsSize( + message_reflection->GetUnknownFields(message)); + } + + return our_size; +} + +int WireFormat::FieldByteSize( + const FieldDescriptor* field, + const Message& message) { + const Reflection* message_reflection = message.GetReflection(); + + if (field->is_extension() && + field->containing_type()->options().message_set_wire_format() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && + !field->is_repeated()) { + return MessageSetItemByteSize(field, message); + } + + int our_size = 0; + + int count = 0; + + if (field->is_repeated()) { + count = message_reflection->FieldSize(message, field); + } else if (message_reflection->HasField(message, field)) { + count = 1; + } + + our_size += count * TagSize(field->number(), field->type()); + + switch (field->type()) { +#define HANDLE_TYPE(TYPE, TYPE_METHOD, CPPTYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + if (field->is_repeated()) { \ + for (int j = 0; j < count; j++) { \ + our_size += TYPE_METHOD##Size( \ + message_reflection->GetRepeated##CPPTYPE_METHOD( \ + message, field, j)); \ + } \ + } else { \ + our_size += TYPE_METHOD##Size( \ + message_reflection->Get##CPPTYPE_METHOD(message, field)); \ + } \ + break; + +#define HANDLE_FIXED_TYPE(TYPE, TYPE_METHOD) \ + case FieldDescriptor::TYPE_##TYPE: \ + our_size += count * k##TYPE_METHOD##Size; \ + break; + + HANDLE_TYPE( INT32, Int32, Int32) + HANDLE_TYPE( INT64, Int64, Int64) + HANDLE_TYPE(SINT32, SInt32, Int32) + HANDLE_TYPE(SINT64, SInt64, Int64) + HANDLE_TYPE(UINT32, UInt32, UInt32) + HANDLE_TYPE(UINT64, UInt64, UInt64) + + HANDLE_FIXED_TYPE( FIXED32, Fixed32) + HANDLE_FIXED_TYPE( FIXED64, Fixed64) + HANDLE_FIXED_TYPE(SFIXED32, SFixed32) + HANDLE_FIXED_TYPE(SFIXED64, SFixed64) + + HANDLE_FIXED_TYPE(FLOAT , Float ) + HANDLE_FIXED_TYPE(DOUBLE, Double) + + HANDLE_FIXED_TYPE(BOOL, Bool) + + HANDLE_TYPE(GROUP , Group , Message) + HANDLE_TYPE(MESSAGE, Message, Message) +#undef HANDLE_TYPE +#undef HANDLE_FIXED_TYPE + + case FieldDescriptor::TYPE_ENUM: { + if (field->is_repeated()) { + for (int j = 0; j < count; j++) { + our_size += EnumSize( + message_reflection->GetRepeatedEnum(message, field, j)->number()); + } + } else { + our_size += EnumSize( + message_reflection->GetEnum(message, field)->number()); + } + break; + } + + // Handle strings separately so that we can get string references + // instead of copying. + case FieldDescriptor::TYPE_STRING: + case FieldDescriptor::TYPE_BYTES: { + for (int j = 0; j < count; j++) { + string scratch; + const string& value = field->is_repeated() ? + message_reflection->GetRepeatedStringReference( + message, field, j, &scratch) : + message_reflection->GetStringReference(message, field, &scratch); + our_size += StringSize(value); + } + break; + } + } + + return our_size; +} + +int WireFormat::MessageSetItemByteSize( + const FieldDescriptor* field, + const Message& message) { + const Reflection* message_reflection = message.GetReflection(); + + int our_size = kMessageSetItemTagsSize; + + // type_id + our_size += io::CodedOutputStream::VarintSize32(field->number()); + + // message + const Message& sub_message = message_reflection->GetMessage(message, field); + int message_size = sub_message.ByteSize(); + + our_size += io::CodedOutputStream::VarintSize32(message_size); + our_size += message_size; + + return our_size; +} + +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/wire_format.h b/src/google/protobuf/wire_format.h new file mode 100644 index 00000000..7e8ab5db --- /dev/null +++ b/src/google/protobuf/wire_format.h @@ -0,0 +1,443 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// atenasio@google.com (Chris Atenasio) (ZigZag transform) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This header is logically internal, but is made public because it is used +// from protocol-compiler-generated code, which may reside in other components. + +#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_H__ +#define GOOGLE_PROTOBUF_WIRE_FORMAT_H__ + +#include +#include +#include + +namespace google { + +namespace protobuf { + namespace io { + class CodedInputStream; // coded_stream.h + class CodedOutputStream; // coded_stream.h + } + class UnknownFieldSet; // unknown_field_set.h +} + +namespace protobuf { +namespace internal { + +// This class is for internal use by the protocol buffer library and by +// protocol-complier-generated message classes. It must not be called +// directly by clients. +// +// This class contains helpers for implementing the binary protocol buffer +// wire format. These helpers are called primarily by generated code. The +// class also contains reflection-based implementations of the wire format. +// +// This class is really a namespace that contains only static methods. +class LIBPROTOBUF_EXPORT WireFormat { + public: + // These procedures can be used to implement the methods of Message which + // handle parsing and serialization of the protocol buffer wire format + // using only the Reflection interface. When you ask the protocol + // compiler to optimize for code size rather than speed, it will implement + // those methods in terms of these procedures. Of course, these are much + // slower than the specialized implementations which the protocol compiler + // generates when told to optimize for speed. + + // Read a message in protocol buffer wire format. + // + // This procedure reads either to the end of the input stream or through + // a WIRETYPE_END_GROUP tag ending the message, whichever comes first. + // It returns false if the input is invalid. + // + // Required fields are NOT checked by this method. You must call + // IsInitialized() on the resulting message yourself. + static bool ParseAndMergePartial(io::CodedInputStream* input, + Message* message); + + // Serialize a message in protocol buffer wire format. + // + // Any embedded messages within the message must have their correct sizes + // cached. However, the top-level message need not; its size is passed as + // a parameter to this procedure. + // + // These return false iff the underlying stream returns a write error. + static bool SerializeWithCachedSizes( + const Message& message, + int size, io::CodedOutputStream* output); + + // Implements Message::ByteSize() via reflection. WARNING: The result + // of this method is *not* cached anywhere. However, all embedded messages + // will have their ByteSize() methods called, so their sizes will be cached. + // Therefore, calling this method is sufficient to allow you to call + // WireFormat::SerializeWithCachedSizes() on the same object. + static int ByteSize(const Message& message); + + // ----------------------------------------------------------------- + // Helpers for dealing with unknown fields + + // Skips a field value of the given WireType. The input should start + // positioned immediately after the tag. If unknown_fields is non-NULL, + // the contents of the field will be added to it. + static bool SkipField(io::CodedInputStream* input, uint32 tag, + UnknownFieldSet* unknown_fields); + + // Reads and ignores a message from the input. If unknown_fields is non-NULL, + // the contents will be added to it. + static bool SkipMessage(io::CodedInputStream* input, + UnknownFieldSet* unknown_fields); + + // Write the contents of an UnknownFieldSet to the output. + static bool SerializeUnknownFields(const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output); + + // Same thing except for messages that have the message_set_wire_format + // option. + static bool SerializeUnknownMessageSetItems( + const UnknownFieldSet& unknown_fields, + io::CodedOutputStream* output); + + // Compute the size of the UnknownFieldSet on the wire. + static int ComputeUnknownFieldsSize(const UnknownFieldSet& unknown_fields); + + // Same thing except for messages that have the message_set_wire_format + // option. + static int ComputeUnknownMessageSetItemsSize( + const UnknownFieldSet& unknown_fields); + + // ----------------------------------------------------------------- + // Helper constants and functions related to the format. These are + // mostly meant for internal and generated code to use. + + // The wire format is composed of a sequence of tag/value pairs, each + // of which contains the value of one field (or one element of a repeated + // field). Each tag is encoded as a varint. The lower bits of the tag + // identify its wire type, which specifies the format of the data to follow. + // The rest of the bits contain the field number. Each type of field (as + // declared by FieldDescriptor::Type, in descriptor.h) maps to one of + // these wire types. Immediately following each tag is the field's value, + // encoded in the format specified by the wire type. Because the tag + // identifies the encoding of this data, it is possible to skip + // unrecognized fields for forwards compatibility. + + enum WireType { + WIRETYPE_VARINT = 0, + WIRETYPE_FIXED64 = 1, + WIRETYPE_LENGTH_DELIMITED = 2, + WIRETYPE_START_GROUP = 3, + WIRETYPE_END_GROUP = 4, + WIRETYPE_FIXED32 = 5, + }; + + static inline WireType WireTypeForFieldType(FieldDescriptor::Type type) { + return kWireTypeForFieldType[type]; + } + + // Number of bits in a tag which identify the wire type. + static const int kTagTypeBits = 3; + // Mask for those bits. + static const uint32 kTagTypeMask = (1 << kTagTypeBits) - 1; + + // Helper functions for encoding and decoding tags. (Inlined below.) + static uint32 MakeTag(const FieldDescriptor* field); + static uint32 MakeTag(int field_number, WireType type); + static WireType GetTagWireType(uint32 tag); + static int GetTagFieldNumber(uint32 tag); + + // Helper functions for converting between floats/doubles and IEEE-754 + // uint32s/uint64s so that they can be written. (Assumes your platform + // uses IEEE-754 floats.) + static uint32 EncodeFloat(float value); + static float DecodeFloat(uint32 value); + static uint64 EncodeDouble(double value); + static double DecodeDouble(uint64 value); + + // Helper functions for mapping signed integers to unsigned integers in + // such a way that numbers with small magnitudes will encode to smaller + // varints. If you simply static_cast a negative number to an unsigned + // number and varint-encode it, it will always take 10 bytes, defeating + // the purpose of varint. So, for the "sint32" and "sint64" field types, + // we ZigZag-encode the values. + static uint32 ZigZagEncode32(int32 n); + static int32 ZigZagDecode32(uint32 n); + static uint64 ZigZagEncode64(int64 n); + static int64 ZigZagDecode64(uint64 n); + + // Parse a single field. The input should start out positioned immidately + // after the tag. + static bool ParseAndMergeField( + uint32 tag, + const FieldDescriptor* field, // May be NULL for unknown + Message* message, + io::CodedInputStream* input); + + // Serialize a single field. + static bool SerializeFieldWithCachedSizes( + const FieldDescriptor* field, // Cannot be NULL + const Message& message, + io::CodedOutputStream* output); + + // Compute size of a single field. If the field is a message type, this + // will call ByteSize() for the embedded message, insuring that it caches + // its size. + static int FieldByteSize( + const FieldDescriptor* field, // Cannot be NULL + const Message& message); + + // ================================================================= + // Methods for reading/writing individual field. The implementations + // of these methods are defined in wire_format_inl.h; you must #include + // that file to use these. + +// Avoid ugly line wrapping +#define input io::CodedInputStream* input +#define output io::CodedOutputStream* output +#define field_number int field_number +#define INL GOOGLE_ATTRIBUTE_ALWAYS_INLINE + + // Read fields, not including tags. The assumption is that you already + // read the tag to determine what field to read. + static inline bool ReadInt32 (input, int32* value); + static inline bool ReadInt64 (input, int64* value); + static inline bool ReadUInt32 (input, uint32* value); + static inline bool ReadUInt64 (input, uint64* value); + static inline bool ReadSInt32 (input, int32* value); + static inline bool ReadSInt64 (input, int64* value); + static inline bool ReadFixed32 (input, uint32* value); + static inline bool ReadFixed64 (input, uint64* value); + static inline bool ReadSFixed32(input, int32* value); + static inline bool ReadSFixed64(input, int64* value); + static inline bool ReadFloat (input, float* value); + static inline bool ReadDouble (input, double* value); + static inline bool ReadBool (input, bool* value); + static inline bool ReadEnum (input, int* value); + + static inline bool ReadString(input, string* value); + static inline bool ReadBytes (input, string* value); + + static inline bool ReadGroup (field_number, input, Message* value); + static inline bool ReadMessage(input, Message* value); + + // Like above, but de-virtualize the call to MergePartialFromCodedStream(). + // The pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override MergePartialFromCodedStream()). + template + static inline bool ReadGroupNoVirtual(field_number, input, + MessageType* value); + template + static inline bool ReadMessageNoVirtual(input, MessageType* value); + + // Write a tag. The Write*() functions automatically include the tag, so + // normally there's no need to call this. + static inline bool WriteTag(field_number, WireType type, output) INL; + + // Write fields, including tags. + static inline bool WriteInt32 (field_number, int32 value, output) INL; + static inline bool WriteInt64 (field_number, int64 value, output) INL; + static inline bool WriteUInt32 (field_number, uint32 value, output) INL; + static inline bool WriteUInt64 (field_number, uint64 value, output) INL; + static inline bool WriteSInt32 (field_number, int32 value, output) INL; + static inline bool WriteSInt64 (field_number, int64 value, output) INL; + static inline bool WriteFixed32 (field_number, uint32 value, output) INL; + static inline bool WriteFixed64 (field_number, uint64 value, output) INL; + static inline bool WriteSFixed32(field_number, int32 value, output) INL; + static inline bool WriteSFixed64(field_number, int64 value, output) INL; + static inline bool WriteFloat (field_number, float value, output) INL; + static inline bool WriteDouble (field_number, double value, output) INL; + static inline bool WriteBool (field_number, bool value, output) INL; + static inline bool WriteEnum (field_number, int value, output) INL; + + static inline bool WriteString(field_number, const string& value, output) INL; + static inline bool WriteBytes (field_number, const string& value, output) INL; + + static inline bool WriteGroup(field_number, const Message& value, output) INL; + static inline bool WriteMessage( + field_number, const Message& value, output) INL; + + // Like above, but de-virtualize the call to SerializeWithCachedSizes(). The + // pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override SerializeWithCachedSizes()). + template + static inline bool WriteGroupNoVirtual( + field_number, const MessageType& value, output) INL; + template + static inline bool WriteMessageNoVirtual( + field_number, const MessageType& value, output) INL; + + // Compute the byte size of a tag. For groups, this includes both the start + // and end tags. + static inline int TagSize(field_number, FieldDescriptor::Type type); + + // Compute the byte size of a field. The XxSize() functions do NOT include + // the tag, so you must also call TagSize(). (This is because, for repeated + // fields, you should only call TagSize() once and multiply it by the element + // count, but you may have to call XxSize() for each individual element.) + static inline int Int32Size ( int32 value); + static inline int Int64Size ( int64 value); + static inline int UInt32Size (uint32 value); + static inline int UInt64Size (uint64 value); + static inline int SInt32Size ( int32 value); + static inline int SInt64Size ( int64 value); + static inline int EnumSize ( int value); + + // These types always have the same size. + static const int kFixed32Size = 4; + static const int kFixed64Size = 8; + static const int kSFixed32Size = 4; + static const int kSFixed64Size = 8; + static const int kFloatSize = 4; + static const int kDoubleSize = 8; + static const int kBoolSize = 1; + + static inline int StringSize(const string& value); + static inline int BytesSize (const string& value); + + static inline int GroupSize (const Message& value); + static inline int MessageSize(const Message& value); + + // Like above, but de-virtualize the call to ByteSize(). The + // pointer must point at an instance of MessageType, *not* a subclass (or + // the subclass must not override ByteSize()). + template + static inline int GroupSizeNoVirtual (const MessageType& value); + template + static inline int MessageSizeNoVirtual(const MessageType& value); + +#undef input +#undef output +#undef field_number +#undef INL + + private: + static const WireType kWireTypeForFieldType[]; + + // Parse/serialize a MessageSet::Item group. Used with messages that use + // opion message_set_wire_format = true. + static bool ParseAndMergeMessageSetItem( + io::CodedInputStream* input, + Message* message); + static bool SerializeMessageSetItemWithCachedSizes( + const FieldDescriptor* field, + const Message& message, + io::CodedOutputStream* output); + static int MessageSetItemByteSize( + const FieldDescriptor* field, + const Message& message); + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(WireFormat); +}; + +// inline methods ==================================================== + +// This macro does the same thing as WireFormat::MakeTag(), but the +// result is usable as a compile-time constant, which makes it usable +// as a switch case or a template input. WireFormat::MakeTag() is more +// type-safe, though, so prefer it if possible. +#define GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(FIELD_NUMBER, TYPE) \ + static_cast( \ + ((FIELD_NUMBER) << ::google::protobuf::internal::WireFormat::kTagTypeBits) | (TYPE)) + +inline uint32 WireFormat::MakeTag(const FieldDescriptor* field) { + return MakeTag(field->number(), WireTypeForFieldType(field->type())); +} + +inline uint32 WireFormat::MakeTag(int field_number, WireType type) { + return GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG(field_number, type); +} + +inline WireFormat::WireType WireFormat::GetTagWireType(uint32 tag) { + return static_cast(tag & kTagTypeMask); +} + +inline int WireFormat::GetTagFieldNumber(uint32 tag) { + return static_cast(tag >> kTagTypeBits); +} + +inline uint32 WireFormat::EncodeFloat(float value) { + union {float f; uint32 i;}; + f = value; + return i; +} + +inline float WireFormat::DecodeFloat(uint32 value) { + union {float f; uint32 i;}; + i = value; + return f; +} + +inline uint64 WireFormat::EncodeDouble(double value) { + union {double f; uint64 i;}; + f = value; + return i; +} + +inline double WireFormat::DecodeDouble(uint64 value) { + union {double f; uint64 i;}; + i = value; + return f; +} + +// ZigZag Transform: Encodes signed integers so that they can be +// effectively used with varint encoding. +// +// varint operates on unsigned integers, encoding smaller numbers into +// fewer bytes. If you try to use it on a signed integer, it will treat +// this number as a very large unsigned integer, which means that even +// small signed numbers like -1 will take the maximum number of bytes +// (10) to encode. ZigZagEncode() maps signed integers to unsigned +// in such a way that those with a small absolute value will have smaller +// encoded values, making them appropriate for encoding using varint. +// +// int32 -> uint32 +// ------------------------- +// 0 -> 0 +// -1 -> 1 +// 1 -> 2 +// -2 -> 3 +// ... -> ... +// 2147483647 -> 4294967294 +// -2147483648 -> 4294967295 +// +// >> encode >> +// << decode << + +inline uint32 WireFormat::ZigZagEncode32(int32 n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); +} + +inline int32 WireFormat::ZigZagDecode32(uint32 n) { + return (n >> 1) ^ -static_cast(n & 1); +} + +inline uint64 WireFormat::ZigZagEncode64(int64 n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); +} + +inline int64 WireFormat::ZigZagDecode64(uint64 n) { + return (n >> 1) ^ -static_cast(n & 1); +} + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_WIRE_FORMAT_H__ diff --git a/src/google/protobuf/wire_format_inl.h b/src/google/protobuf/wire_format_inl.h new file mode 100644 index 00000000..d8cdd8d6 --- /dev/null +++ b/src/google/protobuf/wire_format_inl.h @@ -0,0 +1,371 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ +#define GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ + +#include +#include +#include + + +namespace google { +namespace protobuf { +namespace internal { + +inline bool WireFormat::ReadInt32(io::CodedInputStream* input, int32* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadInt64(io::CodedInputStream* input, int64* value) { + uint64 temp; + if (!input->ReadVarint64(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadUInt32(io::CodedInputStream* input, uint32* value) { + return input->ReadVarint32(value); +} +inline bool WireFormat::ReadUInt64(io::CodedInputStream* input, uint64* value) { + return input->ReadVarint64(value); +} +inline bool WireFormat::ReadSInt32(io::CodedInputStream* input, int32* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = ZigZagDecode32(temp); + return true; +} +inline bool WireFormat::ReadSInt64(io::CodedInputStream* input, int64* value) { + uint64 temp; + if (!input->ReadVarint64(&temp)) return false; + *value = ZigZagDecode64(temp); + return true; +} +inline bool WireFormat::ReadFixed32(io::CodedInputStream* input, + uint32* value) { + return input->ReadLittleEndian32(value); +} +inline bool WireFormat::ReadFixed64(io::CodedInputStream* input, + uint64* value) { + return input->ReadLittleEndian64(value); +} +inline bool WireFormat::ReadSFixed32(io::CodedInputStream* input, + int32* value) { + uint32 temp; + if (!input->ReadLittleEndian32(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadSFixed64(io::CodedInputStream* input, + int64* value) { + uint64 temp; + if (!input->ReadLittleEndian64(&temp)) return false; + *value = static_cast(temp); + return true; +} +inline bool WireFormat::ReadFloat(io::CodedInputStream* input, float* value) { + uint32 temp; + if (!input->ReadLittleEndian32(&temp)) return false; + *value = DecodeFloat(temp); + return true; +} +inline bool WireFormat::ReadDouble(io::CodedInputStream* input, double* value) { + uint64 temp; + if (!input->ReadLittleEndian64(&temp)) return false; + *value = DecodeDouble(temp); + return true; +} +inline bool WireFormat::ReadBool(io::CodedInputStream* input, bool* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = temp != 0; + return true; +} +inline bool WireFormat::ReadEnum(io::CodedInputStream* input, int* value) { + uint32 temp; + if (!input->ReadVarint32(&temp)) return false; + *value = static_cast(temp); + return true; +} + +inline bool WireFormat::ReadString(io::CodedInputStream* input, string* value) { + // WARNING: In wire_format.cc, both strings and bytes are handled by + // ReadString() to avoid code duplication. If the implementations become + // different, you will need to update that usage. + uint32 length; + if (!input->ReadVarint32(&length)) return false; + return input->ReadString(value, length); +} +inline bool WireFormat::ReadBytes(io::CodedInputStream* input, string* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + return input->ReadString(value, length); +} + + +inline bool WireFormat::ReadGroup(int field_number, io::CodedInputStream* input, + Message* value) { + if (!input->IncrementRecursionDepth()) return false; + if (!value->MergePartialFromCodedStream(input)) return false; + input->DecrementRecursionDepth(); + // Make sure the last thing read was an end tag for this group. + if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { + return false; + } + return true; +} +inline bool WireFormat::ReadMessage(io::CodedInputStream* input, Message* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->IncrementRecursionDepth()) return false; + io::CodedInputStream::Limit limit = input->PushLimit(length); + if (!value->MergePartialFromCodedStream(input)) return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + if (!input->ConsumedEntireMessage()) return false; + input->PopLimit(limit); + input->DecrementRecursionDepth(); + return true; +} + +template +inline bool WireFormat::ReadGroupNoVirtual(int field_number, + io::CodedInputStream* input, + MessageType* value) { + if (!input->IncrementRecursionDepth()) return false; + if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + input->DecrementRecursionDepth(); + // Make sure the last thing read was an end tag for this group. + if (!input->LastTagWas(MakeTag(field_number, WIRETYPE_END_GROUP))) { + return false; + } + return true; +} +template +inline bool WireFormat::ReadMessageNoVirtual(io::CodedInputStream* input, + MessageType* value) { + uint32 length; + if (!input->ReadVarint32(&length)) return false; + if (!input->IncrementRecursionDepth()) return false; + io::CodedInputStream::Limit limit = input->PushLimit(length); + if (!value->MessageType::MergePartialFromCodedStream(input)) return false; + // Make sure that parsing stopped when the limit was hit, not at an endgroup + // tag. + if (!input->ConsumedEntireMessage()) return false; + input->PopLimit(limit); + input->DecrementRecursionDepth(); + return true; +} + +// =================================================================== + +inline bool WireFormat::WriteTag(int field_number, WireType type, + io::CodedOutputStream* output) { + return output->WriteTag(MakeTag(field_number, type)); +} + +inline bool WireFormat::WriteInt32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32SignExtended(value); +} +inline bool WireFormat::WriteInt64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(static_cast(value)); +} +inline bool WireFormat::WriteUInt32(int field_number, uint32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(value); +} +inline bool WireFormat::WriteUInt64(int field_number, uint64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(value); +} +inline bool WireFormat::WriteSInt32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(ZigZagEncode32(value)); +} +inline bool WireFormat::WriteSInt64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint64(ZigZagEncode64(value)); +} +inline bool WireFormat::WriteFixed32(int field_number, uint32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(value); +} +inline bool WireFormat::WriteFixed64(int field_number, uint64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(value); +} +inline bool WireFormat::WriteSFixed32(int field_number, int32 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(static_cast(value)); +} +inline bool WireFormat::WriteSFixed64(int field_number, int64 value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(static_cast(value)); +} +inline bool WireFormat::WriteFloat(int field_number, float value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED32, output) && + output->WriteLittleEndian32(EncodeFloat(value)); +} +inline bool WireFormat::WriteDouble(int field_number, double value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_FIXED64, output) && + output->WriteLittleEndian64(EncodeDouble(value)); +} +inline bool WireFormat::WriteBool(int field_number, bool value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32(value ? 1 : 0); +} +inline bool WireFormat::WriteEnum(int field_number, int value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_VARINT, output) && + output->WriteVarint32SignExtended(value); +} + +inline bool WireFormat::WriteString(int field_number, const string& value, + io::CodedOutputStream* output) { + // WARNING: In wire_format.cc, both strings and bytes are handled by + // WriteString() to avoid code duplication. If the implementations become + // different, you will need to update that usage. + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.size()) && + output->WriteString(value); +} +inline bool WireFormat::WriteBytes(int field_number, const string& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.size()) && + output->WriteString(value); +} + + +inline bool WireFormat::WriteGroup(int field_number, const Message& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_START_GROUP, output) && + value.SerializeWithCachedSizes(output) && + WriteTag(field_number, WIRETYPE_END_GROUP, output); +} +inline bool WireFormat::WriteMessage(int field_number, const Message& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.GetCachedSize()) && + value.SerializeWithCachedSizes(output); +} + +template +inline bool WireFormat::WriteGroupNoVirtual( + int field_number, const MessageType& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_START_GROUP, output) && + value.MessageType::SerializeWithCachedSizes(output) && + WriteTag(field_number, WIRETYPE_END_GROUP, output); +} +template +inline bool WireFormat::WriteMessageNoVirtual( + int field_number, const MessageType& value, + io::CodedOutputStream* output) { + return WriteTag(field_number, WIRETYPE_LENGTH_DELIMITED, output) && + output->WriteVarint32(value.MessageType::GetCachedSize()) && + value.MessageType::SerializeWithCachedSizes(output); +} + +// =================================================================== + +inline int WireFormat::TagSize(int field_number, FieldDescriptor::Type type) { + int result = io::CodedOutputStream::VarintSize32( + field_number << kTagTypeBits); + if (type == FieldDescriptor::TYPE_GROUP) { + // Groups have both a start and an end tag. + return result * 2; + } else { + return result; + } +} + +inline int WireFormat::Int32Size(int32 value) { + return io::CodedOutputStream::VarintSize32SignExtended(value); +} +inline int WireFormat::Int64Size(int64 value) { + return io::CodedOutputStream::VarintSize64(static_cast(value)); +} +inline int WireFormat::UInt32Size(uint32 value) { + return io::CodedOutputStream::VarintSize32(value); +} +inline int WireFormat::UInt64Size(uint64 value) { + return io::CodedOutputStream::VarintSize64(value); +} +inline int WireFormat::SInt32Size(int32 value) { + return io::CodedOutputStream::VarintSize32(ZigZagEncode32(value)); +} +inline int WireFormat::SInt64Size(int64 value) { + return io::CodedOutputStream::VarintSize64(ZigZagEncode64(value)); +} +inline int WireFormat::EnumSize(int value) { + return io::CodedOutputStream::VarintSize32SignExtended(value); +} + +inline int WireFormat::StringSize(const string& value) { + return io::CodedOutputStream::VarintSize32(value.size()) + + value.size(); +} +inline int WireFormat::BytesSize(const string& value) { + return io::CodedOutputStream::VarintSize32(value.size()) + + value.size(); +} + + +inline int WireFormat::GroupSize(const Message& value) { + return value.ByteSize(); +} +inline int WireFormat::MessageSize(const Message& value) { + int size = value.ByteSize(); + return io::CodedOutputStream::VarintSize32(size) + size; +} + +template +inline int WireFormat::GroupSizeNoVirtual(const MessageType& value) { + return value.MessageType::ByteSize(); +} +template +inline int WireFormat::MessageSizeNoVirtual(const MessageType& value) { + int size = value.MessageType::ByteSize(); + return io::CodedOutputStream::VarintSize32(size) + size; +} + +} // namespace internal +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_WIRE_FORMAT_INL_H__ diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc new file mode 100644 index 00000000..f5953bea --- /dev/null +++ b/src/google/protobuf/wire_format_unittest.cc @@ -0,0 +1,533 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace google { +namespace protobuf { +namespace internal { +namespace { + +TEST(WireFormatTest, MaxFieldNumber) { + // Make sure the max field number constant is accurate. + EXPECT_EQ((1 << (32 - WireFormat::kTagTypeBits)) - 1, + FieldDescriptor::kMaxNumber); +} + +TEST(WireFormatTest, Parse) { + unittest::TestAllTypes source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetAllFields(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(&input, &dest); + + // Check. + TestUtil::ExpectAllFieldsSet(dest); +} + +TEST(WireFormatTest, ParseExtensions) { + unittest::TestAllExtensions source, dest; + string data; + + // Serialize using the generated code. + TestUtil::SetAllExtensions(&source); + source.SerializeToString(&data); + + // Parse using WireFormat. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + WireFormat::ParseAndMergePartial(&input, &dest); + + // Check. + TestUtil::ExpectAllExtensionsSet(dest); +} + +TEST(WireFormatTest, ByteSize) { + unittest::TestAllTypes message; + TestUtil::SetAllFields(&message); + + EXPECT_EQ(message.ByteSize(), WireFormat::ByteSize(message)); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(message)); +} + +TEST(WireFormatTest, ByteSizeExtensions) { + unittest::TestAllExtensions message; + TestUtil::SetAllExtensions(&message); + + EXPECT_EQ(message.ByteSize(), + WireFormat::ByteSize(message)); + message.Clear(); + EXPECT_EQ(0, message.ByteSize()); + EXPECT_EQ(0, WireFormat::ByteSize(message)); +} + +TEST(WireFormatTest, Serialize) { + unittest::TestAllTypes message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllFields(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes(message, size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); +} + +TEST(WireFormatTest, SerializeExtensions) { + unittest::TestAllExtensions message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllExtensions(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes(message, size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); +} + +TEST(WireFormatTest, SerializeFieldsAndExtensions) { + unittest::TestFieldOrderings message; + string generated_data; + string dynamic_data; + + TestUtil::SetAllFieldsAndExtensions(&message); + int size = message.ByteSize(); + + // Serialize using the generated code. + { + io::StringOutputStream raw_output(&generated_data); + io::CodedOutputStream output(&raw_output); + message.SerializeWithCachedSizes(&output); + } + + // Serialize using WireFormat. + { + io::StringOutputStream raw_output(&dynamic_data); + io::CodedOutputStream output(&raw_output); + WireFormat::SerializeWithCachedSizes(message, size, &output); + } + + // Should be the same. + // Don't use EXPECT_EQ here because we're comparing raw binary data and + // we really don't want it dumped to stdout on failure. + EXPECT_TRUE(dynamic_data == generated_data); + + // Should output in canonical order. + TestUtil::ExpectAllFieldsAndExtensionsInOrder(dynamic_data); + TestUtil::ExpectAllFieldsAndExtensionsInOrder(generated_data); +} + +const int kUnknownTypeId = 1550055; + +TEST(WireFormatTest, SerializeMessageSet) { + // Set up a TestMessageSet with two known messages and an unknown one. + unittest::TestMessageSet message_set; + message_set.MutableExtension( + unittest::TestMessageSetExtension1::message_set_extension)->set_i(123); + message_set.MutableExtension( + unittest::TestMessageSetExtension2::message_set_extension)->set_str("foo"); + message_set.mutable_unknown_fields()->AddField(kUnknownTypeId) + ->add_length_delimited("bar"); + + string data; + ASSERT_TRUE(message_set.SerializeToString(&data)); + + // Parse back using RawMessageSet and check the contents. + unittest::RawMessageSet raw; + ASSERT_TRUE(raw.ParseFromString(data)); + + EXPECT_EQ(0, raw.unknown_fields().field_count()); + + ASSERT_EQ(3, raw.item_size()); + EXPECT_EQ( + unittest::TestMessageSetExtension1::descriptor()->extension(0)->number(), + raw.item(0).type_id()); + EXPECT_EQ( + unittest::TestMessageSetExtension2::descriptor()->extension(0)->number(), + raw.item(1).type_id()); + EXPECT_EQ(kUnknownTypeId, raw.item(2).type_id()); + + unittest::TestMessageSetExtension1 message1; + EXPECT_TRUE(message1.ParseFromString(raw.item(0).message())); + EXPECT_EQ(123, message1.i()); + + unittest::TestMessageSetExtension2 message2; + EXPECT_TRUE(message2.ParseFromString(raw.item(1).message())); + EXPECT_EQ("foo", message2.str()); + + EXPECT_EQ("bar", raw.item(2).message()); +} + +TEST(WireFormatTest, ParseMessageSet) { + // Set up a RawMessageSet with two known messages and an unknown one. + unittest::RawMessageSet raw; + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id( + unittest::TestMessageSetExtension1::descriptor()->extension(0)->number()); + unittest::TestMessageSetExtension1 message; + message.set_i(123); + message.SerializeToString(item->mutable_message()); + } + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id( + unittest::TestMessageSetExtension2::descriptor()->extension(0)->number()); + unittest::TestMessageSetExtension2 message; + message.set_str("foo"); + message.SerializeToString(item->mutable_message()); + } + + { + unittest::RawMessageSet::Item* item = raw.add_item(); + item->set_type_id(kUnknownTypeId); + item->set_message("bar"); + } + + string data; + ASSERT_TRUE(raw.SerializeToString(&data)); + + // Parse as a TestMessageSet and check the contents. + unittest::TestMessageSet message_set; + ASSERT_TRUE(message_set.ParseFromString(data)); + + EXPECT_EQ(123, message_set.GetExtension( + unittest::TestMessageSetExtension1::message_set_extension).i()); + EXPECT_EQ("foo", message_set.GetExtension( + unittest::TestMessageSetExtension2::message_set_extension).str()); + + ASSERT_EQ(1, message_set.unknown_fields().field_count()); + ASSERT_EQ(1, message_set.unknown_fields().field(0).length_delimited_size()); + EXPECT_EQ("bar", message_set.unknown_fields().field(0).length_delimited(0)); +} + +TEST(WireFormatTest, RecursionLimit) { + unittest::TestRecursiveMessage message; + message.mutable_a()->mutable_a()->mutable_a()->mutable_a()->set_i(1); + string data; + message.SerializeToString(&data); + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(4); + unittest::TestRecursiveMessage message2; + EXPECT_TRUE(message2.ParseFromCodedStream(&input)); + } + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(3); + unittest::TestRecursiveMessage message2; + EXPECT_FALSE(message2.ParseFromCodedStream(&input)); + } +} + +TEST(WireFormatTest, UnknownFieldRecursionLimit) { + unittest::TestEmptyMessage message; + message.mutable_unknown_fields() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_group() + ->AddField(1234)->add_varint(123); + string data; + message.SerializeToString(&data); + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(4); + unittest::TestEmptyMessage message2; + EXPECT_TRUE(message2.ParseFromCodedStream(&input)); + } + + { + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream input(&raw_input); + input.SetRecursionLimit(3); + unittest::TestEmptyMessage message2; + EXPECT_FALSE(message2.ParseFromCodedStream(&input)); + } +} + +TEST(WireFormatTest, ZigZag) { +// avoid line-wrapping +#define LL(x) GOOGLE_LONGLONG(x) +#define ULL(x) GOOGLE_ULONGLONG(x) +#define ZigZagEncode32(x) WireFormat::ZigZagEncode32(x) +#define ZigZagDecode32(x) WireFormat::ZigZagDecode32(x) +#define ZigZagEncode64(x) WireFormat::ZigZagEncode64(x) +#define ZigZagDecode64(x) WireFormat::ZigZagDecode64(x) + + EXPECT_EQ(0u, ZigZagEncode32( 0)); + EXPECT_EQ(1u, ZigZagEncode32(-1)); + EXPECT_EQ(2u, ZigZagEncode32( 1)); + EXPECT_EQ(3u, ZigZagEncode32(-2)); + EXPECT_EQ(0x7FFFFFFEu, ZigZagEncode32(0x3FFFFFFF)); + EXPECT_EQ(0x7FFFFFFFu, ZigZagEncode32(0xC0000000)); + EXPECT_EQ(0xFFFFFFFEu, ZigZagEncode32(0x7FFFFFFF)); + EXPECT_EQ(0xFFFFFFFFu, ZigZagEncode32(0x80000000)); + + EXPECT_EQ( 0, ZigZagDecode32(0u)); + EXPECT_EQ(-1, ZigZagDecode32(1u)); + EXPECT_EQ( 1, ZigZagDecode32(2u)); + EXPECT_EQ(-2, ZigZagDecode32(3u)); + EXPECT_EQ(0x3FFFFFFF, ZigZagDecode32(0x7FFFFFFEu)); + EXPECT_EQ(0xC0000000, ZigZagDecode32(0x7FFFFFFFu)); + EXPECT_EQ(0x7FFFFFFF, ZigZagDecode32(0xFFFFFFFEu)); + EXPECT_EQ(0x80000000, ZigZagDecode32(0xFFFFFFFFu)); + + EXPECT_EQ(0u, ZigZagEncode64( 0)); + EXPECT_EQ(1u, ZigZagEncode64(-1)); + EXPECT_EQ(2u, ZigZagEncode64( 1)); + EXPECT_EQ(3u, ZigZagEncode64(-2)); + EXPECT_EQ(ULL(0x000000007FFFFFFE), ZigZagEncode64(LL(0x000000003FFFFFFF))); + EXPECT_EQ(ULL(0x000000007FFFFFFF), ZigZagEncode64(LL(0xFFFFFFFFC0000000))); + EXPECT_EQ(ULL(0x00000000FFFFFFFE), ZigZagEncode64(LL(0x000000007FFFFFFF))); + EXPECT_EQ(ULL(0x00000000FFFFFFFF), ZigZagEncode64(LL(0xFFFFFFFF80000000))); + EXPECT_EQ(ULL(0xFFFFFFFFFFFFFFFE), ZigZagEncode64(LL(0x7FFFFFFFFFFFFFFF))); + EXPECT_EQ(ULL(0xFFFFFFFFFFFFFFFF), ZigZagEncode64(LL(0x8000000000000000))); + + EXPECT_EQ( 0, ZigZagDecode64(0u)); + EXPECT_EQ(-1, ZigZagDecode64(1u)); + EXPECT_EQ( 1, ZigZagDecode64(2u)); + EXPECT_EQ(-2, ZigZagDecode64(3u)); + EXPECT_EQ(LL(0x000000003FFFFFFF), ZigZagDecode64(ULL(0x000000007FFFFFFE))); + EXPECT_EQ(LL(0xFFFFFFFFC0000000), ZigZagDecode64(ULL(0x000000007FFFFFFF))); + EXPECT_EQ(LL(0x000000007FFFFFFF), ZigZagDecode64(ULL(0x00000000FFFFFFFE))); + EXPECT_EQ(LL(0xFFFFFFFF80000000), ZigZagDecode64(ULL(0x00000000FFFFFFFF))); + EXPECT_EQ(LL(0x7FFFFFFFFFFFFFFF), ZigZagDecode64(ULL(0xFFFFFFFFFFFFFFFE))); + EXPECT_EQ(LL(0x8000000000000000), ZigZagDecode64(ULL(0xFFFFFFFFFFFFFFFF))); + + // Some easier-to-verify round-trip tests. The inputs (other than 0, 1, -1) + // were chosen semi-randomly via keyboard bashing. + EXPECT_EQ( 0, ZigZagDecode32(ZigZagEncode32( 0))); + EXPECT_EQ( 1, ZigZagDecode32(ZigZagEncode32( 1))); + EXPECT_EQ( -1, ZigZagDecode32(ZigZagEncode32( -1))); + EXPECT_EQ(14927, ZigZagDecode32(ZigZagEncode32(14927))); + EXPECT_EQ(-3612, ZigZagDecode32(ZigZagEncode32(-3612))); + + EXPECT_EQ( 0, ZigZagDecode64(ZigZagEncode64( 0))); + EXPECT_EQ( 1, ZigZagDecode64(ZigZagEncode64( 1))); + EXPECT_EQ( -1, ZigZagDecode64(ZigZagEncode64( -1))); + EXPECT_EQ(14927, ZigZagDecode64(ZigZagEncode64(14927))); + EXPECT_EQ(-3612, ZigZagDecode64(ZigZagEncode64(-3612))); + + EXPECT_EQ(LL(856912304801416), ZigZagDecode64(ZigZagEncode64( + LL(856912304801416)))); + EXPECT_EQ(LL(-75123905439571256), ZigZagDecode64(ZigZagEncode64( + LL(-75123905439571256)))); +} + +class WireFormatInvalidInputTest : public testing::Test { + protected: + // Make a serialized TestAllTypes in which the field optional_nested_message + // contains exactly the given bytes, which may be invalid. + string MakeInvalidEmbeddedMessage(const char* bytes, int size) { + const FieldDescriptor* field = + unittest::TestAllTypes::descriptor()->FindFieldByName( + "optional_nested_message"); + GOOGLE_CHECK(field != NULL); + + string result; + + { + io::StringOutputStream raw_output(&result); + io::CodedOutputStream output(&raw_output); + + EXPECT_TRUE(WireFormat::WriteString( + field->number(), string(bytes, size), &output)); + } + + return result; + } + + // Make a serialized TestAllTypes in which the field optionalgroup + // contains exactly the given bytes -- which may be invalid -- and + // possibly no end tag. + string MakeInvalidGroup(const char* bytes, int size, bool include_end_tag) { + const FieldDescriptor* field = + unittest::TestAllTypes::descriptor()->FindFieldByName( + "optionalgroup"); + GOOGLE_CHECK(field != NULL); + + string result; + + { + io::StringOutputStream raw_output(&result); + io::CodedOutputStream output(&raw_output); + + EXPECT_TRUE(output.WriteVarint32(WireFormat::MakeTag(field))); + EXPECT_TRUE(output.WriteString(string(bytes, size))); + if (include_end_tag) { + EXPECT_TRUE( + output.WriteVarint32(WireFormat::MakeTag( + field->number(), WireFormat::WIRETYPE_END_GROUP))); + } + } + + return result; + } +}; + +TEST_F(WireFormatInvalidInputTest, InvalidSubMessage) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidEmbeddedMessage("", 0))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\0", 1))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\200", 1))); + + // The byte is an endgroup tag, but we aren't parsing a group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\014", 1))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidEmbeddedMessage("\017", 1))); +} + +TEST_F(WireFormatInvalidInputTest, InvalidGroup) { + unittest::TestAllTypes message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidGroup("", 0, true))); + + // Missing end tag. Groups cannot end at EOF. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("", 0, false))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\0", 1, false))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\200", 1, false))); + + // The byte is an endgroup tag, but not the right one for this group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\014", 1, false))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\017", 1, true))); +} + +TEST_F(WireFormatInvalidInputTest, InvalidUnknownGroup) { + // Use TestEmptyMessage so that the group made by MakeInvalidGroup will not + // be a known tag number. + unittest::TestEmptyMessage message; + + // Control case. + EXPECT_TRUE(message.ParseFromString(MakeInvalidGroup("", 0, true))); + + // Missing end tag. Groups cannot end at EOF. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("", 0, false))); + + // The byte is a valid varint, but not a valid tag (zero). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\0", 1, false))); + + // The byte is a malformed varint. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\200", 1, false))); + + // The byte is an endgroup tag, but not the right one for this group. + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\014", 1, false))); + + // The byte is a valid varint but not a valid tag (bad wire type). + EXPECT_FALSE(message.ParseFromString(MakeInvalidGroup("\017", 1, true))); +} + +TEST_F(WireFormatInvalidInputTest, InvalidStringInUnknownGroup) { + // Test a bug fix: SkipMessage should fail if the message contains a string + // whose length would extend beyond the message end. + + unittest::TestAllTypes message; + message.set_optional_string("foo foo foo foo"); + string data; + message.SerializeToString(&data); + + // Chop some bytes off the end. + data.resize(data.size() - 4); + + // Try to skip it. Note that the bug was only present when parsing to an + // UnknownFieldSet. + io::ArrayInputStream raw_input(data.data(), data.size()); + io::CodedInputStream coded_input(&raw_input); + UnknownFieldSet unknown_fields; + EXPECT_FALSE(WireFormat::SkipMessage(&coded_input, &unknown_fields)); +} + +} // namespace +} // namespace internal +} // namespace protobuf +} // namespace google diff --git a/src/gtest/CHANGES b/src/gtest/CHANGES new file mode 100644 index 00000000..871ef1fb --- /dev/null +++ b/src/gtest/CHANGES @@ -0,0 +1,3 @@ +Changes for 1.0.0: + + * Initial Open Source release of Google Test diff --git a/src/gtest/CONTRIBUTORS b/src/gtest/CONTRIBUTORS new file mode 100644 index 00000000..31341833 --- /dev/null +++ b/src/gtest/CONTRIBUTORS @@ -0,0 +1,23 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Jeffrey Yasskin +Keir Mierle +Keith Ray +Markus Heule +Patrick Hanna +Patrick Riley +Peter Kaminski +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ásgeirsson +Tracy Bialik +Zhanyong Wan diff --git a/src/gtest/COPYING b/src/gtest/COPYING new file mode 100644 index 00000000..1941a11f --- /dev/null +++ b/src/gtest/COPYING @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +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. diff --git a/src/gtest/README b/src/gtest/README new file mode 100644 index 00000000..0e170475 --- /dev/null +++ b/src/gtest/README @@ -0,0 +1,150 @@ +This directory contains Google Test, described below. It is used by the +Protocol Buffer C++ unit tests. If you would like to use Google Test +yourself, you should probably download it from the URL mentioned below, +not attempt to use the sources in this package. + +Two changes were made from the original sources: +* gtest.cc's #include of gtest-internal-inl.h was modified to reflect the + environment it is being built in (replaced "src/" with "gtest/"). +* GetThreadCount() in gtest-port.h was hard-coded to return 1 rather than 0, + since the Protocol Buffer tests do not use threads. + +The original Google Test README follows. + +====================================================================== + +Google C++ Testing Framework +============================ +http://code.google.com/p/googletest/ + +Overview +-------- +Google's framework for writing C++ tests on a variety of platforms (Linux, Mac +OS X, Windows, Windows CE, and Symbian). Based on the xUnit architecture. +Supports automatic test discovery, a rich set of assertions, user-defined +assertions, death tests, fatal and non-fatal failures, various options for +running the tests, and XML test report generation. + +Please see the project page above for more information as well as mailing lists +for questions, discussions, and development. There is also an IRC channel on +OFTC (irc.oftc.net) #gtest available. Please join us! + +Requirements +------------ +Google Test is designed to have fairly minimal requirements to build and use +with your projects, but there are some. Currently, the only Operating System +(OS) on which Google Test is known to build properly is Linux, but we are +actively working on Windows and Mac support as well. The source code itself is +already portable across many other platforms, but we are still developing +robust build systems for each. + +### Linux Requirements ### +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or "gmake" + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98 standards compliant compiler + +Furthermore, if you are building Google Test from a VCS Checkout (also +described below), there are further requirements: + * Automake version 1.9 or newer + * Autoconf version 2.59 or newer + * Libtool / Libtoolize + * Python version 2.4 or newer + +Getting the Source +------------------ +There are two primary ways of getting Google Test's source code: you can +download a source release in your preferred archive format, or directly check +out the source from a Version Control System (VCS, we use Google Code's +Subversion hosting). The VCS checkout requires a few extra steps and some extra +software packages on your system, but lets you track development, and make +patches to contribute much more easily, so we highly encourage it. + +### VCS Checkout: ### +The first step is to select whether you want to check out the main line of +development on Google Test, or one of the released branches. The former will be +much more active and have the latest features, but the latter provides much +more stability and predictability. Choose whichever fits your needs best, and +proceed with the following Subversion commands: + + $ svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +or for a release version X.Y.*'s branch: + + $ svn checkout http://googletest.googlecode.com/svn/branches/release-X.Y/ gtest-X.Y-svn + +Next you will need to prepare the GNU Autotools build system. Enter the +target directory of the checkout command you used ('gtest-svn' or +'gtest-X.Y-svn' above) and proceed with the following commands: + + $ aclocal-1.9 # Where "1.9" must match the following automake command + $ libtoolize -c + $ autoheader + $ automake-1.9 -ac # See Automake version requirements above + $ autoconf + +While this is a bit complicated, it will most often be automatically re-run by +your "make" invocations, so in practice you shouldn't need to worry too much. +Once you have completed these steps, your VCS checkout should be equivalent to +a source package, and you may continue with those directions, skipping over the +acquiring and unpacking of the source itself, as the VCS has done that for you. + +### Source Package: ### +Google Test is also released in source packages which can be downloaded from +its Google Code download page[1]. Several different archive formats are +provided, but the only difference is the tools used to manipulate them, and the +size of the resulting file. Download whichever you are most comfortable with. + + [1] Google Test Downloads: http://code.google.com/p/googletest/downloads/list + +Once downloaded expand the archive using whichever tools you prefer for that +type. This will always result in a new directory with the name "gtest-X.Y.Z" +which contains all of the source code. Here are some examples in Linux: + + $ tar -xvzf gtest-X.Y.Z.tar.gz + $ tar -xvjf gtest-X.Y.Z.tar.bz2 + $ unzip gtest-X.Y.Z.zip + +Building the Source +------------------- +There are two primary options for building the source at this point: build it +inside the source code tree, or in a separate directory. We recommend building +in a separate directory as that tends to produce both more consistent results +and be easier to clean up should anything go wrong, but both patterns are +supported. The only hard restriction is that while the build directory can be +a subdirectory of the source directory, the opposite is not possible and will +result in errors. Once you have selected where you wish to build Google Test, +create the directory if necessary, and enter it. The following steps apply for +either approach by simply substituting the shell variable SRCDIR with "." for +building inside the source directory, and the relative path to the source +directory otherwise. + + $ ${SRCDIR}/configure # Standard GNU configure script, --help for more info + $ make # Standard makefile following GNU conventions + +Other programs will only be able to use Google Test's functionality if you +install it in a location which they can access, in Linux this is typically +under '/usr/local'. The following command will install all of the Google Test +libraries, public headers, and utilities necessary for other programs and +libraries to leverage it: + + $ sudo make install # Not necessary, but allows use by other programs + +TODO(chandlerc@google.com): This section needs to be expanded when the +'gtest-config' script is finished and Autoconf macro's are provided (or not +provided) in order to properly reflect the process for other programs to +locate, include, and link against Google Test. + +Finally, should you need to remove Google Test from your system after having +installed it, run the following command, and it will back out its changes. +However, note carefully that you must run this command on the *same* Google +Test build that you ran the install from, or the results are not predictable. +If you install Google Test on your system, and are working from a VCS checkout, +make sure you run this *before* updating your checkout of the source in order +to uninstall the same version which you installed. + + $ sudo make uninstall # Must be run against the exact same build as "install" + +Happy testing! diff --git a/src/gtest/gen_gtest_pred_impl.py b/src/gtest/gen_gtest_pred_impl.py new file mode 100755 index 00000000..7cb31da2 --- /dev/null +++ b/src/gtest/gen_gtest_pred_impl.py @@ -0,0 +1,733 @@ +#!/usr/bin/python2.4 +# +# Copyright 2006, Google Inc. +# All rights reserved. +# +# 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. + +"""gen_gtest_pred_impl.py v0.1 + +Generates the implementation of Google Test predicate assertions and +accompanying tests. + +Usage: + + gen_gtest_pred_impl.py MAX_ARITY + +where MAX_ARITY is a positive integer. + +The command generates the implementation of up-to MAX_ARITY-ary +predicate assertions, and writes it to file gtest_pred_impl.h in the +directory where the script is. It also generates the accompanying +unit test in file gtest_pred_impl_unittest.cc. +""" + +__author__ = 'wan@google.com (Zhanyong Wan)' + +import os +import sys +import time + +# Where this script is. +SCRIPT_DIR = os.path.dirname(sys.argv[0]) + +# Where to store the generated header. +HEADER = os.path.join(SCRIPT_DIR, '../include/gtest/gtest_pred_impl.h') + +# Where to store the generated unit test. +UNIT_TEST = os.path.join(SCRIPT_DIR, '../test/gtest_pred_impl_unittest.cc') + + +def HeaderPreamble(n): + """Returns the preamble for the header file. + + Args: + n: the maximum arity of the predicate macros to be generated. + """ + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), n), + 'n' : n + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// 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. + +// This file is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most %(n)s. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT(expression, on_failure) \\ + GTEST_AMBIGUOUS_ELSE_BLOCKER \\ + if (const ::testing::AssertionResult gtest_ar = (expression)) \\ + ; \\ + else \\ + on_failure(gtest_ar.failure_message()) +""" % DEFS) + + +def Arity(n): + """Returns the English name of the given arity.""" + + if n < 0: + return None + elif n <= 3: + return ['nullary', 'unary', 'binary', 'ternary'][n] + else: + return '%s-ary' % n + + +def Title(word): + """Returns the given word in title case. The difference between + this and string's title() method is that Title('4-ary') is '4-ary' + while '4-ary'.title() is '4-Ary'.""" + + return word[0].upper() + word[1:] + + +def OneTo(n): + """Returns the list [1, 2, 3, ..., n].""" + + return range(1, n + 1) + + +def Iter(n, format, sep=''): + """Given a positive integer n, a format string that contains 0 or + more '%s' format specs, and optionally a separator string, returns + the join of n strings, each formatted with the format string on an + iterator ranged from 1 to n. + + Example: + + Iter(3, 'v%s', sep=', ') returns 'v1, v2, v3'. + """ + + # How many '%s' specs are in format? + spec_count = len(format.split('%s')) - 1 + return sep.join([format % (spec_count * (i,)) for i in OneTo(n)]) + + +def ImplementationForArity(n): + """Returns the implementation of n-ary predicate assertions.""" + + # A map the defines the values used in the implementation template. + DEFS = { + 'n' : str(n), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)) + } + + impl = """ + +// Helper function for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +template +AssertionResult AssertPred%(n)sHelper(const char* pred_text""" % DEFS + + impl += Iter(n, """, + const char* e%s""") + + impl += """, + Pred pred""" + + impl += Iter(n, """, + const T%s& v%s""") + + impl += """) { + if (pred(%(vs)s)) return AssertionSuccess(); + + Message msg; +""" % DEFS + + impl += ' msg << pred_text << "("' + + impl += Iter(n, """ + << e%s""", sep=' << ", "') + + impl += ' << ") evaluates to false, where"' + + impl += Iter(n, """ + << "\\n" << e%s << " evaluates to " << v%s""") + + impl += """; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT%(n)s. +// Don't use this in your code. +#define GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, on_failure)\\ + GTEST_ASSERT(pred_format(%(vts)s, %(vs)s),\\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED%(n)s. Don't use +// this in your code. +#define GTEST_PRED%(n)s(pred, %(vs)s, on_failure)\\ + GTEST_ASSERT(::testing::AssertPred%(n)sHelper(#pred""" % DEFS + + impl += Iter(n, """, \\ + #v%s""") + + impl += """, \\ + pred""" + + impl += Iter(n, """, \\ + v%s""") + + impl += """), on_failure) + +// %(Arity)s predicate assertion macros. +#define EXPECT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s(pred, %(vs)s, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT%(n)s(pred_format, %(vs)s) \\ + GTEST_PRED_FORMAT%(n)s(pred_format, %(vs)s, GTEST_FATAL_FAILURE) +#define ASSERT_PRED%(n)s(pred, %(vs)s) \\ + GTEST_PRED%(n)s(pred, %(vs)s, GTEST_FATAL_FAILURE) + +""" % DEFS + + return impl + + +def HeaderPostamble(): + """Returns the postamble for the header file.""" + + return """ + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +""" + + +def GenerateFile(path, content): + """Given a file path and a content string, overwrites it with the + given content.""" + + print 'Updating file %s . . .' % path + + f = file(path, 'w+') + print >>f, content, + f.close() + + print 'File %s has been updated.' % path + + +def GenerateHeader(n): + """Given the maximum arity n, updates the header file that implements + the predicate assertions.""" + + GenerateFile(HEADER, + HeaderPreamble(n) + + ''.join([ImplementationForArity(i) for i in OneTo(n)]) + + HeaderPostamble()) + + +def UnitTestPreamble(): + """Returns the preamble for the unit test file.""" + + # A map that defines the values used in the preamble template. + DEFS = { + 'today' : time.strftime('%m/%d/%Y'), + 'year' : time.strftime('%Y'), + 'command' : '%s %s' % (os.path.basename(sys.argv[0]), sys.argv[1]), + } + + return ( +"""// Copyright 2006, Google Inc. +// All rights reserved. +// +// 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. + +// This file is AUTOMATICALLY GENERATED on %(today)s by command +// '%(command)s'. DO NOT EDIT BY HAND! + +// Regression test for gtest_pred_impl.h +// +// This file is generated by a script and quite long. If you intend to +// learn how Google Test works by reading its unit tests, read +// gtest_unittest.cc instead. +// +// This is intended as a regression test for the Google Test predicate +// assertions. We compile it as part of the gtest_unittest target +// only to keep the implementation tidy and compact, as it is quite +// involved to set up the stage for testing Google Test using Google +// Test itself. +// +// Currently, gtest_unittest takes ~11 seconds to run in the testing +// daemon. In the future, if it grows too large and needs much more +// time to finish, we should consider separating this file into a +// stand-alone regression test. + +#include + +#include +#include + +// A user-defined data type. +struct Bool { + explicit Bool(int val) : value(val != 0) {} + + bool operator>(int n) const { return value > Bool(n).value; } + + Bool operator+(const Bool& rhs) const { return Bool(value + rhs.value); } + + bool operator==(const Bool& rhs) const { return value == rhs.value; } + + bool value; +}; + +// Enables Bool to be used in assertions. +std::ostream& operator<<(std::ostream& os, const Bool& x) { + return os << (x.value ? "true" : "false"); +} + +""" % DEFS) + + +def TestsForArity(n): + """Returns the tests for n-ary predicate assertions.""" + + # A map that defines the values used in the template for the tests. + DEFS = { + 'n' : n, + 'es' : Iter(n, 'e%s', sep=', '), + 'vs' : Iter(n, 'v%s', sep=', '), + 'vts' : Iter(n, '#v%s', sep=', '), + 'tvs' : Iter(n, 'T%s v%s', sep=', '), + 'int_vs' : Iter(n, 'int v%s', sep=', '), + 'Bool_vs' : Iter(n, 'Bool v%s', sep=', '), + 'types' : Iter(n, 'typename T%s', sep=', '), + 'v_sum' : Iter(n, 'v%s', sep=' + '), + 'arity' : Arity(n), + 'Arity' : Title(Arity(n)), + } + + tests = ( +"""// Sample functions/functors for testing %(arity)s predicate assertions. + +// A %(arity)s predicate function. +template <%(types)s> +bool PredFunction%(n)s(%(tvs)s) { + return %(v_sum)s > 0; +} + +// The following two functions are needed to circumvent a bug in +// gcc 2.95.3, which sometimes has problem with the above template +// function. +bool PredFunction%(n)sInt(%(int_vs)s) { + return %(v_sum)s > 0; +} +bool PredFunction%(n)sBool(%(Bool_vs)s) { + return %(v_sum)s > 0; +} +""" % DEFS) + + tests += """ +// A %(arity)s predicate functor. +struct PredFunctor%(n)s { + template <%(types)s> + bool operator()(""" % DEFS + + tests += Iter(n, 'const T%s& v%s', sep=""", + """) + + tests += """) { + return %(v_sum)s > 0; + } +}; +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter function. +template <%(types)s> +testing::AssertionResult PredFormatFunction%(n)s(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) { + if (PredFunction%(n)s(%(vs)s)) + return testing::AssertionSuccess(); + + testing::Message msg; + msg << """ % DEFS + + tests += Iter(n, 'e%s', sep=' << " + " << ') + + tests += """ + << " is expected to be positive, but evaluates to " + << %(v_sum)s << "."; + return testing::AssertionFailure(msg); +} +""" % DEFS + + tests += """ +// A %(arity)s predicate-formatter functor. +struct PredFormatFunctor%(n)s { + template <%(types)s> + testing::AssertionResult operator()(""" % DEFS + + tests += Iter(n, 'const char* e%s', sep=""", + """) + + tests += Iter(n, """, + const T%s& v%s""") + + tests += """) const { + return PredFormatFunction%(n)s(%(es)s, %(vs)s); + } +}; +""" % DEFS + + tests += """ +// Tests for {EXPECT|ASSERT}_PRED_FORMAT%(n)s. + +class Predicate%(n)sTest : public testing::Test { + protected: + virtual void SetUp() { + expected_to_finish_ = true; + finished_ = false;""" % DEFS + + tests += """ + """ + Iter(n, 'n%s_ = ') + """0; + } +""" + + tests += """ + virtual void TearDown() { + // Verifies that each of the predicate's arguments was evaluated + // exactly once.""" + + tests += ''.join([""" + EXPECT_EQ(1, n%s_) << + "The predicate assertion didn't evaluate argument %s " + "exactly once.";""" % (i, i + 1) for i in OneTo(n)]) + + tests += """ + + // Verifies that the control flow in the test function is expected. + if (expected_to_finish_ && !finished_) { + FAIL() << "The predicate assertion unexpactedly aborted the test."; + } else if (!expected_to_finish_ && finished_) { + FAIL() << "The failed predicate assertion didn't abort the test " + "as expected."; + } + } + + // true iff the test function is expected to run to finish. + static bool expected_to_finish_; + + // true iff the test function did run to finish. + static bool finished_; +""" % DEFS + + tests += Iter(n, """ + static int n%s_;""") + + tests += """ +}; + +bool Predicate%(n)sTest::expected_to_finish_; +bool Predicate%(n)sTest::finished_; +""" % DEFS + + tests += Iter(n, """int Predicate%%(n)sTest::n%s_; +""") % DEFS + + tests += """ +typedef Predicate%(n)sTest EXPECT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED_FORMAT%(n)sTest; +typedef Predicate%(n)sTest EXPECT_PRED%(n)sTest; +typedef Predicate%(n)sTest ASSERT_PRED%(n)sTest; +""" % DEFS + + def GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type): + """Returns the test for a predicate assertion macro. + + Args: + use_format: true iff the assertion is a *_PRED_FORMAT*. + use_assert: true iff the assertion is a ASSERT_*. + expect_failure: true iff the assertion is expected to fail. + use_functor: true iff the first argument of the assertion is + a functor (as opposed to a function) + use_user_type: true iff the predicate functor/function takes + argument(s) of a user-defined type. + + Example: + + GenTest(1, 0, 0, 1, 0) returns a test that tests the behavior + of a successful EXPECT_PRED_FORMATn() that takes a functor + whose arguments have built-in types.""" + + if use_assert: + assrt = 'ASSERT' # 'assert' is reserved, so we cannot use + # that identifier here. + else: + assrt = 'EXPECT' + + assertion = assrt + '_PRED' + + if use_format: + pred_format = 'PredFormat' + assertion += '_FORMAT' + else: + pred_format = 'Pred' + + assertion += '%(n)s' % DEFS + + if use_functor: + pred_format_type = 'functor' + pred_format += 'Functor%(n)s()' + else: + pred_format_type = 'function' + pred_format += 'Function%(n)s' + if not use_format: + if use_user_type: + pred_format += 'Bool' + else: + pred_format += 'Int' + + test_name = pred_format_type.title() + + if use_user_type: + arg_type = 'user-defined type (Bool)' + test_name += 'OnUserType' + if expect_failure: + arg = 'Bool(n%s_++)' + else: + arg = 'Bool(++n%s_)' + else: + arg_type = 'built-in type (int)' + test_name += 'OnBuiltInType' + if expect_failure: + arg = 'n%s_++' + else: + arg = '++n%s_' + + if expect_failure: + successful_or_failed = 'failed' + expected_or_not = 'expected.' + test_name += 'Failure' + else: + successful_or_failed = 'successful' + expected_or_not = 'UNEXPECTED!' + test_name += 'Success' + + # A map that defines the values used in the test template. + defs = DEFS.copy() + defs.update({ + 'assert' : assrt, + 'assertion' : assertion, + 'test_name' : test_name, + 'pf_type' : pred_format_type, + 'pf' : pred_format, + 'arg_type' : arg_type, + 'arg' : arg, + 'successful' : successful_or_failed, + 'expected' : expected_or_not, + }) + + test = """ +// Tests a %(successful)s %(assertion)s where the +// predicate-formatter is a %(pf_type)s on a %(arg_type)s. +TEST_F(%(assertion)sTest, %(test_name)s) {""" % defs + + indent = (len(assertion) + 3)*' ' + extra_indent = '' + + if expect_failure: + extra_indent = ' ' + if use_assert: + test += """ + expected_to_finish_ = false; + EXPECT_FATAL_FAILURE({ // NOLINT""" + else: + test += """ + EXPECT_NONFATAL_FAILURE({ // NOLINT""" + + test += '\n' + extra_indent + """ %(assertion)s(%(pf)s""" % defs + + test = test % defs + test += Iter(n, ',\n' + indent + extra_indent + '%(arg)s' % defs) + test += ');\n' + extra_indent + ' finished_ = true;\n' + + if expect_failure: + test += ' }, "");\n' + + test += '}\n' + return test + + # Generates tests for all 2**6 = 64 combinations. + tests += ''.join([GenTest(use_format, use_assert, expect_failure, + use_functor, use_user_type) + for use_format in [0, 1] + for use_assert in [0, 1] + for expect_failure in [0, 1] + for use_functor in [0, 1] + for use_user_type in [0, 1] + ]) + + return tests + + +def UnitTestPostamble(): + """Returns the postamble for the tests.""" + + return '' + + +def GenerateUnitTest(n): + """Returns the tests for up-to n-ary predicate assertions.""" + + GenerateFile(UNIT_TEST, + UnitTestPreamble() + + ''.join([TestsForArity(i) for i in OneTo(n)]) + + UnitTestPostamble()) + + +def _Main(): + """The entry point of the script. Generates the header file and its + unit test.""" + + if len(sys.argv) != 2: + print __doc__ + print 'Author: ' + __author__ + sys.exit(1) + + n = int(sys.argv[1]) + GenerateHeader(n) + GenerateUnitTest(n) + + +if __name__ == '__main__': + _Main() diff --git a/src/gtest/gtest-death-test.cc b/src/gtest/gtest-death-test.cc new file mode 100644 index 00000000..09fdd3ff --- /dev/null +++ b/src/gtest/gtest-death-test.cc @@ -0,0 +1,751 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// This file implements death tests. + +#include +#include + +#include +#include +#include + +#include +#include + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION +#include "gtest-internal-inl.h" +#undef GTEST_IMPLEMENTATION + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +namespace internal { +GTEST_DEFINE_string( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "colons. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#ifdef GTEST_HAS_DEATH_TEST + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; +} + +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static String ExitSummary(int exit_code) { + Message m; + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +#ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +#endif + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static String DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} + +// Static string containing a description of the outcome of the +// last death test. +static String last_death_test_message; + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test +// can conclude. DIED means that the process died while executing the +// test code; LIVED means that process lived beyond the end of the test +// code; and RETURNED means that the test statement attempted a "return," +// which is not allowed. IN_PROGRESS means the test has not yet +// concluded. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const char* format, ...) { + // This function may be called from a threadsafe-style death test + // child process, which operates on a very small stack. Use the + // heap for any additional non-miniscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + va_list args; + va_start(args, format); + + if (flag != NULL) { + FILE* parent = fdopen(flag->status_fd, "w"); + fputc(kDeathTestInternalError, parent); + vfprintf(parent, format, args); + fclose(parent); + va_end(args); + _exit(1); + } else { + vfprintf(stderr, format, args); + va_end(args); + abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +#define GTEST_DEATH_TEST_CHECK(expression) \ + do { \ + if (!(expression)) { \ + DeathTestAbort("CHECK failed: File %s, line %d: %s", \ + __FILE__, __LINE__, #expression); \ + } \ + } while (0) + +// This macro is similar to GTEST_DEATH_TEST_CHECK, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +#define GTEST_DEATH_TEST_CHECK_SYSCALL(expression) \ + do { \ + int retval; \ + do { \ + retval = (expression); \ + } while (retval == -1 && errno == EINTR); \ + if (retval == -1) { \ + DeathTestAbort("CHECK failed: File %s, line %d: %s != -1", \ + __FILE__, __LINE__, #expression); \ + } \ + } while (0) + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message.c_str(); +} + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTest { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual bool Passed(bool status_ok); + virtual void Abort(AbortReason reason); + + protected: + void set_forked(bool forked) { forked_ = forked; } + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + void set_read_fd(int fd) { read_fd_ = fd; } + void set_write_fd(int fd) { write_fd_ = fd; } + + private: + // The textual content of the code this object is testing. + const char* const statement_; + // The regular expression which test output must match. + const RE* const regex_; + // True if the death test successfully forked. + bool forked_; + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; + // File descriptors for communicating the death test's status byte. + int read_fd_; // Always -1 in the child process. + int write_fd_; // Always -1 in the parent process. + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* statement, const RE* regex) + : DeathTest(), + statement_(statement), + regex_(regex), + forked_(false), + child_pid_(-1), + read_fd_(-1), + write_fd_(-1), + status_(-1), + outcome_(IN_PROGRESS) { +} + +// Reads an internal failure message from a file descriptor, then calls +// LOG(FATAL) with that message. Called from a death test parent process +// to read a failure message from the death test child process. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + ssize_t num_read; + + do { + while ((num_read = read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + // TODO(smcafee): Maybe just FAIL the test instead? + if (num_read == 0) { + GTEST_LOG(FATAL, error); + } else { + GTEST_LOG(FATAL, + Message() << "Error while reading death test internal: " + << strerror(errno) << " [" << errno << "]"); + } +} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!forked_) + return 0; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + char flag; + ssize_t bytes_read; + + do { + bytes_read = read(read_fd_, &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + outcome_ = DIED; + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + outcome_ = RETURNED; + break; + case kDeathTestLived: + outcome_ = LIVED; + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd_); // Does not return. + break; + default: + GTEST_LOG(FATAL, + Message() << "Death test child process reported unexpected " + << "status byte (" << static_cast(flag) + << ")"); + } + } else { + GTEST_LOG(FATAL, + Message() << "Read from death test child process failed: " + << strerror(errno)); + } + + GTEST_DEATH_TEST_CHECK_SYSCALL(close(read_fd_)); + GTEST_DEATH_TEST_CHECK_SYSCALL(waitpid(child_pid_, &status_, 0)); + return status_; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: an enumeration describing how the death test +// concluded: DIED, LIVED, or RETURNED. The death test fails +// in the latter two cases +// status: the exit status of the child process, in the format +// specified by wait(2) +// regex: a regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the static variable last_death_test_message. +bool ForkingDeathTest::Passed(bool status_ok) { + if (!forked_) + return false; + +#if GTEST_HAS_GLOBAL_STRING + const ::string error_message = GetCapturedStderr(); +#else + const ::std::string error_message = GetCapturedStderr(); +#endif // GTEST_HAS_GLOBAL_STRING + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement_ << "\n"; + switch (outcome_) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg: " << error_message; + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg: " << error_message; + break; + case DIED: + if (status_ok) { + if (RE::PartialMatch(error_message, *regex_)) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex_->pattern() << "\n" + << "Actual msg: " << error_message; + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status_) << "\n"; + } + break; + default: + GTEST_LOG(FATAL, + "DeathTest::Passed somehow called before conclusion of test"); + } + + last_death_test_message = buffer.GetString(); + return success; +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file desriptor, then +// calls _exit(1). +void ForkingDeathTest::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char flag = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : kDeathTestReturned; + GTEST_DEATH_TEST_CHECK_SYSCALL(write(write_fd_, &flag, 1)); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(write_fd_)); + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* statement, const RE* regex) : + ForkingDeathTest(statement, regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG(WARNING, DeathTestThreadWarning(thread_count)); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK(pipe(pipe_fd) != -1); + + last_death_test_message = ""; + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_forked(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* statement, const RE* regex, + const char* file, int line) : + ForkingDeathTest(statement, regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + ~Arguments() { + for (std::vector::iterator i = args_.begin(); + i + 1 != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, strdup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, strdup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +// The main function for a threadsafe-style death test child process. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(args->close_fd)); + execve(args->argv[0], args->argv, environ); + DeathTestAbort("execve failed: %s", strerror(errno)); + return EXIT_FAILURE; +} + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +static bool StackLowerThanAddress(const void* ptr) { + int dummy; + return &dummy < ptr; +} + +static bool StackGrowsDown() { + int dummy; + return StackLowerThanAddress(&dummy); +} + +// A threadsafe implementation of fork(2) for threadsafe-style death tests +// that uses clone(2). It dies with an error message if anything goes +// wrong. +static pid_t ExecDeathTestFork(char* const* argv, int close_fd) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK(stack != MAP_FAILED); + void* const stack_top = + static_cast(stack) + (stack_grows_down ? stack_size : 0); + ExecDeathTestArgs args = { argv, close_fd }; + const pid_t child_pid = clone(&ExecDeathTestChildMain, stack_top, + SIGCHLD, &args); + GTEST_DEATH_TEST_CHECK(child_pid != -1); + GTEST_DEATH_TEST_CHECK(munmap(stack, stack_size) != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->status_fd); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const String filter_flag = + String::Format("--%s%s=%s.%s", + GTEST_FLAG_PREFIX, kFilterFlag, + info->test_case_name(), info->name()); + const String internal_flag = + String::Format("--%s%s=%s:%d:%d:%d", + GTEST_FLAG_PREFIX, kInternalRunDeathTestFlag, file_, line_, + death_test_index, pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvs()); + args.AddArgument("--logtostderr"); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + last_death_test_message = ""; + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestFork(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_forked(true); + return OVERSEE_TEST; +} + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index) { + last_death_test_message = String::Format( + "Death test count (%d) somehow exceeded expected maximum (%d)", + death_test_index, flag->index); + return false; + } + + if (!(flag->file == file && flag->line == line && + flag->index == death_test_index)) { + *test = NULL; + return true; + } + } + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } else { + last_death_test_message = String::Format( + "Unknown death test style \"%s\" encountered", + GTEST_FLAG(death_test_style).c_str()); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (true) { + const ::std::string::size_type colon = str.find(':', pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +// Attempts to parse a string into a positive integer. Returns true +// if that is possible. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static bool ParsePositiveInt(const ::std::string& str, int* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtol's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !isdigit(str[0])) { + return false; + } + char* endptr; + const long parsed = strtol(str.c_str(), &endptr, 10); // NOLINT + if (*endptr == '\0' && parsed <= INT_MAX) { + *number = static_cast(parsed); + return true; + } else { + return false; + } +} + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + InternalRunDeathTestFlag* const internal_run_death_test_flag = + new InternalRunDeathTestFlag; + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), ':', &fields); + if (fields.size() != 4 + || !ParsePositiveInt(fields[1], &internal_run_death_test_flag->line) + || !ParsePositiveInt(fields[2], &internal_run_death_test_flag->index) + || !ParsePositiveInt(fields[3], + &internal_run_death_test_flag->status_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: %s", + GTEST_FLAG(internal_run_death_test).c_str()); + } + internal_run_death_test_flag->file = fields[0].c_str(); + return internal_run_death_test_flag; +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing diff --git a/src/gtest/gtest-death-test.h b/src/gtest/gtest-death-test.h new file mode 100644 index 00000000..cbd41fe6 --- /dev/null +++ b/src/gtest/gtest-death-test.h @@ -0,0 +1,205 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +#include + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string(death_test_style); + +#ifdef GTEST_HAS_DEATH_TEST + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. The assertion fails immediately if there are more than one +// active threads. This is because it's safe to fork() only when +// there is a single thread. +// +// 2. The parent process forks a sub-process and runs the death test +// in it; the sub-process exits with code 0 at the end of the death +// test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Note: +// +// It's not safe to call exit() if the current process is forked from +// a multi-threaded process, so people usually call _exit() instead in +// such a case. However, we are not concerned with this as we run +// death tests only when there is a single thread. Since exit() has a +// cleaner semantics (it also calls functions registered with atexit() +// and on_exit()), this macro calls exit() instead of _exit() to +// terminate the child process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i); +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +#define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST(statement, predicate, regex, GTEST_FATAL_FAILURE) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +#define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST(statement, predicate, regex, GTEST_NONFATAL_FAILURE) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +#define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +#define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + const int exit_code_; +}; + +// Tests that an exit code describes an exit due to termination by a +// given signal. +class KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +#ifdef NDEBUG + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (false) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + do { statement; } while (false) + +#else + +#define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +#define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +#endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ diff --git a/src/gtest/gtest-filepath.cc b/src/gtest/gtest-filepath.cc new file mode 100644 index 00000000..2fba96ea --- /dev/null +++ b/src/gtest/gtest-filepath.cc @@ -0,0 +1,208 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// 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. +// +// Authors: keith.ray@gmail.com (Keith Ray) + +#include +#include + +#ifdef _WIN32 +#include +#include +#endif // _WIN32 + +#include + +#include + +namespace testing { +namespace internal { + +#ifdef GTEST_OS_WINDOWS +const char kPathSeparator = '\\'; +const char kPathSeparatorString[] = "\\"; +const char kCurrentDirectoryString[] = ".\\"; +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + String dot_extension(String::Format(".%s", extension)); + if (pathname_.EndsWithCaseInsensitive(dot_extension.c_str())) { + return FilePath(String(pathname_.c_str(), pathname_.GetLength() - 4)); + } + return *this; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); + return last_sep ? FilePath(String(last_sep + 1)) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); + return FilePath(last_sep ? String(c_str(), last_sep + 1 - c_str()) + : String(kCurrentDirectoryString)); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + FilePath dir(directory.RemoveTrailingPathSeparator()); + if (number == 0) { + return FilePath(String::Format("%s%c%s.%s", dir.c_str(), kPathSeparator, + base_name.c_str(), extension)); + } + return FilePath(String::Format("%s%c%s_%d.%s", dir.c_str(), kPathSeparator, + base_name.c_str(), number, extension)); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#ifdef GTEST_OS_WINDOWS + struct _stat file_stat = {}; + return _stat(pathname_.c_str(), &file_stat) == 0; +#else + struct stat file_stat = {}; + return stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#ifdef _WIN32 + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + struct _stat file_stat = {}; + result = _stat(removed_sep.c_str(), &file_stat) == 0 && + (_S_IFDIR & file_stat.st_mode) != 0; +#else + struct stat file_stat = {}; + result = stat(pathname_.c_str(), &file_stat) == 0 && + S_ISDIR(file_stat.st_mode); +#endif // _WIN32 + return result; +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return pathname_.EndsWith(kPathSeparatorString); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.GetLength() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#ifdef _WIN32 + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // _WIN32 + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return pathname_.EndsWith(kPathSeparatorString) + ? FilePath(String(pathname_.c_str(), pathname_.GetLength() - 1)) + : *this; +} + +} // namespace internal +} // namespace testing diff --git a/src/gtest/gtest-internal-inl.h b/src/gtest/gtest-internal-inl.h new file mode 100644 index 00000000..2a7d71cb --- /dev/null +++ b/src/gtest/gtest-internal-inl.h @@ -0,0 +1,1118 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION is defined iff the current translation unit is +// part of Google Test's implementation. +#ifndef GTEST_IMPLEMENTATION +// A user is trying to include this from his code - just say no. +#error "gtest-internal-inl.h is part of Google Test's internal implementation." +#error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION + +#include + +#include + +#ifdef GTEST_OS_WINDOWS +#include // NOLINT +#endif // GTEST_OS_WINDOWS + +#include // NOLINT +#include +#include + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify these flags in the code, but want +// Google Test's own unit tests to be able to access them. Therefore we +// declare them here as opposed to in gtest.h. +GTEST_DECLARE_bool(break_on_failure); +GTEST_DECLARE_bool(catch_exceptions); +GTEST_DECLARE_string(color); +GTEST_DECLARE_string(filter); +GTEST_DECLARE_bool(list_tests); +GTEST_DECLARE_string(output); +GTEST_DECLARE_int32(repeat); +GTEST_DECLARE_int32(stack_trace_depth); +GTEST_DECLARE_bool(show_internal_stack_frames); + +namespace internal { + +// Names of the flags (needed for parsing Google Test flags). +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kColorFlag[] = "color"; +const char kRepeatFlag[] = "repeat"; + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + repeat_ = GTEST_FLAG(repeat); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(repeat) = repeat_; + } + private: + // Fields for saving the original values of flags. + bool break_on_failure_; + bool catch_exceptions_; + String color_; + String death_test_style_; + String filter_; + String internal_run_death_test_; + bool list_tests_; + String output_; + bool pretty_; + internal::Int32 repeat_; +} GTEST_ATTRIBUTE_UNUSED; + +// Converts a Unicode code-point to its UTF-8 encoding. +String ToUtf8String(wchar_t wchar); + +// Returns the number of active threads, or 0 when there is an error. +size_t GetThreadCount(); + +// List is a simple singly-linked list container. +// +// We cannot use std::list as Microsoft's implementation of STL has +// problems when exception is disabled. There is a hack to work +// around this, but we've seen cases where the hack fails to work. +// +// TODO(wan): switch to std::list when we have a reliable fix for the +// STL problem, e.g. when we upgrade to the next version of Visual +// C++, or (more likely) switch to STLport. +// +// The element type must support copy constructor. + +// Forward declare List +template // E is the element type. +class List; + +// ListNode is a node in a singly-linked list. It consists of an +// element and a pointer to the next node. The last node in the list +// has a NULL value for its next pointer. +template // E is the element type. +class ListNode { + friend class List; + + private: + + E element_; + ListNode * next_; + + // The c'tor is private s.t. only in the ListNode class and in its + // friend class List we can create a ListNode object. + // + // Creates a node with a given element value. The next pointer is + // set to NULL. + // + // ListNode does NOT have a default constructor. Always use this + // constructor (with parameter) to create a ListNode object. + explicit ListNode(const E & element) : element_(element), next_(NULL) {} + + // We disallow copying ListNode + GTEST_DISALLOW_COPY_AND_ASSIGN(ListNode); + + public: + + // Gets the element in this node. + E & element() { return element_; } + const E & element() const { return element_; } + + // Gets the next node in the list. + ListNode * next() { return next_; } + const ListNode * next() const { return next_; } +}; + + +// List is a simple singly-linked list container. +template // E is the element type. +class List { + public: + + // Creates an empty list. + List() : head_(NULL), last_(NULL), size_(0) {} + + // D'tor. + virtual ~List(); + + // Clears the list. + void Clear() { + if ( size_ > 0 ) { + // 1. Deletes every node. + ListNode * node = head_; + ListNode * next = node->next(); + for ( ; ; ) { + delete node; + node = next; + if ( node == NULL ) break; + next = node->next(); + } + + // 2. Resets the member variables. + head_ = last_ = NULL; + size_ = 0; + } + } + + // Gets the number of elements. + int size() const { return size_; } + + // Returns true if the list is empty. + bool IsEmpty() const { return size() == 0; } + + // Gets the first element of the list, or NULL if the list is empty. + ListNode * Head() { return head_; } + const ListNode * Head() const { return head_; } + + // Gets the last element of the list, or NULL if the list is empty. + ListNode * Last() { return last_; } + const ListNode * Last() const { return last_; } + + // Adds an element to the end of the list. A copy of the element is + // created using the copy constructor, and then stored in the list. + // Changes made to the element in the list doesn't affect the source + // object, and vice versa. + void PushBack(const E & element) { + ListNode * new_node = new ListNode(element); + + if ( size_ == 0 ) { + head_ = last_ = new_node; + size_ = 1; + } else { + last_->next_ = new_node; + last_ = new_node; + size_++; + } + } + + // Adds an element to the beginning of this list. + void PushFront(const E& element) { + ListNode* const new_node = new ListNode(element); + + if ( size_ == 0 ) { + head_ = last_ = new_node; + size_ = 1; + } else { + new_node->next_ = head_; + head_ = new_node; + size_++; + } + } + + // Removes an element from the beginning of this list. If the + // result argument is not NULL, the removed element is stored in the + // memory it points to. Otherwise the element is thrown away. + // Returns true iff the list wasn't empty before the operation. + bool PopFront(E* result) { + if (size_ == 0) return false; + + if (result != NULL) { + *result = head_->element_; + } + + ListNode* const old_head = head_; + size_--; + if (size_ == 0) { + head_ = last_ = NULL; + } else { + head_ = head_->next_; + } + delete old_head; + + return true; + } + + // Inserts an element after a given node in the list. It's the + // caller's responsibility to ensure that the given node is in the + // list. If the given node is NULL, inserts the element at the + // front of the list. + ListNode* InsertAfter(ListNode* node, const E& element) { + if (node == NULL) { + PushFront(element); + return Head(); + } + + ListNode* const new_node = new ListNode(element); + new_node->next_ = node->next_; + node->next_ = new_node; + size_++; + if (node == last_) { + last_ = new_node; + } + + return new_node; + } + + // Returns the number of elements that satisfy a given predicate. + // The parameter 'predicate' is a Boolean function or functor that + // accepts a 'const E &', where E is the element type. + template // P is the type of the predicate function/functor + int CountIf(P predicate) const { + int count = 0; + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element()) ) { + count++; + } + } + + return count; + } + + // Applies a function/functor to each element in the list. The + // parameter 'functor' is a function/functor that accepts a 'const + // E &', where E is the element type. This method does not change + // the elements. + template // F is the type of the function/functor + void ForEach(F functor) const { + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + functor(node->element()); + } + } + + // Returns the first node whose element satisfies a given predicate, + // or NULL if none is found. The parameter 'predicate' is a + // function/functor that accepts a 'const E &', where E is the + // element type. This method does not change the elements. + template // P is the type of the predicate function/functor. + const ListNode * FindIf(P predicate) const { + for ( const ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element()) ) { + return node; + } + } + + return NULL; + } + + template + ListNode * FindIf(P predicate) { + for ( ListNode * node = Head(); + node != NULL; + node = node->next() ) { + if ( predicate(node->element() ) ) { + return node; + } + } + + return NULL; + } + + private: + ListNode* head_; // The first node of the list. + ListNode* last_; // The last node of the list. + int size_; // The number of elements in the list. + + // We disallow copying List. + GTEST_DISALLOW_COPY_AND_ASSIGN(List); +}; + +// The virtual destructor of List. +template +List::~List() { + Clear(); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T * x) { + delete x; +} + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const char* key, const char* value) : + key_(key), value_(value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const char* new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + String key_; + // The value supplied by the user. + String value_; +}; + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const char* key) + : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return String(test_property.key()).Compare(key_) == 0; + } + + private: + String key_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the list of TestPartResults. + const internal::List & test_part_results() const { + return test_part_results_; + } + + // Gets the list of TestProperties. + const internal::List & test_properties() const { + return test_properties_; + } + + // Gets the number of successful test parts. + int successful_part_count() const; + + // Gets the number of failed test parts. + int failed_part_count() const; + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const { return failed_part_count() > 0; } + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. + void RecordProperty(const internal::TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const internal::TestProperty& test_property); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the object. + void Clear(); + private: + // Protects mutable state of the property list and of owned properties, whose + // values may be updated. + internal::Mutex test_properites_mutex_; + + // The list of TestPartResults + internal::List test_part_results_; + // The list of TestProperties + internal::List test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN(TestResult); +}; // class TestResult + +class TestInfoImpl { + public: + TestInfoImpl(TestInfo* parent, const char* test_case_name, + const char* name, TypeId fixture_class_id, + TestMaker maker); + ~TestInfoImpl(); + + // Returns true if this test should run. + bool should_run() const { return should_run_; } + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Returns true if this test is disabled. Disabled tests are not run. + bool is_disabled() const { return is_disabled_; } + + // Sets the is_disabled member. + void set_is_disabled(bool is) { is_disabled_ = is; } + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the ID of the test fixture class. + TypeId fixture_class_id() const { return fixture_class_id_; } + + // Returns the test result. + internal::TestResult* result() { return &result_; } + const internal::TestResult* result() const { return &result_; } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + // Calls the given TestInfo object's Run() method. + static void RunTest(TestInfo * test_info) { + test_info->impl()->Run(); + } + + // Clears the test result. + void ClearResult() { result_.Clear(); } + + // Clears the test result in the given TestInfo object. + static void ClearTestResult(TestInfo * test_info) { + test_info->impl()->ClearResult(); + } + + private: + // These fields are immutable properties of the test. + TestInfo* const parent_; // The owner of this object + const String test_case_name_; // Test case name + const String name_; // Test name + const TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + const TestMaker maker_; // The function that creates the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + internal::TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestInfoImpl); +}; + +} // namespace internal + +// A test case, which consists of a list of TestInfos. +// +// TestCase is not copyable. +class TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Gets the (mutable) list of TestInfos in this TestCase. + internal::List& test_info_list() { return *test_info_list_; } + + // Gets the (immutable) list of TestInfos in this TestCase. + const internal::List & test_info_list() const { + return *test_info_list_; + } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + internal::TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Finds and returns a TestInfo with the given name. If one doesn't + // exist, returns NULL. + TestInfo* GetTestInfo(const char* test_name); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs every test in the given TestCase. + static void RunTestCase(TestCase * test_case) { test_case->Run(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo * test_info) { + const internal::TestInfoImpl* const impl = test_info->impl(); + return impl->should_run() && impl->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo * test_info) { + const internal::TestInfoImpl* const impl = test_info->impl(); + return impl->should_run() && impl->result()->Failed(); + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo * test_info) { + return test_info->impl()->is_disabled(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo *test_info) { + return test_info->impl()->should_run(); + } + + private: + // Name of the test case. + internal::String name_; + // List of TestInfos. + internal::List* test_info_list_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + internal::TimeInMillis elapsed_time_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN(TestCase); +}; + +namespace internal { + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static String GetOutputFormat(); + + // Returns the name of the requested output file, or the default if none + // was explicitly specified. + static String GetOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const String &test_case_name, + const String &test_name); + +#ifdef GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + private: + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const String& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as a String. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual String CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(OsStackTraceGetterInterface); +}; + +// A working implemenation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() {} + virtual String CurrentStackTrace(int max_depth, int skip_count); + virtual void UponLeavingGTest(); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + String message; +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class UnitTestImpl : public TestPartResultReporterInterface { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // Reports a test part result. This method is from the + // TestPartResultReporterInterface interface. + virtual void ReportTestPartResult(const TestPartResult& result); + + // Returns the current test part result reporter. + TestPartResultReporterInterface* test_part_result_reporter(); + + // Sets the current test part result reporter. + void set_test_part_result_reporter(TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + internal::TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const internal::TestResult* ad_hoc_test_result() const { + return &ad_hoc_test_result_; + } + + // Sets the unit test result printer. + // + // Does nothing if the input and the current printer object are the + // same; otherwise, deletes the old printer object and makes the + // input the current printer. + void set_result_printer(UnitTestEventListenerInterface * result_printer); + + // Returns the current unit test result printer if it is not NULL; + // otherwise, creates an appropriate result printer, makes it the + // current printer, and returns it. + UnitTestEventListenerInterface* result_printer(); + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as a String. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + String CurrentOsStackTraceExceptTop(int skip_count); + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo * test_info) { + GetTestCase(test_info->test_case_name(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* current_test_case) { + current_test_case_ = current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* current_test_info) { + current_test_info_ = current_test_info; + } + + // Runs all tests in this UnitTest object, prints the result, and + // returns 0 if all tests are successful, or 1 otherwise. If any + // exception is thrown during a test on Windows, this test is + // considered to be failed, but the rest of the tests will still be + // run. (We disable exceptions on Linux and Mac OS X, so the issue + // doesn't apply there.) + int RunAllTests(); + + // Clears the results of all tests, including the ad hoc test. + void ClearResult() { + test_cases_.ForEach(TestCase::ClearTestCaseResult); + ad_hoc_test_result_.Clear(); + } + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // Returns the number of tests that should run. + int FilterTests(); + + // Lists all the tests by name. + void ListAllTests(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the list of environments that need to be set-up/torn-down + // before/after the tests are run. + internal::List* environments() { return &environments_; } + internal::List* environments_in_reverse_order() { + return &environments_in_reverse_order_; + } + + internal::List* test_cases() { return &test_cases_; } + const internal::List* test_cases() const { return &test_cases_; } + + // Getters for the per-thread Google Test trace stack. + internal::List* gtest_trace_stack() { + return gtest_trace_stack_.pointer(); + } + const internal::List* gtest_trace_stack() const { + return gtest_trace_stack_.pointer(); + } + +#ifdef GTEST_HAS_DEATH_TEST + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + private: + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // Points to (but doesn't own) the test part result reporter. + TestPartResultReporterInterface* test_part_result_reporter_; + + // The list of environments that need to be set-up/torn-down + // before/after the tests are run. environments_in_reverse_order_ + // simply mirrors environments_ in reverse order. + internal::List environments_; + internal::List environments_in_reverse_order_; + + internal::List test_cases_; // The list of TestCases. + + // Points to the last death test case registered. Initially NULL. + internal::ListNode* last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initally NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + internal::TestResult ad_hoc_test_result_; + + // The unit test result printer. Will be deleted when the UnitTest + // object is destructed. By default, a plain text printer is used, + // but the user can set this field to use a custom printer if that + // is desired. + UnitTestEventListenerInterface* result_printer_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#ifdef GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ diff --git a/src/gtest/gtest-message.h b/src/gtest/gtest-message.h new file mode 100644 index 00000000..746a1685 --- /dev/null +++ b/src/gtest/gtest-message.h @@ -0,0 +1,236 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#include "gtest-internal.h" // NOLINT +#else +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a StrStream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that StrStream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + // We allocate the StrStream separately because it otherwise each use of + // ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's + // stack frame leading to huge stack frames in some cases; gcc does not reuse + // the stack space. + Message() : ss_(new internal::StrStream) {} + + // Copy constructor. + Message(const Message& msg) : ss_(new internal::StrStream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new internal::StrStream) { + *ss_ << str; + } + + ~Message() { delete ss_; } +#ifdef __SYMBIAN32__ + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + ::GTestStreamToHelper(ss_, val); + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_, pointer); + } + return *this; + } +#endif // __SYMBIAN32__ + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + Message& operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); + } + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as a String. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::String GetString() const { + return internal::StrStreamToString(ss_); + } + + private: +#ifdef __SYMBIAN32__ + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type dummy, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + ::GTestStreamToHelper(ss_, pointer); + } + } + template + inline void StreamHelper(internal::false_type dummy, const T& value) { + ::GTestStreamToHelper(ss_, value); + } +#endif // __SYMBIAN32__ + + // We'll hold the text streamed to this object here. + internal::StrStream* const ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ diff --git a/src/gtest/gtest-port.cc b/src/gtest/gtest-port.cc new file mode 100644 index 00000000..5c126149 --- /dev/null +++ b/src/gtest/gtest-port.cc @@ -0,0 +1,292 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) + +#include + +#include +#ifdef GTEST_HAS_DEATH_TEST +#include +#endif // GTEST_HAS_DEATH_TEST +#include +#include + +#include +#include +#include + +namespace testing { +namespace internal { + +#ifdef GTEST_HAS_DEATH_TEST + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + regfree(®ex_); + free(const_cast(pattern_)); +} + +// Returns true iff str contains regular expression re. +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = strdup(regex); + is_valid_ = regcomp(®ex_, regex, REG_EXTENDED) == 0; + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; +} + +#endif // GTEST_HAS_DEATH_TEST + +// Logs a message at the given severity level. +void GTestLog(GTestLogSeverity severity, const char* file, + int line, const char* msg) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + fprintf(stderr, "\n%s %s:%d: %s\n", marker, file, line, msg); + if (severity == GTEST_FATAL) { + abort(); + } +} + +#ifdef GTEST_HAS_DEATH_TEST + +// Defines the stderr capturer. + +class CapturedStderr { + public: + // The ctor redirects stderr to a temporary file. + CapturedStderr() { + uncaptured_fd_ = dup(STDERR_FILENO); + + char name_template[] = "captured_stderr.XXXXXX"; + const int captured_fd = mkstemp(name_template); + filename_ = name_template; + fflush(NULL); + dup2(captured_fd, STDERR_FILENO); + close(captured_fd); + } + + ~CapturedStderr() { + remove(filename_.c_str()); + } + + // Stops redirecting stderr. + void StopCapture() { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, STDERR_FILENO); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + // Returns the name of the temporary file holding the stderr output. + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + ::std::string filename() const { return filename_; } + + private: + int uncaptured_fd_; + ::std::string filename_; +}; + +static CapturedStderr* g_captured_stderr = NULL; + +// Returns the size (in bytes) of a file. +static size_t GetFileSize(FILE * file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can +// use it here. +static ::std::string ReadEntireFile(FILE * file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const ::std::string content(buffer, buffer+bytes_read); + delete[] buffer; + + return content; +} + +// Starts capturing stderr. +void CaptureStderr() { + if (g_captured_stderr != NULL) { + GTEST_LOG(FATAL, "Only one stderr capturer can exist at one time."); + } + g_captured_stderr = new CapturedStderr; +} + +// Stops capturing stderr and returns the captured string. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can +// use it here. +::std::string GetCapturedStderr() { + g_captured_stderr->StopCapture(); + FILE* const file = fopen(g_captured_stderr->filename().c_str(), "r"); + const ::std::string content = ReadEntireFile(file); + fclose(file); + + delete g_captured_stderr; + g_captured_stderr = NULL; + + return content; +} + +// A copy of all command line arguments. Set by ParseGTestFlags(). +::std::vector g_argvs; + +// Returns the command line as a vector of strings. +const ::std::vector& GetArgvs() { return g_argvs; } + +#endif // GTEST_HAS_DEATH_TEST + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static String FlagToEnvVar(const char* flag) { + const String full_flag = (Message() << GTEST_FLAG_PREFIX << flag).GetString(); + + Message env_var; + for (int i = 0; i != full_flag.GetLength(); i++) { + env_var << static_cast(toupper(full_flag.c_str()[i])); + } + + return env_var.GetString(); +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const string_value = GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const String env_var = FlagToEnvVar(flag); + const char* const value = GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing diff --git a/src/gtest/gtest-spi.h b/src/gtest/gtest-spi.h new file mode 100644 index 00000000..75d0dcf2 --- /dev/null +++ b/src/gtest/gtest-spi.h @@ -0,0 +1,247 @@ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class TestPartResult { + public: + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(TestPartResultType type, + const char* file_name, + int line_number, + const char* message) + : type_(type), + file_name_(file_name), + line_number_(line_number), + message_(message) { + } + + // Gets the outcome of the test part. + TestPartResultType type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { return file_name_.c_str(); } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == TPRT_SUCCESS; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != TPRT_SUCCESS; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == TPRT_NONFATAL_FAILURE; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == TPRT_FATAL_FAILURE; } + private: + TestPartResultType type_; + + // The name of the source file where the test part took place, or + // NULL if the source file is unknown. + internal::String file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + internal::String message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// We define this class as we cannot use STL containers when compiling +// Google Test with MSVC 7.1 and exceptions disabled. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class TestPartResultArray { + public: + TestPartResultArray(); + ~TestPartResultArray(); + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + private: + // Internally we use a list to simulate the array. Yes, this means + // that random access is O(N) in time, but it's OK for its purpose. + internal::List* const list_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a +// Google Test failure is reported. +class ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + TestPartResultReporterInterface* const old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResultType type, + const char* substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResultType type_; + const String substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// Implementation note: The verification is done in the destructor of +// SingleFailureChecker, to make sure that it's done even when +// 'statement' throws an exception. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +#define EXPECT_FATAL_FAILURE(statement, substr) do {\ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TPRT_FATAL_FAILURE, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (false) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with +// 'substr' being part of the failure message. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// Implementation note: The verification is done in the destructor of +// SingleFailureChecker, to make sure that it's done even when +// 'statement' throws an exception or aborts the function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +#define EXPECT_NONFATAL_FAILURE(statement, substr) do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TPRT_NONFATAL_FAILURE, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + >est_failures);\ + statement;\ + }\ + } while (false) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ diff --git a/src/gtest/gtest.cc b/src/gtest/gtest.cc new file mode 100644 index 00000000..1eee3922 --- /dev/null +++ b/src/gtest/gtest.cc @@ -0,0 +1,3546 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef GTEST_OS_LINUX + +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +#define HAS_GETTIMEOFDAY + +#include +#include +#include +// Declares vsnprintf(). This header is not available on Windows. +#include +#include +#include +#include +#include +#include + +#elif defined(_WIN32_WCE) // We are on Windows CE. + +#include // NOLINT + +#elif defined(_WIN32) // We are on Windows proper. + +#include // NOLINT +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#if defined(__MINGW__) || defined(__MINGW32__) +// MinGW has gettimeofday() but not _ftime64() +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +// TODO(kenton): There are other ways to get the time on Windows, like +// GetTickCount() or GetSystemTimeAsFileTime(). MinGW supports these. +// consider using them instead. +#define HAS_GETTIMEOFDAY +#include // NOLINT +#endif + +// cpplint thinks that the header is already included, so we want to +// silence it. +#include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton): Use autoconf to detect availability of gettimeofday(). +#define HAS_GETTIMEOFDAY + +// cpplint thinks that the header is already included, so we want to +// silence it. +#include // NOLINT +#include // NOLINT + +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION +#include +#undef GTEST_IMPLEMENTATION + +#ifdef GTEST_OS_WINDOWS +#define fileno _fileno +#define isatty _isatty +#define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +// Constants. + +// A test that matches this pattern is disabled and not run. +static const char kDisableTestPattern[] = "DISABLED_*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +GTEST_DEFINE_bool( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", false), + "True iff " GTEST_NAME + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to xterm or xterm-color."); + +GTEST_DEFINE_string( + filter, + internal::StringFromGTestEnv("filter", kUniversalFilter), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_int32( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_int32( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_bool( + show_internal_stack_frames, false, + "True iff " GTEST_NAME " should include internal stack frames when " + "printing test failure stack traces."); + +namespace internal { + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). + +// A user must call testing::ParseGTestFlags() to initialize Google +// Test. g_parse_gtest_flags_called is set to true iff +// ParseGTestFlags() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +static bool g_parse_gtest_flags_called = false; +static bool GTestIsInitialized() { return g_parse_gtest_flags_called; } + +// Iterates over a list of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const internal::List& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (const internal::ListNode* node = case_list.Head(); + node != NULL; + node = node->next()) { + sum += (node->element()->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +#ifdef _WIN32_WCE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +static void abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +#endif // _WIN32_WCE + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResultType type, const char* file, + int line, const char* message) + : type_(type), file_(file), line_(line), message_(message) { +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(type_, file_, line_, + AppendUserMessage(message_, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Application pathname gotten in ParseGTestFlags. +String g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if defined(_WIN32_WCE) || defined(_WIN32) + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // _WIN32_WCE || _WIN32 + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +String UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + String(gtest_output_flag) : + String(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +String UnitTestOptions::GetOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return String(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return String(kDefaultOutputFile); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsDirectory()) + return output_name.ToString(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.ToString(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter(const String& name, const char* filter) { + const char *cur_pattern = filter; + while (true) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// TODO(keithray): move String function implementations to gtest-string.cc. + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const String &test_case_name, + const String &test_name) { + const String& full_name = String::Format("%s.%s", + test_case_name.c_str(), + test_name.c_str()); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + String positive; + String negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = String(""); + } else { + positive.Set(p, dash - p); // Everything up to the dash + negative = String(dash+1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#ifdef GTEST_OS_WINDOWS +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle an exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception. + return (GTEST_FLAG(catch_exceptions) && + exception_code != EXCEPTION_BREAKPOINT) ? + EXCEPTION_EXECUTE_HANDLER : + EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_OS_WINDOWS + +} // namespace internal + +// The interface for printing the result of a UnitTest +class UnitTestEventListenerInterface { + public: + // The d'tor is pure virtual as this is an abstract class. + virtual ~UnitTestEventListenerInterface() = 0; + + // Called before the unit test starts. + virtual void OnUnitTestStart(const UnitTest*) {} + + // Called after the unit test ends. + virtual void OnUnitTestEnd(const UnitTest*) {} + + // Called before the test case starts. + virtual void OnTestCaseStart(const TestCase*) {} + + // Called after the test case ends. + virtual void OnTestCaseEnd(const TestCase*) {} + + // Called before the global set-up starts. + virtual void OnGlobalSetUpStart(const UnitTest*) {} + + // Called after the global set-up ends. + virtual void OnGlobalSetUpEnd(const UnitTest*) {} + + // Called before the global tear-down starts. + virtual void OnGlobalTearDownStart(const UnitTest*) {} + + // Called after the global tear-down ends. + virtual void OnGlobalTearDownEnd(const UnitTest*) {} + + // Called before the test starts. + virtual void OnTestStart(const TestInfo*) {} + + // Called after the test ends. + virtual void OnTestEnd(const TestInfo*) {} + + // Called after an assertion. + virtual void OnNewTestPartResult(const TestPartResult*) {} +}; + +// Constructs an empty TestPartResultArray. +TestPartResultArray::TestPartResultArray() + : list_(new internal::List) { +} + +// Destructs a TestPartResultArray. +TestPartResultArray::~TestPartResultArray() { + delete list_; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + list_->PushBack(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + abort(); + } + + const internal::ListNode* p = list_->Head(); + for (int i = 0; i < index; i++) { + p = p->next(); + } + + return p->element(); +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return list_->size(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : old_reporter_(UnitTest::GetInstance()->impl()-> + test_part_result_reporter()), + result_(result) { + internal::UnitTestImpl* const impl = UnitTest::GetInstance()->impl(); + impl->set_test_part_result_reporter(this); +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + UnitTest::GetInstance()->impl()-> + set_test_part_result_reporter(old_reporter_); +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResultType type, + const char* substr) { + const String expected( + type == TPRT_FATAL_FAILURE ? "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure(msg); + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + msg << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + return AssertionFailure(msg); + } + + if (strstr(r.message(), substr) == NULL) { + msg << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + return AssertionFailure(msg); + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResultType type, + const char* substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_.c_str()); +} + +// Reports a test part result. +void UnitTestImpl::ReportTestPartResult(const TestPartResult& result) { + current_test_result()->AddTestPartResult(result); + result_printer()->OnNewTestPartResult(&result); +} + +// Returns the current test part result reporter. +TestPartResultReporterInterface* UnitTestImpl::test_part_result_reporter() { + return test_part_result_reporter_; +} + +// Sets the current test part result reporter. +void UnitTestImpl::set_test_part_result_reporter( + TestPartResultReporterInterface* reporter) { + test_part_result_reporter_ = reporter; +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return test_cases_.CountIf(TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return test_cases_.CountIf(TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return test_cases_.size(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return test_cases_.CountIf(ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as a String. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +String UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return String(""); +} + +static TimeInMillis GetTimeInMillis() { +#ifdef _WIN32_WCE // We are on Windows CE + // Difference between 1970-01-01 and 1601-01-01 in miliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = 11644473600000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton): Shouldn't this just use GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif defined(_WIN32) && !defined(HAS_GETTIMEOFDAY) + __timeb64 now; +#ifdef _MSC_VER + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton): Use GetTickCount()? Or use SystemTimeToFileTime() +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +#pragma warning(pop) // Restores the warning state. +#else + _ftime64(&now); +#endif // _MSC_VER + return static_cast(now.time) * 1000 + now.millitm; +#elif defined(HAS_GETTIMEOFDAY) + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +#error "Don't know how to get the current time on your system." + return 0; +#endif +} + +// Utilities + +// class String + +// Returns the input enclosed in double quotes if it's not NULL; +// otherwise returns "(null)". For example, "\"Hello\"" is returned +// for input "Hello". +// +// This is useful for printing a C string in the syntax of a literal. +// +// Known issue: escape sequences are not handled yet. +String String::ShowCStringQuoted(const char* c_str) { + return c_str ? String::Format("\"%s\"", c_str) : String("(null)"); +} + +// Copies at most length characters from str into a newly-allocated +// piece of memory of size length+1. The memory is allocated with new[]. +// A terminating null byte is written to the memory, and a pointer to it +// is returned. If str is NULL, NULL is returned. +static char* CloneString(const char* str, size_t length) { + if (str == NULL) { + return NULL; + } else { + char* const clone = new char[length + 1]; + // MSVC 8 deprecates strncpy(), so we want to suppress warning + // 4996 (deprecated function) there. +#ifdef GTEST_OS_WINDOWS // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + strncpy(clone, str, length); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + strncpy(clone, str, length); +#endif // GTEST_OS_WINDOWS + clone[length] = '\0'; + return clone; + } +} + +// Clones a 0-terminated C string, allocating memory using new. The +// caller is responsible for deleting[] the return value. Returns the +// cloned string, or NULL if the input is NULL. +const char * String::CloneCString(const char* c_str) { + return (c_str == NULL) ? + NULL : CloneString(c_str, strlen(c_str)); +} + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t len, + Message* msg) { + for (size_t i = 0; i != len; i++) { + // TODO(wan): consider allowing a testing::String object to + // contain '\0'. This will make it behave more like std::string, + // and will allow ToUtf8String() to return the correct encoding + // for '\0' s.t. we can get rid of the conditional here (and in + // several other places). + if (wstr[i]) { + *msg << internal::ToUtf8String(wstr[i]); + } else { + *msg << '\0'; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +namespace internal { + +// Formats a value to be used in a failure message. + +// For a char value, we print it as a C++ char literal and as an +// unsigned integer (both in decimal and in hexadecimal). +String FormatForFailureMessage(char ch) { + const unsigned int ch_as_uint = ch; + // A String object cannot contain '\0', so we print "\\0" when ch is + // '\0'. + return String::Format("'%s' (%u, 0x%X)", + ch ? String::Format("%c", ch).c_str() : "\\0", + ch_as_uint, ch_as_uint); +} + +// For a wchar_t value, we print it as a C++ wchar_t literal and as an +// unsigned integer (both in decimal and in hexidecimal). +String FormatForFailureMessage(wchar_t wchar) { + // The C++ standard doesn't specify the exact size of the wchar_t + // type. It just says that it shall have the same size as another + // integral type, called its underlying type. + // + // Therefore, in order to print a wchar_t value in the numeric form, + // we first convert it to the largest integral type (UInt64) and + // then print the converted value. + // + // We use streaming to print the value as "%llu" doesn't work + // correctly with MSVC 7.1. + const UInt64 wchar_as_uint64 = wchar; + Message msg; + // A String object cannot contain '\0', so we print "\\0" when wchar is + // L'\0'. + msg << "L'" << (wchar ? ToUtf8String(wchar).c_str() : "\\0") << "' (" + << wchar_as_uint64 << ", 0x" << ::std::setbase(16) + << wchar_as_uint64 << ")"; + return msg.GetString(); +} + +} // namespace internal + +// AssertionResult constructor. +AssertionResult::AssertionResult(const internal::String& failure_message) + : failure_message_(failure_message) { +} + + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(); +} + + +// Makes a failed assertion result with the given failure message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionResult(message.GetString()); +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure(msg); +} + + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + Message msg; + msg << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; + return AssertionFailure(msg); +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + StrStream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + StrStream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + Message msg; + msg << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StrStreamToString(&val1_ss) << " vs " + << StrStreamToString(&val2_ss); + + return AssertionFailure(msg); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + Message msg;\ + msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + return AssertionFailure(msg);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowCStringQuoted(expected), + String::ShowCStringQuoted(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + return AssertionFailure(msg); + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + return AssertionFailure(msg); + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure( + Message() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""); +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_STRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#ifdef GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +#ifdef _WIN32_WCE + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; +#else + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; // String::Format can't exceed this length. + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing cr-lf) + for (; message_length && isspace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } +#endif // _WIN32_WCE + + const String error_hex(String::Format("0x%08X ", hr)); + Message msg; + msg << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << error_text << "\n"; + + return ::testing::AssertionFailure(msg); +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code-point to its UTF-8 encoding. +String ToUtf8String(wchar_t wchar) { + char str[5] = {}; // Initializes str to all '\0' characters. + + UInt32 code = static_cast(wchar); + if (code <= kMaxCodePoint1) { + str[0] = static_cast(code); // 0xxxxxxx + } else if (code <= kMaxCodePoint2) { + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code); // 110xxxxx + } else if (code <= kMaxCodePoint3) { + str[2] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code); // 1110xxxx + } else if (code <= kMaxCodePoint4) { + str[3] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code); // 11110xxx + } else { + return String::Format("(Invalid Unicode 0x%llX)", + static_cast(wchar)); + } + + return String(str); +} + +// Converts a wide C string to a String using the UTF-8 encoding. +// NULL will be converted to "(null)". +String String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + StrStream ss; + while (*wide_c_str) { + ss << internal::ToUtf8String(*wide_c_str++); + } + + return internal::StrStreamToString(&ss); +} + +// Similar to ShowWideCString(), except that this function encloses +// the converted string in double quotes. +String String::ShowWideCStringQuoted(const wchar_t* wide_c_str) { + if (wide_c_str == NULL) return String("(null)"); + + return String::Format("L\"%s\"", + String::ShowWideCString(wide_c_str).c_str()); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + String::ShowWideCStringQuoted(expected), + String::ShowWideCStringQuoted(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + Message msg; + msg << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << String::ShowWideCStringQuoted(s1) + << " vs " << String::ShowWideCStringQuoted(s2); + return AssertionFailure(msg); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + +#ifdef GTEST_OS_WINDOWS + return _stricmp(lhs, rhs) == 0; +#else // GTEST_OS_WINDOWS + return strcasecmp(lhs, rhs) == 0; +#endif // GTEST_OS_WINDOWS +} + +// Constructs a String by copying a given number of chars from a +// buffer. E.g. String("hello", 3) will create the string "hel". +String::String(const char * buffer, size_t len) { + char * const temp = new char[ len + 1 ]; + memcpy(temp, buffer, len); + temp[ len ] = '\0'; + c_str_ = temp; +} + +// Compares this with another String. +// Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 +// if this is greater than rhs. +int String::Compare(const String & rhs) const { + if ( c_str_ == NULL ) { + return rhs.c_str_ == NULL ? 0 : -1; // NULL < anything except NULL + } + + return rhs.c_str_ == NULL ? 1 : strcmp(c_str_, rhs.c_str_); +} + +// Returns true iff this String ends with the given suffix. *Any* +// String is considered to end with a NULL or empty suffix. +bool String::EndsWith(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str_ == NULL) return false; + + const size_t this_len = strlen(c_str_); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CStringEquals(c_str_ + this_len - suffix_len, suffix); +} + +// Returns true iff this String ends with the given suffix, ignoring case. +// Any String is considered to end with a NULL or empty suffix. +bool String::EndsWithCaseInsensitive(const char* suffix) const { + if (suffix == NULL || CStringEquals(suffix, "")) return true; + + if (c_str_ == NULL) return false; + + const size_t this_len = strlen(c_str_); + const size_t suffix_len = strlen(suffix); + return (this_len >= suffix_len) && + CaseInsensitiveCStringEquals(c_str_ + this_len - suffix_len, suffix); +} + +// Sets the 0-terminated C string this String object represents. The +// old string in this object is deleted, and this object will own a +// clone of the input string. This function copies only up to length +// bytes (plus a terminating null byte), or until the first null byte, +// whichever comes first. +// +// This function works even when the c_str parameter has the same +// value as that of the c_str_ field. +void String::Set(const char * c_str, size_t length) { + // Makes sure this works when c_str == c_str_ + const char* const temp = CloneString(c_str, length); + delete[] c_str_; + c_str_ = temp; +} + +// Assigns a C string to this object. Self-assignment works. +const String& String::operator=(const char* c_str) { + // Makes sure this works when c_str == c_str_ + if (c_str != c_str_) { + delete[] c_str_; + c_str_ = CloneCString(c_str); + } + return *this; +} + +// Formats a list of arguments to a String, using the same format +// spec string as for printf. +// +// We do not use the StringPrintf class as it is not universally +// available. +// +// The result is limited to 4096 characters (including the tailing 0). +// If 4096 characters are not enough to format the input, +// "" is returned. +String String::Format(const char * format, ...) { + va_list args; + va_start(args, format); + + char buffer[4096]; + // MSVC 8 deprecates vsnprintf(), so we want to suppress warning + // 4996 (deprecated function) there. +#ifdef GTEST_OS_WINDOWS // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + const int size = + vsnprintf(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, format, args); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + const int size = + vsnprintf(buffer, sizeof(buffer)/sizeof(buffer[0]) - 1, format, args); +#endif // GTEST_OS_WINDOWS + va_end(args); + + return String(size >= 0 ? buffer : ""); +} + +// Converts the buffer in a StrStream to a String, converting NUL +// bytes to "\\0" along the way. +String StrStreamToString(StrStream* ss) { +#if GTEST_HAS_STD_STRING + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); +#else + const char* const start = ss->str(); + const char* const end = start + ss->pcount(); +#endif // GTEST_HAS_STD_STRING + + // We need to use a helper StrStream to do this transformation + // because String doesn't support push_back(). + StrStream helper; + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + helper << "\\0"; // Replaces NUL with "\\0"; + } else { + helper.put(*ch); + } + } + +#if GTEST_HAS_STD_STRING + return String(helper.str().c_str()); +#else + const String str(helper.str(), helper.pcount()); + helper.freeze(false); + ss->freeze(false); + return str; +#endif // GTEST_HAS_STD_STRING +} + +// Appends the user-supplied message to the Google-Test-generated message. +String AppendUserMessage(const String& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const String user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + Message msg; + msg << gtest_msg << "\n" << user_msg_string; + + return msg.GetString(); +} + +} // namespace internal + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os << result.file_name() << ":" + << result.line_number() << ": " + << (result.type() == TPRT_SUCCESS ? "Success" : + result.type() == TPRT_FATAL_FAILURE ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +namespace internal { +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.PushBack(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const TestProperty& test_property) { + if (!ValidateTestProperty(test_property)) { + return; + } + MutexLock lock(&test_properites_mutex_); + ListNode* const node_with_matching_key = + test_properties_.FindIf(TestPropertyKeyIs(test_property.key())); + if (node_with_matching_key == NULL) { + test_properties_.PushBack(test_property); + return; + } + TestProperty& property_with_matching_key = node_with_matching_key->element(); + property_with_matching_key.SetValue(test_property.value()); +} + +// Adds a failure if the key is a reserved attribute of Google Test testcase tags. +// Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const TestProperty& test_property) { + String key(test_property.key()); + if (key == "name" || key == "status" || key == "time" || key == "classname") { + ADD_FAILURE() + << "Reserved key used in RecordProperty(): " + << key + << " ('name', 'status', 'time', and 'classname' are reserved by " + << GTEST_NAME << ")"; + return false; + } + return true; +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.Clear(); + test_properties_.Clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test part passed. +static bool TestPartPassed(const TestPartResult & result) { + return result.passed(); +} + +// Gets the number of successful test parts. +int TestResult::successful_part_count() const { + return test_part_results_.CountIf(TestPartPassed); +} + +// Returns true iff the test part failed. +static bool TestPartFailed(const TestPartResult & result) { + return result.failed(); +} + +// Gets the number of failed test parts. +int TestResult::failed_part_count() const { + return test_part_results_.CountIf(TestPartFailed); +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult & result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return test_part_results_.CountIf(TestPartFatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return test_part_results_.size(); +} + +} // namespace internal + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, const char* value) { + UnitTest::GetInstance()->RecordPropertyForCurrentTest(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const char* key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +#ifdef GTEST_OS_WINDOWS +// We are on Windows. + +// Adds an "exception thrown" fatal failure to the current test. +static void AddExceptionThrownFailure(DWORD exception_code, + const char* location) { + Message message; + message << "Exception thrown with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " in " << location << "."; + + UnitTest* const unit_test = UnitTest::GetInstance(); + unit_test->AddTestPartResult( + TPRT_FATAL_FAILURE, + static_cast(NULL), + // We have no info about the source file where the exception + // occurred. + -1, // We have no info on which line caused the exception. + message.GetString(), + internal::String("")); +} + +#endif // GTEST_OS_WINDOWS + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const internal::TestInfoImpl* const first_test_info = + test_case->test_info_list().Head()->element()->impl(); + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id(); + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const internal::TestInfoImpl* const this_test_info = + impl->current_test_info()->impl(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id(); + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); +#ifdef GTEST_OS_WINDOWS + // We are on Windows. + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + SetUp(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "SetUp()"); + } + + // We will run the test only if SetUp() had no fatal failure. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + TestBody(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "the test body"); + } + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + __try { + TearDown(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), "TearDown()"); + } + +#else // We are on Linux or Mac - exceptions are disabled. + impl->os_stack_trace_getter()->UponLeavingGTest(); + SetUp(); + + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + TestBody(); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + TearDown(); +#endif // GTEST_OS_WINDOWS +} + + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. +TestInfo::TestInfo(const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + TestMaker maker) { + impl_ = new internal::TestInfoImpl(this, test_case_name, name, + fixture_class_id, maker); +} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { + delete impl_; +} + +// Creates a TestInfo object and registers it with the UnitTest +// singleton; returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// maker: pointer to the function that creates a test object +TestInfo* TestInfo::MakeAndRegisterInstance( + const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestMaker maker) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, fixture_class_id, maker); + internal::GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +// Returns the test case name. +const char* TestInfo::test_case_name() const { + return impl_->test_case_name(); +} + +// Returns the test name. +const char* TestInfo::name() const { + return impl_->name(); +} + +// Returns true if this test should run. +bool TestInfo::should_run() const { return impl_->should_run(); } + +// Returns the result of the test. +const internal::TestResult* TestInfo::result() const { return impl_->result(); } + +// Increments the number of death tests encountered in this test so +// far. +int TestInfo::increment_death_test_count() { + return impl_->result()->increment_death_test_count(); +} + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && internal::String(test_info->name()).Compare(name_) == 0; + } + + private: + internal::String name_; +}; + +} // namespace + +// Finds and returns a TestInfo with the given name. If one doesn't +// exist, returns NULL. +TestInfo * TestCase::GetTestInfo(const char* test_name) { + // Can we find a TestInfo with the given name? + internal::ListNode * const node = test_info_list_->FindIf( + TestNameIs(test_name)); + + // Returns the TestInfo found. + return node ? node->element() : NULL; +} + +namespace internal { + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfoImpl::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(parent_); + + // Notifies the unit test event listener that a test is about to + // start. + UnitTestEventListenerInterface* const result_printer = + impl->result_printer(); + result_printer->OnTestStart(parent_); + + const TimeInMillis start = GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); +#ifdef GTEST_OS_WINDOWS + // We are on Windows. + Test* test = NULL; + + __try { + // Creates the test object. + test = (*maker_)(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + AddExceptionThrownFailure(GetExceptionCode(), + "the test fixture's constructor"); + return; + } +#else // We are on Linux or Mac OS - exceptions are disabled. + + // TODO(wan): If test->Run() throws, test won't be deleted. This is + // not a problem now as we don't use exceptions. If we were to + // enable exceptions, we should revise the following to be + // exception-safe. + + // Creates the test object. + Test* test = (*maker_)(); +#endif // GTEST_OS_WINDOWS + + // Runs the test only if the constructor of the test fixture didn't + // generate a fatal failure. + if (!Test::HasFatalFailure()) { + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + delete test; + test = NULL; + + result_.set_elapsed_time(GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + result_printer->OnTestEnd(parent_); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +} // namespace internal + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return test_info_list_->CountIf(TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return test_info_list_->CountIf(TestFailed); +} + +int TestCase::disabled_test_count() const { + return test_info_list_->CountIf(TestDisabled); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return test_info_list_->CountIf(ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return test_info_list_->size(); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(name), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { + test_info_list_ = new internal::List; +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + test_info_list_->ForEach(internal::Delete); + + // Then deletes the Test collection. + delete test_info_list_; + test_info_list_ = NULL; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_->PushBack(test_info); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + UnitTestEventListenerInterface * const result_printer = + impl->result_printer(); + + result_printer->OnTestCaseStart(this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + set_up_tc_(); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + test_info_list_->ForEach(internal::TestInfoImpl::RunTest); + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + tear_down_tc_(); + result_printer->OnTestCaseEnd(this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + test_info_list_->ForEach(internal::TestInfoImpl::ClearTestResult); +} + + +// class UnitTestEventListenerInterface + +// The virtual d'tor. +UnitTestEventListenerInterface::~UnitTestEventListenerInterface() { +} + +// A result printer that never prints anything. Used in the child process +// of an exec-style death test to avoid needless output clutter. +class NullUnitTestResultPrinter : public UnitTestEventListenerInterface {}; + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static internal::String FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::String::Format("%d %s", count, + count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static internal::String FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static internal::String FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResultType enum to human-friendly string +// representation. Both TPRT_NONFATAL_FAILURE and TPRT_FATAL_FAILURE +// are translated to "Failure", as the user usually doesn't care about +// the difference between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResultType type) { + switch (type) { + case TPRT_SUCCESS: + return "Success"; + + case TPRT_NONFATAL_FAILURE: + case TPRT_FATAL_FAILURE: + return "Failure"; + } + + return "Unknown result type"; +} + +// Prints a TestPartResult. +static void PrintTestPartResult( + const TestPartResult & test_part_result) { + const char * const file_name = test_part_result.file_name(); + + printf("%s", file_name == NULL ? "unknown file" : file_name); + if (test_part_result.line_number() >= 0) { + printf(":%d", test_part_result.line_number()); + } + printf(": %s\n", TestPartResultTypeToString(test_part_result.type())); + printf("%s\n", test_part_result.message()); + fflush(stdout); +} + +// class PrettyUnitTestResultPrinter + +namespace internal { + +enum GTestColor { + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#ifdef _WIN32 + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + } + return 0; +} + +#else + +// Returns the ANSI color code for the given color. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + }; + return NULL; +} + +#endif // _WIN32 + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#ifdef _WIN32 + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // _WIN32 + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + + static const bool use_color = ShouldUseColor(isatty(fileno(stdout)) != 0); + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#ifdef _WIN32 + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // _WIN32 + va_end(args); +} + +} // namespace internal + +using internal::ColoredPrintf; +using internal::COLOR_RED; +using internal::COLOR_GREEN; +using internal::COLOR_YELLOW; + +// This class implements the UnitTestEventListenerInterface interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public UnitTestEventListenerInterface { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the + // UnitTestEventListenerInterface class. + virtual void OnUnitTestStart(const UnitTest * unit_test); + virtual void OnGlobalSetUpStart(const UnitTest*); + virtual void OnTestCaseStart(const TestCase * test_case); + virtual void OnTestStart(const TestInfo * test_info); + virtual void OnNewTestPartResult(const TestPartResult * result); + virtual void OnTestEnd(const TestInfo * test_info); + virtual void OnGlobalTearDownStart(const UnitTest*); + virtual void OnUnitTestEnd(const UnitTest * unit_test); + + private: + internal::String test_case_name_; +}; + +// Called before the unit test starts. +void PrettyUnitTestResultPrinter::OnUnitTestStart( + const UnitTest * unit_test) { + const char * const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!internal::String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME, filter); + } + + const internal::UnitTestImpl* const impl = unit_test->impl(); + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(impl->test_to_run_count()).c_str(), + FormatTestCaseCount(impl->test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnGlobalSetUpStart(const UnitTest*) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart( + const TestCase * test_case) { + test_case_name_ = test_case->name(); + const internal::String counts = + FormatCountableNoun(test_case->test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s\n", counts.c_str(), test_case_name_.c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo * test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_case_name_.c_str(), test_info->name()); + printf("\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo * test_info) { + if (test_info->result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_case_name_.c_str(), test_info->name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnNewTestPartResult( + const TestPartResult * result) { + // If the test part succeeded, we don't need to do anything. + if (result->type() == TPRT_SUCCESS) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(*result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnGlobalTearDownStart(const UnitTest*) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +namespace internal { + +// Internal helper for printing the list of failed tests. +static void PrintFailedTestsPretty(const UnitTestImpl* impl) { + const int failed_test_count = impl->failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (const internal::ListNode* node = impl->test_cases()->Head(); + node != NULL; node = node->next()) { + const TestCase* const tc = node->element(); + if (!tc->should_run() || (tc->failed_test_count() == 0)) { + continue; + } + for (const internal::ListNode* tinode = + tc->test_info_list().Head(); + tinode != NULL; tinode = tinode->next()) { + const TestInfo* const ti = tinode->element(); + if (!tc->ShouldRunTest(ti) || tc->TestPassed(ti)) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s\n", ti->test_case_name(), ti->name()); + } + } +} + +} // namespace internal + +void PrettyUnitTestResultPrinter::OnUnitTestEnd( + const UnitTest * unit_test) { + const internal::UnitTestImpl* const impl = unit_test->impl(); + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.\n", + FormatTestCount(impl->test_to_run_count()).c_str(), + FormatTestCaseCount(impl->test_case_to_run_count()).c_str()); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(impl->successful_test_count()).c_str()); + + int num_failures = impl->failed_test_count(); + if (!impl->Passed()) { + const int failed_test_count = impl->failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + internal::PrintFailedTestsPretty(impl); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = impl->disabled_test_count(); + if (num_disabled) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class UnitTestEventsRepeater +// +// This class forwards events to other event listeners. +class UnitTestEventsRepeater : public UnitTestEventListenerInterface { + public: + typedef internal::List Listeners; + typedef internal::ListNode ListenersNode; + UnitTestEventsRepeater() {} + virtual ~UnitTestEventsRepeater(); + void AddListener(UnitTestEventListenerInterface *listener); + + virtual void OnUnitTestStart(const UnitTest* unit_test); + virtual void OnUnitTestEnd(const UnitTest* unit_test); + virtual void OnGlobalSetUpStart(const UnitTest* unit_test); + virtual void OnGlobalSetUpEnd(const UnitTest* unit_test); + virtual void OnGlobalTearDownStart(const UnitTest* unit_test); + virtual void OnGlobalTearDownEnd(const UnitTest* unit_test); + virtual void OnTestCaseStart(const TestCase* test_case); + virtual void OnTestCaseEnd(const TestCase* test_case); + virtual void OnTestStart(const TestInfo* test_info); + virtual void OnTestEnd(const TestInfo* test_info); + virtual void OnNewTestPartResult(const TestPartResult* result); + + private: + Listeners listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTestEventsRepeater); +}; + +UnitTestEventsRepeater::~UnitTestEventsRepeater() { + for (ListenersNode* listener = listeners_.Head(); + listener != NULL; + listener = listener->next()) { + delete listener->element(); + } +} + +void UnitTestEventsRepeater::AddListener( + UnitTestEventListenerInterface *listener) { + listeners_.PushBack(listener); +} + +// Since the methods are identical, use a macro to reduce boilerplate. +// This defines a member that repeats the call to all listeners. +#define GTEST_REPEATER_METHOD(Name, Type) \ +void UnitTestEventsRepeater::Name(const Type* parameter) { \ + for (ListenersNode* listener = listeners_.Head(); \ + listener != NULL; \ + listener = listener->next()) { \ + listener->element()->Name(parameter); \ + } \ +} + +GTEST_REPEATER_METHOD(OnUnitTestStart, UnitTest) +GTEST_REPEATER_METHOD(OnUnitTestEnd, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalSetUpStart, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalSetUpEnd, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalTearDownStart, UnitTest) +GTEST_REPEATER_METHOD(OnGlobalTearDownEnd, UnitTest) +GTEST_REPEATER_METHOD(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD(OnTestCaseEnd, TestCase) +GTEST_REPEATER_METHOD(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD(OnTestEnd, TestInfo) +GTEST_REPEATER_METHOD(OnNewTestPartResult, TestPartResult) + +#undef GTEST_REPEATER_METHOD + +// End PrettyUnitTestResultPrinter + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public UnitTestEventListenerInterface { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnUnitTestEnd(const UnitTest* unit_test); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static internal::String EscapeXml(const char* str, + bool is_attribute); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static internal::String EscapeXmlAttribute(const char* str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static internal::String EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Prints an XML representation of a TestInfo object. + static void PrintXmlTestInfo(FILE* out, + const char* test_case_name, + const TestInfo* test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(FILE* out, const TestCase* test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(FILE* out, const UnitTest* unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the String is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static internal::String TestPropertiesAsXmlAttributes( + const internal::TestResult* result); + + // The output file. + const internal::String output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnUnitTestEnd(const UnitTest* unit_test) { + FILE* xmlout = NULL; + internal::FilePath output_file(output_file_); + internal::FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + // MSVC 8 deprecates fopen(), so we want to suppress warning 4996 + // (deprecated function) there. +#ifdef GTEST_OS_WINDOWS + // We are on Windows. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + xmlout = fopen(output_file_.c_str(), "w"); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + xmlout = fopen(output_file_.c_str(), "w"); +#endif // GTEST_OS_WINDOWS + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + PrintXmlUnitTest(xmlout, unit_test); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +internal::String XmlUnitTestResultPrinter::EscapeXml(const char* str, + bool is_attribute) { + Message m; + + if (str != NULL) { + for (const char* src = str; *src; ++src) { + switch (*src) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(*src)) { + if (is_attribute && IsNormalizableWhitespace(*src)) + m << internal::String::Format("&#x%02X;", unsigned(*src)); + else + m << *src; + } + break; + } + } + } + + return m.GetString(); +} + + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// +// <-- individual assertion failures +// +// +// +// + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::PrintXmlTestInfo(FILE* out, + const char* test_case_name, + const TestInfo* test_info) { + const internal::TestResult * const result = test_info->result(); + const internal::List &results = result->test_part_results(); + fprintf(out, + " name()).c_str(), + test_info->should_run() ? "run" : "notrun", + internal::StreamableToString(result->elapsed_time()).c_str(), + EscapeXmlAttribute(test_case_name).c_str(), + TestPropertiesAsXmlAttributes(result).c_str()); + + int failures = 0; + for (const internal::ListNode* part_node = results.Head(); + part_node != NULL; + part_node = part_node->next()) { + const TestPartResult& part = part_node->element(); + if (part.failed()) { + const internal::String message = + internal::String::Format("%s:%d\n%s", part.file_name(), + part.line_number(), part.message()); + if (++failures == 1) + fprintf(out, ">\n"); + fprintf(out, + " \n", + EscapeXmlAttribute(message.c_str()).c_str()); + } + } + + if (failures == 0) + fprintf(out, " />\n"); + else + fprintf(out, " \n"); +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(FILE* out, + const TestCase* test_case) { + fprintf(out, + " name()).c_str(), + test_case->total_test_count(), + test_case->failed_test_count(), + test_case->disabled_test_count()); + fprintf(out, + "errors=\"0\" time=\"%s\">\n", + internal::StreamableToString(test_case->elapsed_time()).c_str()); + for (const internal::ListNode* info_node = + test_case->test_info_list().Head(); + info_node != NULL; + info_node = info_node->next()) { + PrintXmlTestInfo(out, test_case->name(), info_node->element()); + } + fprintf(out, " \n"); +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(FILE* out, + const UnitTest* unit_test) { + const internal::UnitTestImpl* const impl = unit_test->impl(); + fprintf(out, "\n"); + fprintf(out, + "total_test_count(), + impl->failed_test_count(), + impl->disabled_test_count(), + internal::StreamableToString(impl->elapsed_time()).c_str()); + fprintf(out, "name=\"AllTests\">\n"); + for (const internal::ListNode* case_node = + impl->test_cases()->Head(); + case_node != NULL; + case_node = case_node->next()) { + PrintXmlTestCase(out, case_node->element()); + } + fprintf(out, "\n"); +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +internal::String XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const internal::TestResult* result) { + using internal::TestProperty; + Message attributes; + const internal::List& properties = result->test_properties(); + for (const internal::ListNode* property_node = + properties.Head(); + property_node != NULL; + property_node = property_node->next()) { + const TestProperty& property = property_node->element(); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +namespace internal { + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +// L < UnitTest::mutex_ +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +// L < UnitTest::mutex_ +ScopedTrace::~ScopedTrace() { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as a String. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +// L < mutex_ +// We use "L < mutex_" to denote that the function may acquire mutex_. +String OsStackTraceGetter::CurrentStackTrace(int, int) { + return String(""); +} + +// L < mutex_ +void OsStackTraceGetter::UponLeavingGTest() { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME " internal frames ..."; + +} // namespace internal + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest * UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. +#if _MSC_VER == 1310 && !defined(_DEBUG) // MSVC 7.1 and optimized build. + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // _MSC_VER==1310 && !defined(_DEBUG) +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments()->PushBack(env); + impl_->environments_in_reverse_order()->PushFront(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +// L < mutex_ +void UnitTest::AddTestPartResult(TestPartResultType result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack()->size() > 0) { + msg << "\n" << GTEST_NAME << " trace:"; + + for (internal::ListNode* node = + impl_->gtest_trace_stack()->Head(); + node != NULL; + node = node->next()) { + const internal::TraceInfo& trace = node->element(); + msg << "\n" << trace.file << ":" << trace.line << ": " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << "\nStack trace:\n" << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->test_part_result_reporter()->ReportTestPartResult(result); + + // If this is a failure and the user wants the debugger to break on + // failures ... + if (result_type != TPRT_SUCCESS && GTEST_FLAG(break_on_failure)) { + // ... then we generate a seg fault. + *static_cast(NULL) = 1; + } +} + +// Creates and adds a property to the current TestResult. If a property matching +// the supplied value already exists, updates its value instead. +void UnitTest::RecordPropertyForCurrentTest(const char* key, + const char* value) { + const internal::TestProperty test_property(key, value); + impl_->current_test_result()->RecordProperty(test_property); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { +#ifdef GTEST_OS_WINDOWS + +#if !defined(_WIN32_WCE) + // SetErrorMode doesn't exist on CE. + if (GTEST_FLAG(catch_exceptions)) { + // The user wants Google Test to catch exceptions thrown by the tests. + + // This lets fatal errors be handled by us, instead of causing pop-ups. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); + } +#endif // _WIN32_WCE + + __try { + return impl_->RunAllTests(); + } __except(internal::UnitTestOptions::GTestShouldProcessSEH( + GetExceptionCode())) { + printf("Exception thrown with code 0x%x.\nFAIL\n", GetExceptionCode()); + fflush(stdout); + return 1; + } + +#else + // We are on Linux or Mac OS. There is no exception of any kind. + + return impl_->RunAllTests(); +#endif // GTEST_OS_WINDOWS +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestCase* UnitTest::current_test_case() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +// L < mutex_ +const TestInfo* UnitTest::current_test_info() const { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +// L < mutex_ +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack()->PushFront(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +// L < mutex_ +void UnitTest::PopGTestTrace() { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack()->PopFront(NULL); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), + test_cases_(), + last_death_test_case_(NULL), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + result_printer_(NULL), + os_stack_trace_getter_(NULL), +#ifdef GTEST_HAS_DEATH_TEST + elapsed_time_(0), + internal_run_death_test_flag_(NULL), + death_test_factory_(new DefaultDeathTestFactory) { +#else + elapsed_time_(0) { +#endif // GTEST_HAS_DEATH_TEST + // We do the assignment here instead of in the initializer list, as + // doing that latter causes MSVC to issue a warning about using + // 'this' in initializers. + test_part_result_reporter_ = this; +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + test_cases_.ForEach(internal::Delete); + + // Deletes every Environment. + environments_.ForEach(internal::Delete); + + // Deletes the current test result printer. + delete result_printer_; + + delete os_stack_trace_getter_; +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const String& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + String name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. +// +// Arguments: +// +// test_case_name: name of the test case +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + internal::ListNode* node = test_cases_.FindIf( + TestCaseNameIs(test_case_name)); + + if (node == NULL) { + // No. Let's create one. + TestCase* const test_case = + new TestCase(test_case_name, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (String(test_case_name).EndsWith("DeathTest")) { + // Yes. Inserts the test case after the last death test case + // defined so far. + node = test_cases_.InsertAfter(last_death_test_case_, test_case); + last_death_test_case_ = node; + } else { + // No. Appends to the end of the list. + test_cases_.PushBack(test_case); + node = test_cases_.Last(); + } + } + + // Returns the TestCase found. + return node->element(); +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the List::ForEach() method. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns 0 if all tests are successful, or 1 otherwise. If any +// exception is thrown during a test on Windows, this test is +// considered to be failed, but the rest of the tests will still be +// run. (We disable exceptions on Linux and Mac OS X, so the issue +// doesn't apply there.) +int UnitTestImpl::RunAllTests() { + // True iff Google Test is initialized before RUN_ALL_TESTS() is called. + const bool gtest_is_initialized_before_run_all_tests = GTestIsInitialized(); + + // Lists all the tests and exits if the --gtest_list_tests + // flag was specified. + if (GTEST_FLAG(list_tests)) { + ListAllTests(); + return 0; + } + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#ifdef GTEST_HAS_DEATH_TEST + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + UnitTestEventListenerInterface * const printer = result_printer(); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests() > 0; + // True iff at least one test has failed. + bool failed = false; + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + if (repeat != 1) { + printf("\nRepeating all tests (iteration %d) . . .\n\n", i + 1); + } + + // Tells the unit test event listener that the tests are about to + // start. + printer->OnUnitTestStart(parent_); + + const TimeInMillis start = GetTimeInMillis(); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + printer->OnGlobalSetUpStart(parent_); + environments_.ForEach(SetUpEnvironment); + printer->OnGlobalSetUpEnd(parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + test_cases_.ForEach(TestCase::RunTestCase); + } + + // Tears down all environments in reverse order afterwards. + printer->OnGlobalTearDownStart(parent_); + environments_in_reverse_order_.ForEach(TearDownEnvironment); + printer->OnGlobalTearDownEnd(parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just + // finished. + printer->OnUnitTestEnd(parent_); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + ClearResult(); + } + + if (!gtest_is_initialized_before_run_all_tests) { + ColoredPrintf( + COLOR_RED, "\nIMPORTANT NOTICE - DO NOT IGNORE:\n" + "This test program did NOT call %s() before calling RUN_ALL_TESTS(). " + "This is INVALID. Soon " GTEST_NAME + " will start to enforce the valid usage. " + "Please fix it ASAP, or IT WILL START TO FAIL.\n", + "testing::ParseGTestFlags" + ); // NOLINT + } + + // Returns 0 if all tests passed, or 1 other wise. + return failed ? 1 : 0; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests() { + int num_runnable_tests = 0; + for (const internal::ListNode *test_case_node = + test_cases_.Head(); + test_case_node != NULL; + test_case_node = test_case_node->next()) { + TestCase * const test_case = test_case_node->element(); + const String &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (const internal::ListNode *test_info_node = + test_case->test_info_list().Head(); + test_info_node != NULL; + test_info_node = test_info_node->next()) { + TestInfo * const test_info = test_info_node->element(); + const String test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestPattern. + const bool is_disabled = + internal::UnitTestOptions::PatternMatchesString(kDisableTestPattern, + test_case_name.c_str()) || + internal::UnitTestOptions::PatternMatchesString(kDisableTestPattern, + test_name.c_str()); + test_info->impl()->set_is_disabled(is_disabled); + + const bool should_run = !is_disabled && + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->impl()->set_should_run(should_run); + test_case->set_should_run(test_case->should_run() || should_run); + if (should_run) { + num_runnable_tests++; + } + } + } + return num_runnable_tests; +} + +// Lists all tests by name. +void UnitTestImpl::ListAllTests() { + for (const internal::ListNode* test_case_node = test_cases_.Head(); + test_case_node != NULL; + test_case_node = test_case_node->next()) { + const TestCase* const test_case = test_case_node->element(); + + // Prints the test case name following by an indented list of test nodes. + printf("%s.\n", test_case->name()); + + for (const internal::ListNode* test_info_node = + test_case->test_info_list().Head(); + test_info_node != NULL; + test_info_node = test_info_node->next()) { + const TestInfo* const test_info = test_info_node->element(); + + printf(" %s\n", test_info->name()); + } + } + fflush(stdout); +} + +// Sets the unit test result printer. +// +// Does nothing if the input and the current printer object are the +// same; otherwise, deletes the old printer object and makes the +// input the current printer. +void UnitTestImpl::set_result_printer( + UnitTestEventListenerInterface* result_printer) { + if (result_printer_ != result_printer) { + delete result_printer_; + result_printer_ = result_printer; + } +} + +// Returns the current unit test result printer if it is not NULL; +// otherwise, creates an appropriate result printer, makes it the +// current printer, and returns it. +UnitTestEventListenerInterface* UnitTestImpl::result_printer() { + if (result_printer_ != NULL) { + return result_printer_; + } + +#ifdef GTEST_HAS_DEATH_TEST + if (internal_run_death_test_flag_.get() != NULL) { + result_printer_ = new NullUnitTestResultPrinter; + return result_printer_; + } +#endif // GTEST_HAS_DEATH_TEST + + UnitTestEventsRepeater *repeater = new UnitTestEventsRepeater; + const String& output_format = internal::UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + repeater->AddListener(new XmlUnitTestResultPrinter( + internal::UnitTestOptions::GetOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } + repeater->AddListener(new PrettyUnitTestResultPrinter); + result_printer_ = repeater; + return result_printer_; +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +internal::TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + current_test_info_->impl()->result() : &ad_hoc_test_result_; +} + +// TestInfoImpl constructor. +TestInfoImpl::TestInfoImpl(TestInfo* parent, + const char* test_case_name, + const char* name, + TypeId fixture_class_id, + TestMaker maker) : + parent_(parent), + test_case_name_(String(test_case_name)), + name_(String(name)), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + maker_(maker) { +} + +// TestInfoImpl destructor. +TestInfoImpl::~TestInfoImpl() { +} + +} // namespace internal + +namespace internal { + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX. + const String flag_str = String::Format("--%s%s", GTEST_FLAG_PREFIX, flag); + const size_t flag_len = flag_str.GetLength(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, String* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// The internal implementation of ParseGTestFlags(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void ParseGTestFlagsImpl(int* argc, CharType** argv) { + g_parse_gtest_flags_called = true; + if (*argc <= 0) return; + +#ifdef GTEST_HAS_DEATH_TEST + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } +#endif // GTEST_HAS_DEATH_TEST + + for (int i = 1; i != *argc; i++) { + const String arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } + } +} + +} // namespace internal + +// Parses a command line for the flags that Google Test recognizes. +// Whenever a Google Test flag is seen, it is removed from argv, and *argc +// is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +void ParseGTestFlags(int* argc, char** argv) { + internal::g_executable_path = argv[0]; + internal::ParseGTestFlagsImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +#ifdef GTEST_OS_WINDOWS +void ParseGTestFlags(int* argc, wchar_t** argv) { + // g_executable_path uses normal characters rather than wide chars, so call + // StreamableToString to convert argv[0] to normal characters (utf8 encoding). + internal::g_executable_path = internal::StreamableToString(argv[0]); + internal::ParseGTestFlagsImpl(argc, argv); +} +#endif // GTEST_OS_WINDOWS + +} // namespace testing diff --git a/src/gtest/gtest.h b/src/gtest/gtest.h new file mode 100644 index 00000000..4e3d7bcb --- /dev/null +++ b/src/gtest/gtest.h @@ -0,0 +1,1243 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#ifndef GTEST_NOT_MAC_FRAMEWORK_MODE +// Protobuf never uses gTest in "mac framework mode". +#define GTEST_NOT_MAC_FRAMEWORK_MODE +#endif + +// The following platform macros are used throughout Google Test: +// _WIN32_WCE Windows CE (set in project files) +// __SYMBIAN32__ Symbian (set by Symbian tool chain) +// +// Note that even though _MSC_VER and _WIN32_WCE really indicate a compiler +// and a Win32 implementation, respectively, we use them to indicate the +// combination of compiler - Win 32 API - C library, since the code currently +// only supports: +// Windows proper with Visual C++ and MS C library (_MSC_VER && !_WIN32_WCE) and +// Windows Mobile with Visual C++ and no C library (_WIN32_WCE). + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes +// will be in the framework headers folder along with gtest.h. Define +// GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on the +// Mac and are not using it as a framework. More info on frameworks +// available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-death-test.h" // NOLINT +#include "gtest-internal.h" // NOLINT +#include "gtest-message.h" // NOLINT +#include "gtest-string.h" // NOLINT +#include "gtest_prod.h" // NOLINT +#else +#include +#include +#include +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Depending on the platform, different string classes are available. +// On Windows, ::std::string compiles only when exceptions are +// enabled. On Linux, in addition to ::std::string, Google also makes +// use of class ::string, which has the same interface as +// ::std::string, but has a different implementation. +// +// The user can tell us whether ::std::string is available in his +// environment by defining the macro GTEST_HAS_STD_STRING to either 1 +// or 0 on the compiler command line. He can also define +// GTEST_HAS_GLOBAL_STRING to 1 to indicate that ::string is available +// AND is a distinct type to ::std::string, or define it to 0 to +// indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_STD_STRING to 1 and +// GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_STD_STRING and/or +// GTEST_HAS_GLOBAL_STRING, they are defined heuristically. + +namespace testing { + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32(stack_trace_depth); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool(show_internal_stack_frames); + +// The possible outcomes of a test part (i.e. an assertion or an +// explicit SUCCEED(), FAIL(), or ADD_FAILURE()). +enum TestPartResultType { + TPRT_SUCCESS, // Succeeded. + TPRT_NONFATAL_FAILURE, // Failed but the test can continue. + TPRT_FATAL_FAILURE // Failed and the test should be terminated. +}; + +namespace internal { + +class GTestFlagSaver; + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared in gtest-internal.h but defined here, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that described how it failed. +// +// This class is useful for defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// The constructor of AssertionResult is private. To create an +// instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// For example, in order to be able to write: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you just need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) return testing::AssertionSuccess(); +// +// Message msg; +// msg << "Expected: " << expr << " is even\n" +// << " Actual: it's " << n; +// return testing::AssertionFailure(msg); +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +class AssertionResult { + public: + // Declares factory functions for making successful and failed + // assertion results as friends. + friend AssertionResult AssertionSuccess(); + friend AssertionResult AssertionFailure(const Message&); + + // Returns true iff the assertion succeeded. + operator bool() const { return failure_message_.c_str() == NULL; } // NOLINT + + // Returns the assertion's failure message. + const char* failure_message() const { return failure_message_.c_str(); } + + private: + // The default constructor. It is used when the assertion succeeded. + AssertionResult() {} + + // The constructor used when the assertion failed. + explicit AssertionResult(const internal::String& failure_message); + + // Stores the assertion's failure message. + internal::String failure_message_; +}; + +// Makes a successful assertion result. +AssertionResult AssertionSuccess(); + +// Makes a failed assertion result with the given failure message. +AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class Test { + public: + friend class internal::TestInfoImpl; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef void (*SetUpTestCaseFunc)(); + typedef void (*TearDownTestCaseFunc)(); + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Logs a property for the current test. Only the last value for a given + // key is remembered. + // These are public static so they can be called from utility functions + // that are not members of the test fixture. + // The arguments are const char* instead strings, as Google Test is used + // on platforms where string doesn't compile. + // + // Note that a driving consideration for these RecordProperty methods + // was to produce xml output suited to the Greenspan charting utility, + // which at present will only chart values that fit in a 32-bit int. It + // is the user's responsibility to restrict their values to 32-bit ints + // if they intend them to be used with Greenspan. + static void RecordProperty(const char* key, const char* value); + static void RecordProperty(const char* key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN(Test); +}; + + +// Defines the type of a function pointer that creates a Test object +// when invoked. +typedef Test* (*TestMaker)(); + + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Creates a TestInfo object and registers it with the UnitTest + // singleton; returns the created object. + // + // Arguments: + // + // test_case_name: name of the test case + // name: name of the test + // fixture_class_id: ID of the test fixture class + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // maker: pointer to the function that creates a test object + // + // This is public only because it's needed by the TEST and TEST_F macros. + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + static TestInfo* MakeAndRegisterInstance( + const char* test_case_name, + const char* name, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestMaker maker); + + // Returns the test case name. + const char* test_case_name() const; + + // Returns the test name. + const char* name() const; + + // Returns true if this test should run. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const; + + // Returns the result of the test. + const internal::TestResult* result() const; + private: +#ifdef GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif + friend class internal::TestInfoImpl; + friend class internal::UnitTestImpl; + friend class Test; + friend class TestCase; + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count(); + + // Accessors for the implementation object. + internal::TestInfoImpl* impl() { return impl_; } + const internal::TestInfoImpl* impl() const { return impl_; } + + // Constructs a TestInfo object. + TestInfo(const char* test_case_name, const char* name, + internal::TypeId fixture_class_id, TestMaker maker); + + // An opaque implementation object. + internal::TestInfoImpl* impl_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(TestInfo); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// A UnitTest consists of a list of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + void AddTestPartResult(TestPartResultType result_type, + const char* file_name, + int line_number, + const internal::String& message, + const internal::String& os_stack_trace); + + // Adds a TestProperty to the current TestResult object. If the result already + // contains a property with the same key, the value will be updated. + void RecordPropertyForCurrentTest(const char* key, const char* value); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const; + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const; + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + private: + // ScopedTrace is a friend as it needs to modify the per-thread + // trace stack, which is a private member of UnitTest. + friend class internal::ScopedTrace; + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace(); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Parses a command line for the flags that Google Test recognizes. +// Whenever a Google Test flag is seen, it is removed from argv, and *argc +// is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +void ParseGTestFlags(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +#ifdef GTEST_OS_WINDOWS +void ParseGTestFlags(int* argc, wchar_t** argv); +#endif // GTEST_OS_WINDOWS + +namespace internal { + +// These overloaded versions handle ::std::string and ::std::wstring. +#if GTEST_HAS_STD_STRING +inline String FormatForFailureMessage(const ::std::string& str) { + return (Message() << '"' << str << '"').GetString(); +} +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +inline String FormatForFailureMessage(const ::std::wstring& wstr) { + return (Message() << "L\"" << wstr << '"').GetString(); +} +#endif // GTEST_HAS_STD_WSTRING + +// These overloaded versions handle ::string and ::wstring. +#if GTEST_HAS_GLOBAL_STRING +inline String FormatForFailureMessage(const ::string& str) { + return (Message() << '"' << str << '"').GetString(); +} +#endif // GTEST_HAS_GLOBAL_STRING +#if GTEST_HAS_GLOBAL_WSTRING +inline String FormatForFailureMessage(const ::wstring& wstr) { + return (Message() << "L\"" << wstr << '"').GetString(); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char*, and print it as a C string when it is compared against an +// std::string object, for example. +// +// The default implementation ignores the type of the other operand. +// Some specialized versions are used to handle formatting wide or +// narrow C strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +String FormatForComparisonFailureMessage(const T1& value, + const T2& /* other_operand */) { + return FormatForFailureMessage(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to + // ASSERT_EQ() is a pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + T2* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + Message msg;\ + msg << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + return AssertionFailure(msg);\ + }\ +}\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +#if GTEST_HAS_STD_STRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + StrStream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + StrStream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StrStreamToString(&expected_ss), + StrStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResultType type, const char* file, int line, + const char* message); + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE macro below. + void operator=(const Message& message) const; + private: + TestPartResultType const type_; + const char* const file_; + int const line_; + String const message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(AssertHelper); +}; + +} // namespace internal + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. +// +// Examples: +// +// EXPECT_TRUE(server.StatusIsOK()); +// ASSERT_FALSE(server.HasPendingRequest(port)) +// << "There are still pending requests " << "on port " << port; + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE("Failed") + +// Generates a fatal failure with a generic message. +#define FAIL() GTEST_FATAL_FAILURE("Failed") + +// Generates a success with a generic message. +#define SUCCEED() GTEST_SUCCESS("Succeeded") + +// Boolean assertions. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest_pred_impl.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// C String Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#ifdef GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the expected result +// and the actual result with both a human-readable string representation of +// the error, if available, as well as the hex result code. +#define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +#define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +#define TEST(test_case_name, test_name)\ + GTEST_TEST(test_case_name, test_name, ::testing::Test) + + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST(test_fixture, test_name, test_fixture) + +// Use this macro in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by ParseGTestFlags(). + +#define RUN_ALL_TESTS()\ + (::testing::UnitTest::GetInstance()->Run()) + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/src/gtest/gtest_main.cc b/src/gtest/gtest_main.cc new file mode 100644 index 00000000..c216bd2d --- /dev/null +++ b/src/gtest/gtest_main.cc @@ -0,0 +1,39 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include + +int main(int argc, char **argv) { + std::cout << "Running main() from gtest_main.cc\n"; + + testing::ParseGTestFlags(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/gtest/gtest_pred_impl.h b/src/gtest/gtest_pred_impl.h new file mode 100644 index 00000000..984f7930 --- /dev/null +++ b/src/gtest/gtest_pred_impl.h @@ -0,0 +1,368 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// 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. + +// This file is AUTOMATICALLY GENERATED on 06/22/2008 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1(pred_format, v1, on_failure)\ + GTEST_ASSERT(pred_format(#v1, v1),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1(pred, v1, on_failure)\ + GTEST_ASSERT(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1(pred_format, v1, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1(pred, v1, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1(pred_format, v1, GTEST_FATAL_FAILURE) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1(pred, v1, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, v1, v2),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2(pred, v1, v2, on_failure)\ + GTEST_ASSERT(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2(pred_format, v1, v2, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2(pred, v1, v2, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2(pred_format, v1, v2, GTEST_FATAL_FAILURE) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2(pred, v1, v2, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, v1, v2, v3),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3(pred, v1, v2, v3, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + Message msg; + msg << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; + return AssertionFailure(msg); +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5),\ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ diff --git a/src/gtest/gtest_prod.h b/src/gtest/gtest_prod.h new file mode 100644 index 00000000..da80ddc6 --- /dev/null +++ b/src/gtest/gtest_prod.h @@ -0,0 +1,58 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// 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: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ diff --git a/src/gtest/internal/gtest-death-test-internal.h b/src/gtest/internal/gtest-death-test-internal.h new file mode 100644 index 00000000..b49c6e47 --- /dev/null +++ b/src/gtest/internal/gtest-death-test-internal.h @@ -0,0 +1,201 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#ifdef GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the two reasons that a test might be aborted. + enum AbortReason { TEST_ENCOUNTERED_RETURN_STATEMENT, TEST_DID_NOT_DIE }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status); + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +#define GTEST_DEATH_TEST(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (true) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + { statement; } \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// A struct representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +struct InternalRunDeathTestFlag { + String file; + int line; + int index; + int status_fd; +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ diff --git a/src/gtest/internal/gtest-filepath.h b/src/gtest/internal/gtest-filepath.h new file mode 100644 index 00000000..6f63718d --- /dev/null +++ b/src/gtest/internal/gtest-filepath.h @@ -0,0 +1,168 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// 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: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in testing/base/internal/gtest-internal.h +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + explicit FilePath(const char* pathname) : pathname_(pathname) { } + explicit FilePath(const String& pathname) : pathname_(pathname) { } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + String ToString() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + private: + String pathname_; + + // Don't implement operator= because it is banned by the style guide. + FilePath& operator=(const FilePath& rhs); +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ diff --git a/src/gtest/internal/gtest-internal.h b/src/gtest/internal/gtest-internal.h new file mode 100644 index 00000000..2be1b4ac --- /dev/null +++ b/src/gtest/internal/gtest-internal.h @@ -0,0 +1,569 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-port.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +#ifdef GTEST_OS_LINUX +#include +#include +#include +#include +#endif // GTEST_OS_LINUX + +#include // NOLINT +#include // NOLINT + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-string.h" // NOLINT +#include "gtest-filepath.h" // NOLINT +#else +#include +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN(foo, bar) GTEST_CONCAT_TOKEN_IMPL(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL(foo, bar) foo ## bar + +// Google Test defines the testing::Message class to allow construction of +// test messages via the << operator. The idea is that anything +// streamable to std::ostream can be streamed to a testing::Message. +// This allows a user to use his own types in Google Test assertions by +// overloading the << operator. +// +// util/gtl/stl_logging-inl.h overloads << for STL containers. These +// overloads cannot be defined in the std namespace, as that will be +// undefined behavior. Therefore, they are defined in the global +// namespace instead. +// +// C++'s symbol lookup rule (i.e. Koenig lookup) says that these +// overloads are visible in either the std namespace or the global +// namespace, but not other namespaces, including the testing +// namespace which Google Test's Message class is in. +// +// To allow STL containers (and other types that has a << operator +// defined in the global namespace) to be used in Google Test assertions, +// testing::Message must access the custom << operator from the global +// namespace. Hence this helper function. +// +// Note: Jeffrey Yasskin suggested an alternative fix by "using +// ::operator<<;" in the definition of Message's operator<<. That fix +// doesn't require a helper function, but unfortunately doesn't +// compile with MSVC. +template +inline void GTestStreamToHelper(std::ostream* os, const T& val) { + *os << val; +} + +namespace testing { + +// Forward declaration of classes. + +class Message; // Represents a failure message. +class TestCase; // A collection of related tests. +class TestPartResult; // Result of a test part. +class TestInfo; // Information about a test. +class UnitTest; // A collection of test cases. +class UnitTestEventListenerInterface; // Listens to Google Test events. +class AssertionResult; // Result of an assertion. + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class TestResult; // Result of a single Test. +class UnitTestImpl; // Opaque implementation of UnitTest + +template class List; // A generic list. +template class ListNode; // A node in a generic list. + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef __SYMBIAN32__ // Symbian +// Passing non-POD classes through ellipsis (...) crashes the ARM compiler. +// The Nokia Symbian compiler tries to instantiate a copy constructor for +// objects passed through ellipsis (...), failing for uncopyable objects. +// Hence we define this to false (and lose support for NULL detection). +#define GTEST_IS_NULL_LITERAL(x) false +#else // ! __SYMBIAN32__ +#define GTEST_IS_NULL_LITERAL(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // __SYMBIAN32__ + +// Appends the user-supplied message to the Google-Test-generated message. +String AppendUserMessage(const String& gtest_msg, + const Message& user_msg); + +// A helper class for creating scoped traces in user programs. +class ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable); + +// Formats a value to be used in a failure message. + +#ifdef __SYMBIAN32__ + +// These are needed as the Nokia Symbian Compiler cannot decide between +// const T& and const T* in a function template. The Nokia compiler _can_ +// decide between class template specializations for T and T*, so a +// tr1::type_traits-like is_pointer works, and we can overload on that. + +// This overload makes sure that all pointers (including +// those to char or wchar_t) are printed as raw pointers. +template +inline String FormatValueForFailureMessage(internal::true_type dummy, + T* pointer) { + return StreamableToString(static_cast(pointer)); +} + +template +inline String FormatValueForFailureMessage(internal::false_type dummy, + const T& value) { + return StreamableToString(value); +} + +template +inline String FormatForFailureMessage(const T& value) { + return FormatValueForFailureMessage( + typename internal::is_pointer::type(), value); +} + +#else + +template +inline String FormatForFailureMessage(const T& value) { + return StreamableToString(value); +} + +// This overload makes sure that all pointers (including +// those to char or wchar_t) are printed as raw pointers. +template +inline String FormatForFailureMessage(T* pointer) { + return StreamableToString(static_cast(pointer)); +} + +#endif // __SYMBIAN32__ + +// These overloaded versions handle narrow and wide characters. +String FormatForFailureMessage(char ch); +String FormatForFailureMessage(wchar_t wchar); + +// When this operand is a const char* or char*, and the other operand +// is a ::std::string or ::string, we print this operand as a C string +// rather than a pointer. We do the same for wide strings. + +// This internal macro is used to avoid duplicated code. +#define GTEST_FORMAT_IMPL(operand2_type, operand1_printer)\ +inline String FormatForComparisonFailureMessage(\ + operand2_type::value_type* str, const operand2_type& operand2) {\ + return operand1_printer(str);\ +}\ +inline String FormatForComparisonFailureMessage(\ + const operand2_type::value_type* str, const operand2_type& operand2) {\ + return operand1_printer(str);\ +} + +#if GTEST_HAS_STD_STRING +GTEST_FORMAT_IMPL(::std::string, String::ShowCStringQuoted) +#endif // GTEST_HAS_STD_STRING +#if GTEST_HAS_STD_WSTRING +GTEST_FORMAT_IMPL(::std::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_STRING +GTEST_FORMAT_IMPL(::string, String::ShowCStringQuoted) +#endif // GTEST_HAS_GLOBAL_STRING +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_FORMAT_IMPL(::wstring, String::ShowWideCStringQuoted) +#endif // GTEST_HAS_GLOBAL_WSTRING + +#undef GTEST_FORMAT_IMPL + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const String& expected_value, + const String& actual_value, + bool ignoring_case); + + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm. + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) : value_(x) {} + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.bits_ = bits; + return fp.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(bits_, rhs.bits_) <= kMaxUlps; + } + + private: + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + union { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; +}; + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef void* TypeId; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +inline TypeId GetTypeId() { + static bool dummy = false; + // The compiler is required to create an instance of the static + // variable dummy for each T used to instantiate the template. + // Therefore, the address of dummy is guaranteed to be unique. + return &dummy; +} + +#ifdef GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +AssertionResult IsHRESULTSuccess(const char* expr, long hr); // NOLINT +AssertionResult IsHRESULTFailure(const char* expr, long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE(message, result_type) \ + ::testing::internal::AssertHelper(result_type, __FILE__, __LINE__, message) \ + = ::testing::Message() + +#define GTEST_FATAL_FAILURE(message) \ + return GTEST_MESSAGE(message, ::testing::TPRT_FATAL_FAILURE) + +#define GTEST_NONFATAL_FAILURE(message) \ + GTEST_MESSAGE(message, ::testing::TPRT_NONFATAL_FAILURE) + +#define GTEST_SUCCESS(message) \ + GTEST_MESSAGE(message, ::testing::TPRT_SUCCESS) + +#define GTEST_TEST_BOOLEAN(boolexpr, booltext, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER \ + if (boolexpr) \ + ; \ + else \ + fail("Value of: " booltext "\n Actual: " #actual "\nExpected: " #expected) + +// Helper macro for defining tests. +#define GTEST_TEST(test_case_name, test_name, parent_class)\ +class test_case_name##_##test_name##_Test : public parent_class {\ + public:\ + test_case_name##_##test_name##_Test() {}\ + static ::testing::Test* NewTest() {\ + return new test_case_name##_##test_name##_Test;\ + }\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN(test_case_name##_##test_name##_Test);\ +};\ +\ +::testing::TestInfo* const test_case_name##_##test_name##_Test::test_info_ =\ + ::testing::TestInfo::MakeAndRegisterInstance(\ + #test_case_name, \ + #test_name, \ + ::testing::internal::GetTypeId< parent_class >(), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + test_case_name##_##test_name##_Test::NewTest);\ +void test_case_name##_##test_name##_Test::TestBody() + + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ diff --git a/src/gtest/internal/gtest-port.h b/src/gtest/internal/gtest-port.h new file mode 100644 index 00000000..36d5a149 --- /dev/null +++ b/src/gtest/internal/gtest-port.h @@ -0,0 +1,596 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#ifndef GTEST_NOT_MAC_FRAMEWORK_MODE +// Protobuf never uses gTest in "mac framework mode". +#define GTEST_NOT_MAC_FRAMEWORK_MODE +#endif + +// The user can define the following macros in the build script to +// control Google Test's behavior: +// +// GTEST_HAS_STD_STRING - Define it to 1/0 to indicate that +// std::string does/doesn't work (Google Test can be +// used where std::string is unavailable). Leave +// it undefined to let Google Test define it. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define ::string, +// which is different to std::string). Leave it +// undefined to let Google Test define it. + +// This header defines the following utilities: +// +// Macros indicating the name of the Google C++ Testing Framework project: +// GTEST_NAME - a string literal of the project name. +// GTEST_FLAG_PREFIX - a string literal of the prefix all Google +// Test flag names share. +// GTEST_FLAG_PREFIX_UPPER - a string literal of the prefix all Google +// Test flag names share, in upper case. +// +// Macros indicating the current platform: +// GTEST_OS_LINUX - defined iff compiled on Linux. +// GTEST_OS_MAC - defined iff compiled on Mac OS X. +// GTEST_OS_WINDOWS - defined iff compiled on Windows. +// Note that it is possible that none of the GTEST_OS_ macros are defined. +// +// Macros indicating available Google Test features: +// GTEST_HAS_DEATH_TEST - defined iff death tests are supported. +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED - declares that a class' instances don't have to +// be used. +// GTEST_DISALLOW_COPY_AND_ASSIGN() - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian only. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax. Not available on +// Windows. +// +// Logging: +// GTEST_LOG() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stderr capturing: +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include + +#include +#include + +#define GTEST_NAME "Google Test" +#define GTEST_FLAG_PREFIX "gtest_" +#define GTEST_FLAG_PREFIX_UPPER "GTEST_" + +// Determines the platform on which Google Test is compiled. +#ifdef _MSC_VER +// TODO(kenton): GTEST_OS_WINDOWS is currently used to mean both "The OS is +// Windows" and "The compiler is MSVC". These meanings really should be +// separated in order to better support Windows compilers other than MSVC. +// Then again, the macro _WIN32 is already a good way to check for the first +// case and _MSC_VER is a good way to check for the latter, so maybe +// GTEST_OS_WINDOWS should be removed? +#define GTEST_OS_WINDOWS +#elif defined __APPLE__ +#define GTEST_OS_MAC +#elif defined __linux__ +#define GTEST_OS_LINUX +#endif // _MSC_VER + +// Determines whether ::std::string and ::string are available. + +#ifndef GTEST_HAS_STD_STRING +// The user didn't tell us whether ::std::string is available, so we +// need to figure it out. + +#ifdef GTEST_OS_WINDOWS +// Assumes that exceptions are enabled by default. +#ifndef _HAS_EXCEPTIONS +#define _HAS_EXCEPTIONS 1 +#endif // _HAS_EXCEPTIONS +// GTEST_HAS_EXCEPTIONS is non-zero iff exceptions are enabled. It is +// always defined, while _HAS_EXCEPTIONS is defined only on Windows. +#define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +// On Windows, we can use ::std::string if the compiler version is VS +// 2005 or above, or if exceptions are enabled. +#define GTEST_HAS_STD_STRING ((_MSC_VER >= 1400) || GTEST_HAS_EXCEPTIONS) +#else // We are on Linux or Mac OS. +#define GTEST_HAS_EXCEPTIONS 0 +#define GTEST_HAS_STD_STRING 1 +#endif // GTEST_OS_WINDOWS + +#endif // GTEST_HAS_STD_STRING + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +#define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_STD_STRING || GTEST_HAS_GLOBAL_STRING +#include // NOLINT +#endif // GTEST_HAS_STD_STRING || GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_STD_STRING +#include // NOLINT +#else +#include // NOLINT +#endif // GTEST_HAS_STD_STRING + +// Determines whether to support death tests. +#if GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX) +#define GTEST_HAS_DEATH_TEST +// On some platforms, needs someone to define size_t, and +// won't compile if being #included first. Therefore it's important +// that we #include it after . +#include +#include +#include +#include +#endif // GTEST_HAS_STD_STRING && defined(GTEST_OS_LINUX) + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +#define GTEST_AMBIGUOUS_ELSE_BLOCKER +#else +#define GTEST_AMBIGUOUS_ELSE_BLOCKER switch (0) case 0: // NOLINT +#endif + +// Use this annotation at the end of a struct / class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED; +#if defined(GTEST_OS_WINDOWS) || (defined(GTEST_OS_LINUX) && defined(SWIG)) +#define GTEST_ATTRIBUTE_UNUSED +#else +#define GTEST_ATTRIBUTE_UNUSED __attribute__ ((unused)) +#endif // GTEST_OS_WINDOWS || (GTEST_OS_LINUX && SWIG) + +// A macro to disallow the evil copy constructor and operator= functions +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN(type)\ + type(const type &);\ + void operator=(const type &) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT; +#if defined(__GNUC__) \ + && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) \ + && !defined(COMPILER_ICC) +#define GTEST_MUST_USE_RESULT __attribute__ ((warn_unused_result)) +#else +#define GTEST_MUST_USE_RESULT +#endif // (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4) + +namespace testing { + +class Message; + +namespace internal { + +class String; + +// std::strstream is deprecated. However, we have to use it on +// Windows as std::stringstream won't compile on Windows when +// exceptions are disabled. We use std::stringstream on other +// platforms to avoid compiler warnings there. +#if GTEST_HAS_STD_STRING +typedef ::std::stringstream StrStream; +#else +typedef ::std::strstream StrStream; +#endif // GTEST_HAS_STD_STRING + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (sizeof(T) > 0) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN(scoped_ptr); +}; + +#ifdef GTEST_HAS_DEATH_TEST + +// Defines RE. Currently only needed for death tests. + +// A simple C++ wrapper for . It uses the POSIX Enxtended +// Regular Expression syntax. +class RE { + public: + // Constructs an RE from a string. +#if GTEST_HAS_STD_STRING + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_GLOBAL_STRING + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // Returns true iff str contains regular expression re. + + // TODO(wan): make PartialMatch() work when str contains NUL + // characters. +#if GTEST_HAS_STD_STRING + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } +#endif // GTEST_HAS_STD_STRING + +#if GTEST_HAS_GLOBAL_STRING + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } +#endif // GTEST_HAS_GLOBAL_STRING + + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of a string, as Google Test may be used + // where string is not available. We also do not use Google Test's own + // String type here, in order to simplify dependencies between the + // files. + const char* pattern_; + regex_t regex_; + bool is_valid_; +}; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines logging utilities: +// GTEST_LOG() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +void GTestLog(GTestLogSeverity severity, const char* file, + int line, const char* msg); + +#define GTEST_LOG(severity, msg)\ + ::testing::internal::GTestLog(\ + ::testing::internal::GTEST_##severity, __FILE__, __LINE__, \ + (::testing::Message() << (msg)).GetString().c_str()) + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// Defines the stderr capturer: +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. + +#ifdef GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by ParseGTestFlags(). +extern ::std::vector g_argvs; + +void CaptureStderr(); +// GTEST_HAS_DEATH_TEST implies we have ::std::string. +::std::string GetCapturedStderr(); +const ::std::vector& GetArgvs(); + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + explicit Mutex(int unused) {} + void AssertHeld() const {} + enum { NO_CONSTRUCTOR_NEEDED_FOR_STATIC_MUTEX = 0 }; +}; + +// We cannot call it MutexLock directly as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class ThreadLocal { + public: + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// There's no portable way to detect the number of threads, so we just +// return 0 to indicate that we cannot detect it. +// CHANGED FOR PROTOBUF: The protobuf tests do not use multiple threads, +// so we know there is one thread. +inline size_t GetThreadCount() { return 1; } + +// Defines tr1::is_pointer (only needed for Symbian). + +#ifdef __SYMBIAN32__ + +// Symbian does not have tr1::type_traits, so we define our own is_pointer +// These are needed as the Nokia Symbian Compiler cannot decide between +// const T& and const T* in a function template. + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +#endif // __SYMBIAN32__ + +// Defines BiggestInt as the biggest signed integer type the compiler +// supports. + +#ifdef GTEST_OS_WINDOWS +typedef __int64 BiggestInt; +#else +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#ifdef GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// A wrapper for getenv() that works on Linux, Windows, and Mac OS. +inline const char* GetEnv(const char* name) { +#ifdef _WIN32_WCE // We are on Windows CE. + // CE has no environment variables. + return NULL; +#elif defined(GTEST_OS_WINDOWS) // We are on Windows proper. + // MSVC 8 deprecates getenv(), so we want to suppress warning 4996 + // (deprecated function) there. +#pragma warning(push) // Saves the current warning state. +#pragma warning(disable:4996) // Temporarily disables warning 4996. + return getenv(name); +#pragma warning(pop) // Restores the warning state. +#else // We are on Linux or Mac OS. + return getenv(name); +#endif +} + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool(name) extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32(name) \ + extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string(name) \ + extern ::testing::internal::String GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool(name, default_val, doc) \ + bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32(name, default_val, doc) \ + ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string(name, default_val, doc) \ + ::testing::internal::String GTEST_FLAG(name) = (default_val) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ diff --git a/src/gtest/internal/gtest-string.h b/src/gtest/internal/gtest-string.h new file mode 100644 index 00000000..3d20c0fc --- /dev/null +++ b/src/gtest/internal/gtest-string.h @@ -0,0 +1,280 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// 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. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by testing/base/internal/gtest-internal.h. +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#include + +#if defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) +// When using Google Test on the Mac as a framework, all the includes will be +// in the framework headers folder along with gtest.h. +// Define GTEST_NOT_MAC_FRAMEWORK_MODE if you are building Google Test on +// the Mac and are not using it as a framework. +// More info on frameworks available here: +// http://developer.apple.com/documentation/MacOSX/Conceptual/BPFrameworks/ +// Concepts/WhatAreFrameworks.html. +#include "gtest-port.h" // NOLINT +#else +#include +#endif // defined(__APPLE__) && !defined(GTEST_NOT_MAC_FRAMEWORK_MODE) + +namespace testing { +namespace internal { + +// String - a UTF-8 string class. +// +// We cannot use std::string as Microsoft's STL implementation in +// Visual C++ 7.1 has problems when exception is disabled. There is a +// hack to work around this, but we've seen cases where the hack fails +// to work. +// +// Also, String is different from std::string in that it can represent +// both NULL and the empty string, while std::string cannot represent +// NULL. +// +// NULL and the empty string are considered different. NULL is less +// than anything (including the empty string) except itself. +// +// This class only provides minimum functionality necessary for +// implementing Google Test. We do not intend to implement a full-fledged +// string class here. +// +// Since the purpose of this class is to provide a substitute for +// std::string on platforms where it cannot be used, we define a copy +// constructor and assignment operators such that we don't need +// conditional compilation in a lot of places. +// +// In order to make the representation efficient, the d'tor of String +// is not virtual. Therefore DO NOT INHERIT FROM String. +class String { + public: + // Static utility methods + + // Returns the input if it's not NULL, otherwise returns "(null)". + // This function serves two purposes: + // + // 1. ShowCString(NULL) has type 'const char *', instead of the + // type of NULL (which is int). + // + // 2. In MSVC, streaming a null char pointer to StrStream generates + // an access violation, so we need to convert NULL to "(null)" + // before streaming it. + static inline const char* ShowCString(const char* c_str) { + return c_str ? c_str : "(null)"; + } + + // Returns the input enclosed in double quotes if it's not NULL; + // otherwise returns "(null)". For example, "\"Hello\"" is returned + // for input "Hello". + // + // This is useful for printing a C string in the syntax of a literal. + // + // Known issue: escape sequences are not handled yet. + static String ShowCStringQuoted(const char* c_str); + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static String ShowWideCString(const wchar_t* wide_c_str); + + // Similar to ShowWideCString(), except that this function encloses + // the converted string in double quotes. + static String ShowWideCStringQuoted(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Formats a list of arguments to a String, using the same format + // spec string as for printf. + // + // We do not use the StringPrintf class as it is not universally + // available. + // + // The result is limited to 4096 characters (including the tailing + // 0). If 4096 characters are not enough to format the input, + // "" is returned. + static String Format(const char* format, ...); + + // C'tors + + // The default c'tor constructs a NULL string. + String() : c_str_(NULL) {} + + // Constructs a String by cloning a 0-terminated C string. + String(const char* c_str) : c_str_(NULL) { // NOLINT + *this = c_str; + } + + // Constructs a String by copying a given number of chars from a + // buffer. E.g. String("hello", 3) will create the string "hel". + String(const char* buffer, size_t len); + + // The copy c'tor creates a new copy of the string. The two + // String objects do not share content. + String(const String& str) : c_str_(NULL) { + *this = str; + } + + // D'tor. String is intended to be a final class, so the d'tor + // doesn't need to be virtual. + ~String() { delete[] c_str_; } + + // Returns true iff this is an empty string (i.e. ""). + bool empty() const { + return (c_str_ != NULL) && (*c_str_ == '\0'); + } + + // Compares this with another String. + // Returns < 0 if this is less than rhs, 0 if this is equal to rhs, or > 0 + // if this is greater than rhs. + int Compare(const String& rhs) const; + + // Returns true iff this String equals the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator==(const char* c_str) const { + return CStringEquals(c_str_, c_str); + } + + // Returns true iff this String doesn't equal the given C string. A NULL + // string and a non-NULL string are considered not equal. + bool operator!=(const char* c_str) const { + return !CStringEquals(c_str_, c_str); + } + + // Returns true iff this String ends with the given suffix. *Any* + // String is considered to end with a NULL or empty suffix. + bool EndsWith(const char* suffix) const; + + // Returns true iff this String ends with the given suffix, not considering + // case. Any String is considered to end with a NULL or empty suffix. + bool EndsWithCaseInsensitive(const char* suffix) const; + + // Returns the length of the encapsulated string, or -1 if the + // string is NULL. + int GetLength() const { + return c_str_ ? static_cast(strlen(c_str_)) : -1; + } + + // Gets the 0-terminated C string this String object represents. + // The String object still owns the string. Therefore the caller + // should NOT delete the return value. + const char* c_str() const { return c_str_; } + + // Sets the 0-terminated C string this String object represents. + // The old string in this object is deleted, and this object will + // own a clone of the input string. This function copies only up to + // length bytes (plus a terminating null byte), or until the first + // null byte, whichever comes first. + // + // This function works even when the c_str parameter has the same + // value as that of the c_str_ field. + void Set(const char* c_str, size_t length); + + // Assigns a C string to this object. Self-assignment works. + const String& operator=(const char* c_str); + + // Assigns a String object to this object. Self-assignment works. + const String& operator=(const String &rhs) { + *this = rhs.c_str_; + return *this; + } + + private: + const char* c_str_; +}; + +// Streams a String to an ostream. +inline ::std::ostream& operator <<(::std::ostream& os, const String& str) { + // We call String::ShowCString() to convert NULL to "(null)". + // Otherwise we'll get an access violation on Windows. + return os << String::ShowCString(str.c_str()); +} + +// Gets the content of the StrStream's buffer as a String. Each '\0' +// character in the buffer is replaced with "\\0". +String StrStreamToString(StrStream* stream); + +// Converts a streamable value to a String. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". + +// Declared here but defined in gtest.h, so that it has access +// to the definition of the Message class, required by the ARM +// compiler. +template +String StreamableToString(const T& streamable); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ diff --git a/vsprojects/config.h b/vsprojects/config.h new file mode 100644 index 00000000..95befa10 --- /dev/null +++ b/vsprojects/config.h @@ -0,0 +1,21 @@ +/* protobuf config.h for MSVC. On other platforms, this is generated + * automatically by autoheader / autoconf / configure. */ + +/* the location of */ +#define HASH_MAP_H + +/* the namespace of hash_map/hash_set */ +#if _MSC_VER < 1310 +#define HASH_NAMESPACE std +#else +#define HASH_NAMESPACE stdext +#endif + +/* the location of */ +#define HASH_SET_H + +/* define if the compiler has hash_map */ +#define HAVE_HASH_MAP + +/* define if the compiler has hash_set */ +#define HAVE_HASH_SET diff --git a/vsprojects/convert2008to2005.sh b/vsprojects/convert2008to2005.sh new file mode 100755 index 00000000..60eccaf3 --- /dev/null +++ b/vsprojects/convert2008to2005.sh @@ -0,0 +1,20 @@ +#! /bin/sh -e + +# This script downgrades MSVC 2008 projects to MSVC 2005 projects, allowing +# people with MSVC 2005 to open them. Otherwise, MSVC 2005 simply refuses to +# open projects created with 2008. We run this as part of our release process. +# If you obtained the code direct from version control and you want to use +# MSVC 2005, you may have to run this manually. (Hint: Use Cygwin or MSYS.) + +for file in *.sln; do + echo "downgrading $file..." + sed -i -re 's/Format Version 10.00/Format Version 9.00/g; + s/Visual Studio 2008/Visual Studio 2005/g;' $file +done + +for file in *.vcproj; do + echo "downgrading $file..." + sed -i -re 's/Version="9.00"/Version="8.00"/g;' $file +done + +# Yes, really, that's it. diff --git a/vsprojects/extract_includes.bat b/vsprojects/extract_includes.bat new file mode 100755 index 00000000..85c4d92b --- /dev/null +++ b/vsprojects/extract_includes.bat @@ -0,0 +1,36 @@ +md include +md include\google +md include\google\protobuf +md include\google\protobuf\stubs +md include\google\protobuf\io +md include\google\protobuf\compiler +md include\google\protobuf\compiler\cpp +md include\google\protobuf\compiler\java +md include\google\protobuf\compiler\python +copy ..\src\google\protobuf\stubs\common.h include\google\protobuf\stubs\common.h +copy ..\src\google\protobuf\descriptor.h include\google\protobuf\descriptor.h +copy ..\src\google\protobuf\descriptor.pb.h include\google\protobuf\descriptor.pb.h +copy ..\src\google\protobuf\descriptor_database.h include\google\protobuf\descriptor_database.h +copy ..\src\google\protobuf\dynamic_message.h include\google\protobuf\dynamic_message.h +copy ..\src\google\protobuf\extension_set.h include\google\protobuf\extension_set.h +copy ..\src\google\protobuf\generated_message_reflection.h include\google\protobuf\generated_message_reflection.h +copy ..\src\google\protobuf\message.h include\google\protobuf\message.h +copy ..\src\google\protobuf\reflection_ops.h include\google\protobuf\reflection_ops.h +copy ..\src\google\protobuf\repeated_field.h include\google\protobuf\repeated_field.h +copy ..\src\google\protobuf\service.h include\google\protobuf\service.h +copy ..\src\google\protobuf\text_format.h include\google\protobuf\text_format.h +copy ..\src\google\protobuf\unknown_field_set.h include\google\protobuf\unknown_field_set.h +copy ..\src\google\protobuf\wire_format.h include\google\protobuf\wire_format.h +copy ..\src\google\protobuf\wire_format_inl.h include\google\protobuf\wire_format_inl.h +copy ..\src\google\protobuf\io\coded_stream.h include\google\protobuf\io\coded_stream.h +copy ..\src\google\protobuf\io\printer.h include\google\protobuf\io\printer.h +copy ..\src\google\protobuf\io\tokenizer.h include\google\protobuf\io\tokenizer.h +copy ..\src\google\protobuf\io\zero_copy_stream.h include\google\protobuf\io\zero_copy_stream.h +copy ..\src\google\protobuf\io\zero_copy_stream_impl.h include\google\protobuf\io\zero_copy_stream_impl.h +copy ..\src\google\protobuf\compiler\code_generator.h include\google\protobuf\compiler\code_generator.h +copy ..\src\google\protobuf\compiler\command_line_interface.h include\google\protobuf\compiler\command_line_interface.h +copy ..\src\google\protobuf\compiler\importer.h include\google\protobuf\compiler\importer.h +copy ..\src\google\protobuf\compiler\parser.h include\google\protobuf\compiler\parser.h +copy ..\src\google\protobuf\compiler\cpp\cpp_generator.h include\google\protobuf\compiler\cpp\cpp_generator.h +copy ..\src\google\protobuf\compiler\java\java_generator.h include\google\protobuf\compiler\java\java_generator.h +copy ..\src\google\protobuf\compiler\python\python_generator.h include\google\protobuf\compiler\python\python_generator.h diff --git a/vsprojects/libprotobuf.vcproj b/vsprojects/libprotobuf.vcproj new file mode 100644 index 00000000..2b22cb71 --- /dev/null +++ b/vsprojects/libprotobuf.vcproj @@ -0,0 +1,412 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/libprotoc.vcproj b/vsprojects/libprotoc.vcproj new file mode 100644 index 00000000..0b478d6d --- /dev/null +++ b/vsprojects/libprotoc.vcproj @@ -0,0 +1,513 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/protobuf.sln b/vsprojects/protobuf.sln new file mode 100644 index 00000000..9523a864 --- /dev/null +++ b/vsprojects/protobuf.sln @@ -0,0 +1,50 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libprotobuf", "libprotobuf.vcproj", "{3E283F37-A4ED-41B7-A3E6-A2D89D131A30}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libprotoc", "libprotoc.vcproj", "{B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}" + ProjectSection(ProjectDependencies) = postProject + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "protoc", "protoc.vcproj", "{1738D5F6-ED1E-47E0-B2F0-456864B93C1E}" + ProjectSection(ProjectDependencies) = postProject + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} = {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcproj", "{4DF72760-C055-40A5-A77E-30A17E2AC2DB}" + ProjectSection(ProjectDependencies) = postProject + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} = {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE} + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} = {3E283F37-A4ED-41B7-A3E6-A2D89D131A30} + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E} = {1738D5F6-ED1E-47E0-B2F0-456864B93C1E} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Debug|Win32.Build.0 = Debug|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.ActiveCfg = Release|Win32 + {3E283F37-A4ED-41B7-A3E6-A2D89D131A30}.Release|Win32.Build.0 = Release|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Debug|Win32.ActiveCfg = Debug|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Debug|Win32.Build.0 = Debug|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Release|Win32.ActiveCfg = Release|Win32 + {B84FF31A-5F9A-46F8-AB22-DBFC9BECE3BE}.Release|Win32.Build.0 = Release|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Debug|Win32.ActiveCfg = Debug|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Debug|Win32.Build.0 = Debug|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Release|Win32.ActiveCfg = Release|Win32 + {1738D5F6-ED1E-47E0-B2F0-456864B93C1E}.Release|Win32.Build.0 = Release|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Debug|Win32.ActiveCfg = Debug|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Debug|Win32.Build.0 = Debug|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Release|Win32.ActiveCfg = Release|Win32 + {4DF72760-C055-40A5-A77E-30A17E2AC2DB}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/vsprojects/protoc.vcproj b/vsprojects/protoc.vcproj new file mode 100644 index 00000000..1efc520d --- /dev/null +++ b/vsprojects/protoc.vcproj @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vsprojects/readme.txt b/vsprojects/readme.txt new file mode 100644 index 00000000..b79f6006 --- /dev/null +++ b/vsprojects/readme.txt @@ -0,0 +1,76 @@ +This directory contains project files for compiling Protocol Buffers using +MSVC. This is not the recommended way to do Protocol Buffer development -- +we prefer to develop under a Unix-like environment -- but it may be more +accessible to those who primarily work with MSVC. + +Compiling and Installing +======================== + +1) Open protobuf.sln in Microsoft Visual Studio. +2) Choose "Debug" or "Release" configuration as desired.* +3) From the Build menu, choose "Build Solution". Wait for compiling to finish. +4) From a command shell, run tests.exe and check that all tests pass. +5) Run extract_includes.bat to copy all the public headers into a separate + "include" directory (under the top-level package directory). +6) Copy the contents of the include directory to wherever you want to put + headers. +7) Copy protoc.exe and the two DLLs (libprotobuf and libprotoc) wherever you + put build tools. +8) Copy libprotobuf.{lib,dll} and libprotoc.{lib,dll} wherever you put + libraries. + +* To avoid conflicts between the MSVC debug and release runtime libraries, when + compiling a debug build of your application, you must link against a debug + build of libprotobuf.dll. Similarly, release builds must link against + release DLLs. + +DLLs and Distribution +===================== + +When distributing your software to end users, we strongly recommend that you +do NOT install libprotobuf.dll or libprotoc.dll to any shared location. +Instead, keep these libraries next to your binaries, in your application's +own install directory. C++ makes it very difficult to maintain binary +compatibility between releases, so it is likely that future versions of these +libraries will *not* be usable as drop-in replacements. The only reason we +provide these libraries as DLLs rather than static libs is so that a program +which is itself split into multiple DLLs can safely pass protocol buffer +objects between them. + +If your project is itself a DLL intended for use by third-party software, we +recommend that you do NOT expose protocol buffer objects in your library's +public interface, and that you statically link protocol buffers into your +library. + +TODO(kenton): This sounds kind of scary. Maybe we should only provide static + libraries? + +Notes on Compiler Warnings +========================== + +The following warnings have been disabled while building the protobuf libraries +and compiler. You may have to disable some of them in your own project as +well, or live with them. + +C4018 - 'expression' : signed/unsigned mismatch +C4146 - unary minus operator applied to unsigned type, result still unsigned +C4244 - Conversion from 'type1' to 'type2', possible loss of data. +C4251 - 'identifier' : class 'type' needs to have dll-interface to be used by + clients of class 'type2' +C4267 - Conversion from 'size_t' to 'type', possible loss of data. +C4305 - 'identifier' : truncation from 'type1' to 'type2' +C4355 - 'this' : used in base member initializer list +C4800 - 'type' : forcing value to bool 'true' or 'false' (performance warning) +C4996 - 'function': was declared deprecated + +C4251 is of particular note. The protocol buffer library uses templates in +its public interfaces. MSVC does not provide any reasonable way to export +template classes from a DLL. However, in practice, it appears that exporting +templates is not necessary anyway. Since the complete definition of any +template is available in the header files, anyone importing the DLL will just +end up compiling instances of the templates into their own binary. The +Protocol Buffer implementation does not rely on static template members being +unique, so there should be no problem with this, but MSVC prints warning +nevertheless. So, we disable it. Unfortunately, this warning will also be +produced when compiling code which merely uses protocol buffers, meaning you +may have to disable it in your code too. diff --git a/vsprojects/tests.vcproj b/vsprojects/tests.vcproj new file mode 100644 index 00000000..bba50904 --- /dev/null +++ b/vsprojects/tests.vcproj @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +