diff --git a/CMake/PackageDebian.cmake b/CMake/PackageDebian.cmake new file mode 100644 index 0000000000000000000000000000000000000000..ebe893140a971f4036afb3d66e5e39838703bc47 --- /dev/null +++ b/CMake/PackageDebian.cmake @@ -0,0 +1,57 @@ +# ------------------- Debianization --------------------- +if (UNIX) + + # Set build environment + SET(CPACK_GENERATOR "TGZ;DEB") + SET(CPACK_SOURCE_TGZ "ON") + + # Common package information + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY + "FlatBuffers is an efficient cross platform serialization library for C++, with support for Java, C# and Go. It was created at Google specifically for game development and other performance-critical applications.") + SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "https://github.com/google/flatbuffers") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Vitaly Isaev <vitalyisaev2@gmail.com>") + + # Derive package version from git + EXECUTE_PROCESS( + COMMAND date +%Y%m%d + OUTPUT_VARIABLE DATE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + EXECUTE_PROCESS( + COMMAND git describe + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_DESCRIBE_DIRTY + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${GIT_DESCRIBE_DIRTY}") + string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${GIT_DESCRIBE_DIRTY}") + string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${GIT_DESCRIBE_DIRTY}") + string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+\\-([0-9]+).*" "\\1" VERSION_COMMIT "${GIT_DESCRIBE_DIRTY}") + SET(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) + SET(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) + SET(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) + SET(CPACK_PACKAGE_VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_COMMIT}") + SET(CPACK_DEBIAN_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION}") + + # Derive architecture + IF(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE) + FIND_PROGRAM(DPKG_CMD dpkg) + IF(NOT DPKG_CMD) + MESSAGE(STATUS "Can not find dpkg in your path, default to i386.") + SET(CPACK_DEBIAN_PACKAGE_ARCHITECTURE i386) + ENDIF(NOT DPKG_CMD) + EXECUTE_PROCESS(COMMAND "${DPKG_CMD}" --print-architecture + OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF(NOT CPACK_DEBIAN_PACKAGE_ARCHITECTURE) + + # Package name + SET(CPACK_DEBIAN_PACKAGE_NAME "flatbuffers") + SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/LICENSE.txt) + SET(CPACK_PACKAGE_FILE_NAME + "${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") + +endif(UNIX) + +INCLUDE(CPack) diff --git a/CMakeLists.txt b/CMakeLists.txt index 81c9e5837d7af4bc5c4784cfa00ab81a9a197d5e..1af3d3334159823e16fca5511161e0ef8a2ce416 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_fbs.cpp src/idl_gen_grpc.cpp src/flatc.cpp + grpc/src/compiler/schema_interface.h grpc/src/compiler/cpp_generator.h grpc/src/compiler/cpp_generator.cc ) @@ -222,3 +223,7 @@ if(FLATBUFFERS_BUILD_TESTS) endif() include(CMake/BuildFlatBuffers.cmake) + +if(FLATBUFFERS_PACKAGE_DEBIAN) + include(CMake/PackageDebian.cmake) +endif() diff --git a/docs/source/doxyfile b/docs/source/doxyfile index bef63f582770ddc49d98b6c9de189394685d203e..db3eeac7151b52f767543bbdbdb905a8e0005766 100755 --- a/docs/source/doxyfile +++ b/docs/source/doxyfile @@ -761,7 +761,7 @@ INPUT = "FlatBuffers.md" \ "WhitePaper.md" \ "Internals.md" \ "Grammar.md" \ - "CONTRIBUTING.md" \ + "../../CONTRIBUTING.md" \ "Tutorial.md" \ "GoApi.md" \ "groups" \ diff --git a/grpc/src/compiler/cpp_generator.cc b/grpc/src/compiler/cpp_generator.cc index 9319c41934984ab32ac7a0f03eb17fbcdbf0e4f8..e8ad49e70de3ec379725266a51ffd13ee125f565 100644 --- a/grpc/src/compiler/cpp_generator.cc +++ b/grpc/src/compiler/cpp_generator.cc @@ -67,7 +67,8 @@ grpc::string FilenameIdentifier(const grpc::string &filename) { template<class T, size_t N> T *array_end(T (&array)[N]) { return array + N; } -void PrintIncludes(Printer *printer, const std::vector<grpc::string>& headers, const Parameters ¶ms) { +void PrintIncludes(grpc_generator::Printer *printer, + const std::vector<grpc::string>& headers, const Parameters ¶ms) { std::map<grpc::string, grpc::string> vars; vars["l"] = params.use_system_headers ? '<' : '"'; @@ -86,7 +87,7 @@ void PrintIncludes(Printer *printer, const std::vector<grpc::string>& headers, c } } -grpc::string GetHeaderPrologue(File *file, const Parameters & /*params*/) { +grpc::string GetHeaderPrologue(grpc_generator::File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -111,7 +112,7 @@ grpc::string GetHeaderPrologue(File *file, const Parameters & /*params*/) { return output; } -grpc::string GetHeaderIncludes(File *file, +grpc::string GetHeaderIncludes(grpc_generator::File *file, const Parameters ¶ms) { grpc::string output; { @@ -154,7 +155,7 @@ grpc::string GetHeaderIncludes(File *file, } void PrintHeaderClientMethodInterfaces( - Printer *printer, const Method *method, + grpc_generator::Printer *printer, const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars, bool is_public) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -303,8 +304,8 @@ void PrintHeaderClientMethodInterfaces( } } -void PrintHeaderClientMethod(Printer *printer, - const Method *method, +void PrintHeaderClientMethod(grpc_generator::Printer *printer, + const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars, bool is_public) { (*vars)["Method"] = method->name(); @@ -445,13 +446,13 @@ void PrintHeaderClientMethod(Printer *printer, } } -void PrintHeaderClientMethodData(Printer *printer, const Method *method, +void PrintHeaderClientMethodData(grpc_generator::Printer *printer, const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); printer->Print(*vars, "const ::grpc::RpcMethod rpcmethod_$Method$_;\n"); } -void PrintHeaderServerMethodSync(Printer *printer, const Method *method, +void PrintHeaderServerMethodSync(grpc_generator::Printer *printer, const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -483,8 +484,8 @@ void PrintHeaderServerMethodSync(Printer *printer, const Method *method, } void PrintHeaderServerMethodAsync( - Printer *printer, - const Method *method, + grpc_generator::Printer *printer, + const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -599,8 +600,8 @@ void PrintHeaderServerMethodAsync( } void PrintHeaderServerMethodGeneric( - Printer *printer, - const Method *method, + grpc_generator::Printer *printer, + const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -669,8 +670,8 @@ void PrintHeaderServerMethodGeneric( printer->Print(*vars, "};\n"); } -void PrintHeaderService(Printer *printer, - const Service *service, +void PrintHeaderService(grpc_generator::Printer *printer, + const grpc_generator::Service *service, std::map<grpc::string, grpc::string> *vars) { (*vars)["Service"] = service->name(); @@ -764,7 +765,7 @@ void PrintHeaderService(Printer *printer, printer->Print("};\n"); } -grpc::string GetHeaderServices(File *file, +grpc::string GetHeaderServices(grpc_generator::File *file, const Parameters ¶ms) { grpc::string output; { @@ -795,7 +796,7 @@ grpc::string GetHeaderServices(File *file, return output; } -grpc::string GetHeaderEpilogue(File *file, const Parameters & /*params*/) { +grpc::string GetHeaderEpilogue(grpc_generator::File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -821,7 +822,7 @@ grpc::string GetHeaderEpilogue(File *file, const Parameters & /*params*/) { return output; } -grpc::string GetSourcePrologue(File *file, const Parameters & /*params*/) { +grpc::string GetSourcePrologue(grpc_generator::File *file, const Parameters & /*params*/) { grpc::string output; { // Scope the output stream so it closes and finalizes output to the string. @@ -845,7 +846,7 @@ grpc::string GetSourcePrologue(File *file, const Parameters & /*params*/) { return output; } -grpc::string GetSourceIncludes(File *file, +grpc::string GetSourceIncludes(grpc_generator::File *file, const Parameters ¶ms) { grpc::string output; { @@ -880,8 +881,8 @@ grpc::string GetSourceIncludes(File *file, return output; } -void PrintSourceClientMethod(Printer *printer, - const Method *method, +void PrintSourceClientMethod(grpc_generator::Printer *printer, + const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -981,8 +982,8 @@ void PrintSourceClientMethod(Printer *printer, } } -void PrintSourceServerMethod(Printer *printer, - const Method *method, +void PrintSourceServerMethod(grpc_generator::Printer *printer, + const grpc_generator::Method *method, std::map<grpc::string, grpc::string> *vars) { (*vars)["Method"] = method->name(); (*vars)["Request"] = method->input_type_name(); @@ -1040,8 +1041,8 @@ void PrintSourceServerMethod(Printer *printer, } } -void PrintSourceService(Printer *printer, - const Service *service, +void PrintSourceService(grpc_generator::Printer *printer, + const grpc_generator::Service *service, std::map<grpc::string, grpc::string> *vars) { (*vars)["Service"] = service->name(); @@ -1153,7 +1154,7 @@ void PrintSourceService(Printer *printer, } } -grpc::string GetSourceServices(File *file, +grpc::string GetSourceServices(grpc_generator::File *file, const Parameters ¶ms) { grpc::string output; { @@ -1182,7 +1183,7 @@ grpc::string GetSourceServices(File *file, return output; } -grpc::string GetSourceEpilogue(File *file, const Parameters & /*params*/) { +grpc::string GetSourceEpilogue(grpc_generator::File *file, const Parameters & /*params*/) { grpc::string temp; if (!file->package().empty()) { diff --git a/grpc/src/compiler/cpp_generator.h b/grpc/src/compiler/cpp_generator.h index 953ddfd569e0ccb1d2bc39d6dbe5d47b4e6f66eb..a4adee70ecc86a314efbe8026fa184c50f07dfdf 100644 --- a/grpc/src/compiler/cpp_generator.h +++ b/grpc/src/compiler/cpp_generator.h @@ -41,16 +41,7 @@ #include <memory> #include <vector> -#ifndef GRPC_CUSTOM_STRING -#include <string> -#define GRPC_CUSTOM_STRING std::string -#endif - -namespace grpc { - -typedef GRPC_CUSTOM_STRING string; - -} // namespace grpc +#include "src/compiler/schema_interface.h" namespace grpc_cpp_generator { @@ -64,83 +55,29 @@ struct Parameters { grpc::string grpc_search_path; }; -// An abstract interface representing a method. -struct Method { - virtual ~Method() {} - - virtual grpc::string name() const = 0; - - virtual grpc::string input_type_name() const = 0; - virtual grpc::string output_type_name() const = 0; - - virtual bool NoStreaming() const = 0; - virtual bool ClientOnlyStreaming() const = 0; - virtual bool ServerOnlyStreaming() const = 0; - virtual bool BidiStreaming() const = 0; -}; - -// An abstract interface representing a service. -struct Service { - virtual ~Service() {} - - virtual grpc::string name() const = 0; - - virtual int method_count() const = 0; - virtual std::unique_ptr<const Method> method(int i) const = 0; -}; - -struct Printer { - virtual ~Printer() {} - - virtual void Print(const std::map<grpc::string, grpc::string> &vars, - const char *template_string) = 0; - virtual void Print(const char *string) = 0; - virtual void Indent() = 0; - virtual void Outdent() = 0; -}; - -// An interface that allows the source generated to be output using various -// libraries/idls/serializers. -struct File { - virtual ~File() {} - - virtual grpc::string filename() const = 0; - virtual grpc::string filename_without_ext() const = 0; - virtual grpc::string message_header_ext() const = 0; - virtual grpc::string service_header_ext() const = 0; - virtual grpc::string package() const = 0; - virtual std::vector<grpc::string> package_parts() const = 0; - virtual grpc::string additional_headers() const = 0; - - virtual int service_count() const = 0; - virtual std::unique_ptr<const Service> service(int i) const = 0; - - virtual std::unique_ptr<Printer> CreatePrinter(grpc::string *str) const = 0; -}; - // Return the prologue of the generated header file. -grpc::string GetHeaderPrologue(File *file, const Parameters ¶ms); +grpc::string GetHeaderPrologue(grpc_generator::File *file, const Parameters ¶ms); // Return the includes needed for generated header file. -grpc::string GetHeaderIncludes(File *file, const Parameters ¶ms); +grpc::string GetHeaderIncludes(grpc_generator::File *file, const Parameters ¶ms); // Return the includes needed for generated source file. -grpc::string GetSourceIncludes(File *file, const Parameters ¶ms); +grpc::string GetSourceIncludes(grpc_generator::File *file, const Parameters ¶ms); // Return the epilogue of the generated header file. -grpc::string GetHeaderEpilogue(File *file, const Parameters ¶ms); +grpc::string GetHeaderEpilogue(grpc_generator::File *file, const Parameters ¶ms); // Return the prologue of the generated source file. -grpc::string GetSourcePrologue(File *file, const Parameters ¶ms); +grpc::string GetSourcePrologue(grpc_generator::File *file, const Parameters ¶ms); // Return the services for generated header file. -grpc::string GetHeaderServices(File *file, const Parameters ¶ms); +grpc::string GetHeaderServices(grpc_generator::File *file, const Parameters ¶ms); // Return the services for generated source file. -grpc::string GetSourceServices(File *file, const Parameters ¶ms); +grpc::string GetSourceServices(grpc_generator::File *file, const Parameters ¶ms); // Return the epilogue of the generated source file. -grpc::string GetSourceEpilogue(File *file, const Parameters ¶ms); +grpc::string GetSourceEpilogue(grpc_generator::File *file, const Parameters ¶ms); } // namespace grpc_cpp_generator diff --git a/grpc/src/compiler/schema_interface.h b/grpc/src/compiler/schema_interface.h new file mode 100644 index 0000000000000000000000000000000000000000..332a51a54fea82deb7ef21c52b5c5b6d81072e9c --- /dev/null +++ b/grpc/src/compiler/schema_interface.h @@ -0,0 +1,108 @@ +/* + * + * Copyright 2015, 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. + * + */ + +#ifndef GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H +#define GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H + + #include <memory> + #include <vector> + + #ifndef GRPC_CUSTOM_STRING + #include <string> + #define GRPC_CUSTOM_STRING std::string + #endif + +namespace grpc { + + typedef GRPC_CUSTOM_STRING string; + +} // namespace grpc + +namespace grpc_generator { + + // An abstract interface representing a method. + struct Method { + virtual ~Method() {} + + virtual grpc::string name() const = 0; + + virtual grpc::string input_type_name() const = 0; + virtual grpc::string output_type_name() const = 0; + + virtual bool NoStreaming() const = 0; + virtual bool ClientOnlyStreaming() const = 0; + virtual bool ServerOnlyStreaming() const = 0; + virtual bool BidiStreaming() const = 0; + }; + + // An abstract interface representing a service. + struct Service { + virtual ~Service() {} + + virtual grpc::string name() const = 0; + + virtual int method_count() const = 0; + virtual std::unique_ptr<const Method> method(int i) const = 0; + }; + + struct Printer { + virtual ~Printer() {} + + virtual void Print(const std::map<grpc::string, grpc::string> &vars, + const char *template_string) = 0; + virtual void Print(const char *string) = 0; + virtual void Indent() = 0; + virtual void Outdent() = 0; + }; + + // An interface that allows the source generated to be output using various + // libraries/idls/serializers. + struct File { + virtual ~File() {} + + virtual grpc::string filename() const = 0; + virtual grpc::string filename_without_ext() const = 0; + virtual grpc::string message_header_ext() const = 0; + virtual grpc::string service_header_ext() const = 0; + virtual grpc::string package() const = 0; + virtual std::vector<grpc::string> package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; + + virtual int service_count() const = 0; + virtual std::unique_ptr<const Service> service(int i) const = 0; + + virtual std::unique_ptr<Printer> CreatePrinter(grpc::string *str) const = 0; + }; +} // namespace grpc_generator + +#endif // GRPC_INTERNAL_COMPILER_SCHEMA_INTERFACE_H diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 4644f9c600184a1e9f694d167af7ffbe6d464c54..b193b17911222c951028feeb7c04eecba2da7aba 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -1436,11 +1436,6 @@ class Struct FLATBUFFERS_FINAL_CLASS { return ReadScalar<T>(&data_[o]); } - template<typename T> T GetPointer(uoffset_t o) const { - auto p = &data_[o]; - return reinterpret_cast<T>(p + ReadScalar<uoffset_t>(p)); - } - template<typename T> T GetStruct(uoffset_t o) const { return reinterpret_cast<T>(&data_[o]); } diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 5909a4e20ae20d385537fa9507ca036394078ade..1c1e63494cd7a223c72ab1ad6b60463805b676ab 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -596,7 +596,9 @@ extern void GenComment(const std::vector<std::string> &dc, // if it is less than 0, no linefeeds will be generated either. // See idl_gen_text.cpp. // strict_json adds "quotes" around field names if true. -extern void GenerateText(const Parser &parser, +// If the flatbuffer cannot be encoded in JSON (e.g., it contains non-UTF-8 +// byte arrays in String values), returns false. +extern bool GenerateText(const Parser &parser, const void *flatbuffer, std::string *text); extern bool GenerateTextFile(const Parser &parser, diff --git a/java/com/google/flatbuffers/FlatBufferBuilder.java b/java/com/google/flatbuffers/FlatBufferBuilder.java index 7ab24e10d0a575ae7829f0e67d99f497ca1cc944..fd4b729e6693efdd74fc57864cb3c357f8e11b11 100644 --- a/java/com/google/flatbuffers/FlatBufferBuilder.java +++ b/java/com/google/flatbuffers/FlatBufferBuilder.java @@ -367,6 +367,28 @@ public class FlatBufferBuilder { } /// @endcond + /** + * Create a new array/vector and return a ByteBuffer to be filled later. + * Call {@link #endVector} after this method to get an offset to the beginning + * of vector. + * + * @param elem_size the size of each element in bytes. + * @param num_elems number of elements in the vector. + * @param alignment byte alignment. + * @return ByteBuffer with position and limit set to the space allocated for the array. + */ + public ByteBuffer createUnintializedVector(int elem_size, int num_elems, int alignment) { + int length = elem_size * num_elems; + startVector(elem_size, num_elems, alignment); + + bb.position(space -= length); + + // Slice and limit the copy vector to point to the 'array' + ByteBuffer copy = bb.slice().order(ByteOrder.LITTLE_ENDIAN); + copy.limit(length); + return copy; + } + /** * Create a vector of tables. * @@ -438,6 +460,20 @@ public class FlatBufferBuilder { return endVector(); } + /** + * Create a byte array in the buffer. + * + * @param arr A source array with data + * @return The offset in the buffer where the encoded array starts. + */ + public int createByteVector(byte[] arr) { + int length = arr.length; + startVector(1, length, 1); + bb.position(space -= length); + bb.put(arr); + return endVector(); + } + /// @cond FLATBUFFERS_INTERNAL /** * Should not be accessing the final buffer before it is finished. diff --git a/java/com/google/flatbuffers/Table.java b/java/com/google/flatbuffers/Table.java index c9c6545616a7ee79db4080c4f12e6e66659956ad..b853842a77cd2311ae13b0a3d44437fd9c2da0ca 100644 --- a/java/com/google/flatbuffers/Table.java +++ b/java/com/google/flatbuffers/Table.java @@ -37,6 +37,12 @@ public class Table { return Charset.forName("UTF-8").newDecoder(); } }; + public final static ThreadLocal<Charset> UTF8_CHARSET = new ThreadLocal<Charset>() { + @Override + protected Charset initialValue() { + return Charset.forName("UTF-8"); + } + }; private final static ThreadLocal<CharBuffer> CHAR_BUFFER = new ThreadLocal<CharBuffer>(); /** Used to hold the position of the `bb` buffer. */ protected int bb_pos; @@ -75,7 +81,7 @@ public class Table { protected int __indirect(int offset) { return offset + bb.getInt(offset); } - + protected static int __indirect(int offset, ByteBuffer bb) { return offset + bb.getInt(offset); } @@ -197,17 +203,21 @@ public class Table { } return true; } - + /** * Sort tables by the key. * * @param offsets An 'int' indexes of the tables into the bb. * @param bb A {@code ByteBuffer} to get the tables. */ - protected void sortTables(int[] offsets, ByteBuffer bb) { + protected void sortTables(int[] offsets, final ByteBuffer bb) { Integer[] off = new Integer[offsets.length]; for (int i = 0; i < offsets.length; i++) off[i] = offsets[i]; - Arrays.sort(off, (Integer o1, Integer o2) -> keysCompare(o1, o2, bb)); + java.util.Arrays.sort(off, new java.util.Comparator<Integer>() { + public int compare(Integer o1, Integer o2) { + return keysCompare(o1, o2, bb); + } + }); for (int i = 0; i < offsets.length; i++) offsets[i] = off[i]; } @@ -219,7 +229,7 @@ public class Table { * @param bb A {@code ByteBuffer} to get the keys. */ protected int keysCompare(Integer o1, Integer o2, ByteBuffer bb) { return 0; } - + /** * Compare two strings in the buffer. * @@ -242,7 +252,7 @@ public class Table { } return len_1 - len_2; } - + /** * Compare string from the buffer with the 'String' object. * diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs index 5fa1ac7a51d0603b638565615b711659ebf6c5dd..37a2c7e6ed3800153632424310cc44c0ebdf33ff 100755 --- a/net/FlatBuffers/ByteBuffer.cs +++ b/net/FlatBuffers/ByteBuffer.cs @@ -14,7 +14,20 @@ * limitations under the License. */ -//#define UNSAFE_BYTEBUFFER // uncomment this line to use faster ByteBuffer +// There are 2 #defines that have an impact on performance of this ByteBuffer implementation +// +// UNSAFE_BYTEBUFFER +// This will use unsafe code to manipulate the underlying byte array. This +// can yield a reasonable performance increase. +// +// BYTEBUFFER_NO_BOUNDS_CHECK +// This will disable the bounds check asserts to the byte array. This can +// yield a small performance gain in normal code.. +// +// Using UNSAFE_BYTEBUFFER and BYTEBUFFER_NO_BOUNDS_CHECK together can yield a +// performance gain of ~15% for some operations, however doing so is potentially +// dangerous. Do so at your own risk! +// using System; @@ -22,9 +35,6 @@ namespace FlatBuffers { /// <summary> /// Class to mimic Java's ByteBuffer which is used heavily in Flatbuffers. - /// If your execution environment allows unsafe code, you should enable - /// unsafe code in your project and #define UNSAFE_BYTEBUFFER to use a - /// MUCH faster version of ByteBuffer. /// </summary> public class ByteBuffer { @@ -126,11 +136,14 @@ namespace FlatBuffers } #endif // !UNSAFE_BYTEBUFFER + private void AssertOffsetAndLength(int offset, int length) { + #if !BYTEBUFFER_NO_BOUNDS_CHECK if (offset < 0 || offset > _buffer.Length - length) throw new ArgumentOutOfRangeException(); + #endif } public void PutSbyte(int offset, sbyte value) @@ -200,7 +213,6 @@ namespace FlatBuffers public unsafe void PutUlong(int offset, ulong value) { AssertOffsetAndLength(offset, sizeof(ulong)); - fixed (byte* ptr = _buffer) { *(ulong*)(ptr + offset) = BitConverter.IsLittleEndian diff --git a/pom.xml b/pom.xml index 8e32feec3310b1eafd2bebdb453a626010bcc9b2..86d7f5329ca3d9e3becc49e381e127765a0afc93 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.google.flatbuffers</groupId> <artifactId>flatbuffers-java</artifactId> - <version>1.3.0-SNAPSHOT</version> + <version>1.4.0-SNAPSHOT</version> <packaging>bundle</packaging> <name>FlatBuffers Java API</name> <description> diff --git a/samples/sample_text.cpp b/samples/sample_text.cpp index 557077d4bcfa5b99a823cddc1244a0df5dd8bc2b..d851120d1785db78930d59876816533b339ed7b8 100644 --- a/samples/sample_text.cpp +++ b/samples/sample_text.cpp @@ -46,7 +46,10 @@ int main(int /*argc*/, const char * /*argv*/[]) { // to ensure it is correct, we now generate text back from the binary, // and compare the two: std::string jsongen; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen)) { + printf("Couldn't serialize parsed data to JSON!\n"); + return 1; + } if (jsongen != jsonfile) { printf("%s----------------\n%s", jsongen.c_str(), jsonfile.c_str()); diff --git a/src/flatc.cpp b/src/flatc.cpp index 44ce91350ae971caa277837088e90c38ca8c11c4..5ca7ab3b512bdf0e5950bee2ee02a4fc5e5a0fea 100644 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -19,7 +19,7 @@ #include "flatbuffers/util.h" #include <limits> -#define FLATC_VERSION "1.3.0 (" __DATE__ ")" +#define FLATC_VERSION "1.4.0 (" __DATE__ ")" static void Error(const std::string &err, bool usage = false, bool show_exe_name = true); @@ -243,7 +243,7 @@ int main(int argc, const char *argv[]) { goto found; } } - Error("unknown commandline argument" + arg, true); + Error("unknown commandline argument: " + arg, true); found:; } } else { diff --git a/src/idl_gen_cpp.cpp b/src/idl_gen_cpp.cpp index edb14cc6bf97e59e961d94ef0d440a117a5250e6..74a6bf9a0c4fc8695cfe7c6acf71c1c20c22abc3 100644 --- a/src/idl_gen_cpp.cpp +++ b/src/idl_gen_cpp.cpp @@ -333,15 +333,15 @@ class CppGenerator : public BaseGenerator { return (opts.scoped_enums ? "enum class " : "enum ") + enum_def.name; } - static std::string GenEnumVal(const EnumDef &enum_def, - const std::string &enum_val, - const IDLOptions &opts) { + static std::string GenEnumValDecl(const EnumDef &enum_def, + const std::string &enum_val, + const IDLOptions &opts) { return opts.prefixed_enums ? enum_def.name + "_" + enum_val : enum_val; } - static std::string GetEnumVal(const EnumDef &enum_def, - const EnumVal &enum_val, - const IDLOptions &opts) { + static std::string GetEnumValUse(const EnumDef &enum_def, + const EnumVal &enum_val, + const IDLOptions &opts) { if (opts.scoped_enums) { return enum_def.name + "::" + enum_val.name; } else if (opts.prefixed_enums) { @@ -396,7 +396,7 @@ class CppGenerator : public BaseGenerator { ++it) { auto &ev = **it; GenComment(ev.doc_comment, code_ptr, nullptr, " "); - code += " " + GenEnumVal(enum_def, ev.name, parser_.opts) + " = "; + code += " " + GenEnumValDecl(enum_def, ev.name, parser_.opts) + " = "; code += NumToString(ev.value) + ",\n"; minv = !minv || minv->value > ev.value ? &ev : minv; maxv = !maxv || maxv->value < ev.value ? &ev : maxv; @@ -406,15 +406,15 @@ class CppGenerator : public BaseGenerator { assert(minv && maxv); if (enum_def.attributes.Lookup("bit_flags")) { if (minv->value != 0) // If the user didn't defined NONE value - code += " " + GenEnumVal(enum_def, "NONE", parser_.opts) + " = 0,\n"; + code += " " + GenEnumValDecl(enum_def, "NONE", parser_.opts) + " = 0,\n"; if (maxv->value != anyv) // If the user didn't defined ANY value - code += " " + GenEnumVal(enum_def, "ANY", parser_.opts) + " = " + + code += " " + GenEnumValDecl(enum_def, "ANY", parser_.opts) + " = " + NumToString(anyv) + "\n"; } else { // MIN & MAX are useless for bit_flags - code += " " + GenEnumVal(enum_def, "MIN", parser_.opts) + " = "; - code += GenEnumVal(enum_def, minv->name, parser_.opts) + ",\n"; - code += " " + GenEnumVal(enum_def, "MAX", parser_.opts) + " = "; - code += GenEnumVal(enum_def, maxv->name, parser_.opts) + "\n"; + code += " " + GenEnumValDecl(enum_def, "MIN", parser_.opts) + " = "; + code += GenEnumValDecl(enum_def, minv->name, parser_.opts) + ",\n"; + code += " " + GenEnumValDecl(enum_def, "MAX", parser_.opts) + " = "; + code += GenEnumValDecl(enum_def, maxv->name, parser_.opts) + "\n"; } } code += "};\n"; @@ -429,7 +429,7 @@ class CppGenerator : public BaseGenerator { code += " " + enum_def.name + " type;\n\n"; code += " flatbuffers::NativeTable *table;\n"; code += " " + enum_def.name + "Union() : type("; - code += GenEnumVal(enum_def, "NONE", parser_.opts); + code += GetEnumValUse(enum_def, *enum_def.vals.Lookup("NONE"), parser_.opts); code += "), table(nullptr) {}\n"; code += " " + enum_def.name + "Union(const "; code += enum_def.name + "Union &);\n"; @@ -445,7 +445,7 @@ class CppGenerator : public BaseGenerator { auto native_name = NativeName(WrapInNameSpace(*ev.struct_def)); code += " " + native_name + " *As"; code += ev.name + "() { return type == "; - code += GetEnumVal(enum_def, ev, parser_.opts); + code += GetEnumValUse(enum_def, ev, parser_.opts); code += " ? reinterpret_cast<" + native_name; code += " *>(table) : nullptr; }\n"; } @@ -478,7 +478,7 @@ class CppGenerator : public BaseGenerator { code += "()[static_cast<int>(e)"; if (enum_def.vals.vec.front()->value) { code += " - static_cast<int>("; - code += GetEnumVal(enum_def, *enum_def.vals.vec.front(), parser_.opts) + + code += GetEnumValUse(enum_def, *enum_def.vals.vec.front(), parser_.opts) + ")"; } code += "]; }\n\n"; @@ -500,7 +500,7 @@ class CppGenerator : public BaseGenerator { for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - code += " case " + GetEnumVal(enum_def, ev, parser_.opts); + code += " case " + GetEnumValUse(enum_def, ev, parser_.opts); if (!ev.value) { code += ": return true;\n"; // "NONE" enum value. } else { @@ -518,7 +518,7 @@ class CppGenerator : public BaseGenerator { for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - code += " case " + GetEnumVal(enum_def, ev, parser_.opts); + code += " case " + GetEnumValUse(enum_def, ev, parser_.opts); if (!ev.value) { code += ": return nullptr;\n"; // "NONE" enum value. } else { @@ -533,7 +533,7 @@ class CppGenerator : public BaseGenerator { for (auto it = enum_def.vals.vec.begin(); it != enum_def.vals.vec.end(); ++it) { auto &ev = **it; - code += " case " + GetEnumVal(enum_def, ev, parser_.opts); + code += " case " + GetEnumValUse(enum_def, ev, parser_.opts); if (!ev.value) { code += ": return 0;\n"; // "NONE" enum value. } else { @@ -553,7 +553,7 @@ class CppGenerator : public BaseGenerator { ++it) { auto &ev = **it; if (ev.value) { - code += " case " + GenEnumVal(enum_def, ev.name, parser_.opts); + code += " case " + GetEnumValUse(enum_def, ev, parser_.opts); code += ": delete reinterpret_cast<"; code += NativeName(WrapInNameSpace(*ev.struct_def)); code += " *>(table); break;\n"; @@ -613,7 +613,7 @@ class CppGenerator : public BaseGenerator { if (ev) { code += WrapInNameSpace( field.value.type.enum_def->defined_namespace, - GetEnumVal(*field.value.type.enum_def, *ev, parser_.opts)); + GetEnumValUse(*field.value.type.enum_def, *ev, parser_.opts)); } else { code += GenUnderlyingCast(field, true, field.value.constant); } diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index 88ec0db2555f94af119edbd3b8c3648e2e8ade83..274a22f5e36e3e164dd2ada9fae8041c4a6030c5 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -237,7 +237,7 @@ class GeneralGenerator : public BaseGenerator { // Save out the generated code for a single class while adding // declaration boilerplate. bool SaveType(const std::string &defname, const Namespace &ns, - const std::string &classcode, bool needs_includes) { + const std::string &classcode, bool needs_includes) { if (!classcode.length()) return true; std::string code; @@ -684,8 +684,7 @@ std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, const char *num = key_offset += num; key_offset += (lang_.language == IDLOptions::kCSharp ? ".Value, builder.DataBuffer)" : ", _bb)"); - } - else { + } else { key_offset += GenByteBufferLength("bb"); key_offset += " - tableOffset, bb)"; } @@ -694,23 +693,21 @@ std::string GenOffsetGetter(flatbuffers::FieldDef *key_field, const char *num = std::string GenLookupKeyGetter(flatbuffers::FieldDef *key_field) { std::string key_getter = " "; - key_getter += "tableOffset = __indirect(vectorLocation + 4 * (start + middle)"; + key_getter += "int tableOffset = __indirect(vectorLocation + 4 * (start + middle)"; key_getter += ", bb);\n "; if (key_field->value.type.base_type == BASE_TYPE_STRING) { - key_getter += "comp = " + FunctionStart('C') + "ompareStrings("; + key_getter += "int comp = " + FunctionStart('C') + "ompareStrings("; key_getter += GenOffsetGetter(key_field); key_getter += ", byteKey, bb);\n"; - } - else { + } else { auto get_val = GenGetter(key_field->value.type) + "(" + GenOffsetGetter(key_field) + ")"; if (lang_.language == IDLOptions::kCSharp) { - key_getter += "comp = " + get_val + ".CompareTo(key);\n"; - } - else { + key_getter += "int comp = " + get_val + ".CompateTo(key);\n"; + } else { key_getter += GenTypeGet(key_field->value.type) + " val = "; key_getter += get_val + ";\n"; - key_getter += " comp = val > key ? 1 : val < key ? -1 : 0;\n"; + key_getter += " int comp = val > key ? 1 : val < key ? -1 : 0;\n"; } } return key_getter; @@ -1215,8 +1212,7 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "); }\n"; } } - if (struct_def.has_key && (lang_.language == IDLOptions::kJava || - lang_.language == IDLOptions::kCSharp)) { + if (struct_def.has_key) { if (lang_.language == IDLOptions::kJava) { code += "\n @Override\n protected int keysCompare("; code += "Integer o1, Integer o2, ByteBuffer _bb) {"; @@ -1238,32 +1234,31 @@ void GenStruct(StructDef &struct_def, std::string *code_ptr) { code += "ookupByKey(" + GenVectorOffsetType(); code += " vectorOffset, " + GenTypeGet(key_field->value.type); code += " key, ByteBuffer bb) {\n"; - if (key_field->value.type.base_type == BASE_TYPE_STRING) { - code += " byte[] byteKey = "; - if (lang_.language == IDLOptions::kJava) - code += "key.getBytes(java.nio.charset.StandardCharsets.UTF_8);\n"; - else - code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; - } + code += " byte[] byteKey = "; + if (lang_.language == IDLOptions::kJava) + code += "key.getBytes(Table.UTF8_CHARSET.get());\n"; + else + code += "System.Text.Encoding.UTF8.GetBytes(key);\n"; code += " int vectorLocation = " + GenByteBufferLength("bb"); code += " - vectorOffset"; if (lang_.language == IDLOptions::kCSharp) code += ".Value"; code += ";\n int span = "; - code += "bb." + FunctionStart('G') + "etInt(vectorLocation), "; - code += "middle, start = 0, comp, tableOffset; \n"; + code += "bb." + FunctionStart('G') + "etInt(vectorLocation);\n"; + code += " int start = 0;\n"; code += " vectorLocation += 4;\n"; code += " while (span != 0) {\n"; - code += " middle = span / 2;\n"; + code += " int middle = span / 2;\n"; code += GenLookupKeyGetter(key_field); - code += " if (comp > 0) span = middle;\n"; - code += " else if (comp < 0) {\n"; + code += " if (comp > 0) {\n"; + code += " span = middle;\n"; + code += " } else if (comp < 0) {\n"; code += " middle++;\n"; code += " start += middle;\n"; code += " span -= middle;\n"; - code += " }\n"; - code += " else return new " + struct_def.name; + code += " } else {\n"; + code += " return new " + struct_def.name; code += "().__init(tableOffset, bb);\n"; - code += " }\n"; + code += " }\n }\n"; code += " return null;\n"; code += " }\n"; } diff --git a/src/idl_gen_go.cpp b/src/idl_gen_go.cpp index 573300980dad3be60ee5ceabfb7061c81b7c91a7..2df8c7b62314f1ae1a1396f5cafa08a7f97472ac 100644 --- a/src/idl_gen_go.cpp +++ b/src/idl_gen_go.cpp @@ -288,9 +288,6 @@ static void GetMemberOfVectorOfStruct(const StructDef &struct_def, if (!(vectortype.struct_def->fixed)) { code += "\t\tx = rcv._tab.Indirect(x)\n"; } - code += "\t\tif obj == nil {\n"; - code += "\t\t\tobj = new(" + TypeName(field) + ")\n"; - code += "\t\t}\n"; code += "\t\tobj.Init(rcv._tab.Bytes, x)\n"; code += "\t\treturn true\n\t}\n"; code += "\treturn false\n"; diff --git a/src/idl_gen_grpc.cpp b/src/idl_gen_grpc.cpp index 6ada3e87394d77123f215e9c74e1607c7c5d1638..9bcd5bcfa5f32a78b1ead204114ddd1cf160dfa4 100644 --- a/src/idl_gen_grpc.cpp +++ b/src/idl_gen_grpc.cpp @@ -24,7 +24,7 @@ namespace flatbuffers { -class FlatBufMethod : public grpc_cpp_generator::Method { +class FlatBufMethod : public grpc_generator::Method { public: enum Streaming { kNone, kClient, kServer, kBiDi }; @@ -62,7 +62,7 @@ class FlatBufMethod : public grpc_cpp_generator::Method { Streaming streaming_; }; -class FlatBufService : public grpc_cpp_generator::Service { +class FlatBufService : public grpc_generator::Service { public: FlatBufService(const ServiceDef *service) : service_(service) {} @@ -72,8 +72,8 @@ class FlatBufService : public grpc_cpp_generator::Service { return static_cast<int>(service_->calls.vec.size()); }; - std::unique_ptr<const grpc_cpp_generator::Method> method(int i) const { - return std::unique_ptr<const grpc_cpp_generator::Method>( + std::unique_ptr<const grpc_generator::Method> method(int i) const { + return std::unique_ptr<const grpc_generator::Method>( new FlatBufMethod(service_->calls.vec[i])); }; @@ -81,7 +81,7 @@ class FlatBufService : public grpc_cpp_generator::Service { const ServiceDef *service_; }; -class FlatBufPrinter : public grpc_cpp_generator::Printer { +class FlatBufPrinter : public grpc_generator::Printer { public: FlatBufPrinter(std::string *str) : str_(str), escape_char_('$'), indent_(0) {} @@ -133,7 +133,7 @@ class FlatBufPrinter : public grpc_cpp_generator::Printer { int indent_; }; -class FlatBufFile : public grpc_cpp_generator::File { +class FlatBufFile : public grpc_generator::File { public: FlatBufFile(const Parser &parser, const std::string &file_name) : parser_(parser), file_name_(file_name) {} @@ -163,13 +163,13 @@ class FlatBufFile : public grpc_cpp_generator::File { return static_cast<int>(parser_.services_.vec.size()); }; - std::unique_ptr<const grpc_cpp_generator::Service> service(int i) const { - return std::unique_ptr<const grpc_cpp_generator::Service> ( + std::unique_ptr<const grpc_generator::Service> service(int i) const { + return std::unique_ptr<const grpc_generator::Service> ( new FlatBufService(parser_.services_.vec[i])); } - std::unique_ptr<grpc_cpp_generator::Printer> CreatePrinter(std::string *str) const { - return std::unique_ptr<grpc_cpp_generator::Printer>( + std::unique_ptr<grpc_generator::Printer> CreatePrinter(std::string *str) const { + return std::unique_ptr<grpc_generator::Printer>( new FlatBufPrinter(str)); } diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 3e41a0a7683bb4803e1763fbeff4b74e2903c62c..4ff13c8a961138e87ff0547c2808183c9eddbf88 100644 --- a/src/idl_gen_text.cpp +++ b/src/idl_gen_text.cpp @@ -22,7 +22,7 @@ namespace flatbuffers { -static void GenStruct(const StructDef &struct_def, const Table *table, +static bool GenStruct(const StructDef &struct_def, const Table *table, int indent, const IDLOptions &opts, std::string *_text); @@ -48,7 +48,7 @@ void OutputIdentifier(const std::string &name, const IDLOptions &opts, // Print (and its template specialization below for pointers) generate text // for a single FlatBuffer value into JSON format. // The general case for scalars: -template<typename T> void Print(T val, Type type, int /*indent*/, +template<typename T> bool Print(T val, Type type, int /*indent*/, StructDef * /*union_sd*/, const IDLOptions &opts, std::string *_text) { @@ -57,7 +57,7 @@ template<typename T> void Print(T val, Type type, int /*indent*/, auto enum_val = type.enum_def->ReverseLookup(static_cast<int>(val)); if (enum_val) { OutputIdentifier(enum_val->name, opts, _text); - return; + return true; } } @@ -66,10 +66,12 @@ template<typename T> void Print(T val, Type type, int /*indent*/, } else { text += NumToString(val); } + + return true; } // Print a vector a sequence of JSON values, comma separated, wrapped in "[]". -template<typename T> void PrintVector(const Vector<T> &v, Type type, +template<typename T> bool PrintVector(const Vector<T> &v, Type type, int indent, const IDLOptions &opts, std::string *_text) { std::string &text = *_text; @@ -81,19 +83,25 @@ template<typename T> void PrintVector(const Vector<T> &v, Type type, text += NewLine(opts); } text.append(indent + Indent(opts), ' '); - if (IsStruct(type)) - Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type, - indent + Indent(opts), nullptr, opts, _text); - else - Print(v[i], type, indent + Indent(opts), nullptr, - opts, _text); + if (IsStruct(type)) { + if (!Print(v.GetStructFromOffset(i * type.struct_def->bytesize), type, + indent + Indent(opts), nullptr, opts, _text)) { + return false; + } + } else { + if (!Print(v[i], type, indent + Indent(opts), nullptr, + opts, _text)) { + return false; + } + } } text += NewLine(opts); text.append(indent, ' '); text += "]"; + return true; } -static void EscapeString(const String &s, std::string *_text, const IDLOptions& opts) { +static bool EscapeString(const String &s, std::string *_text, const IDLOptions& opts) { std::string &text = *_text; text += "\""; for (uoffset_t i = 0; i < s.size(); i++) { @@ -118,9 +126,19 @@ static void EscapeString(const String &s, std::string *_text, const IDLOptions& text += "\\x"; text += IntToStringHex(static_cast<uint8_t>(c), 2); } else { - // We previously checked for non-UTF-8 and returned a parse error, - // so we shouldn't reach here. - assert(0); + // There are two cases here: + // + // 1) We reached here by parsing an IDL file. In that case, + // we previously checked for non-UTF-8, so we shouldn't reach + // here. + // + // 2) We reached here by someone calling GenerateText() + // on a previously-serialized flatbuffer. The data might have + // non-UTF-8 Strings, or might be corrupt. + // + // In both cases, we have to give up and inform the caller + // they have no JSON. + return false; } } else { if (ucc <= 0xFFFF) { @@ -145,10 +163,11 @@ static void EscapeString(const String &s, std::string *_text, const IDLOptions& } } text += "\""; + return true; } // Specialization of Print above for pointer types. -template<> void Print<const void *>(const void *val, +template<> bool Print<const void *>(const void *val, Type type, int indent, StructDef *union_sd, const IDLOptions &opts, @@ -158,21 +177,27 @@ template<> void Print<const void *>(const void *val, // If this assert hits, you have an corrupt buffer, a union type field // was not present or was out of range. assert(union_sd); - GenStruct(*union_sd, - reinterpret_cast<const Table *>(val), - indent, - opts, - _text); + if (!GenStruct(*union_sd, + reinterpret_cast<const Table *>(val), + indent, + opts, + _text)) { + return false; + } break; case BASE_TYPE_STRUCT: - GenStruct(*type.struct_def, - reinterpret_cast<const Table *>(val), - indent, - opts, - _text); + if (!GenStruct(*type.struct_def, + reinterpret_cast<const Table *>(val), + indent, + opts, + _text)) { + return false; + } break; case BASE_TYPE_STRING: { - EscapeString(*reinterpret_cast<const String *>(val), _text, opts); + if (!EscapeString(*reinterpret_cast<const String *>(val), _text, opts)) { + return false; + } break; } case BASE_TYPE_VECTOR: @@ -182,31 +207,35 @@ template<> void Print<const void *>(const void *val, #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ PTYPE) \ case BASE_TYPE_ ## ENUM: \ - PrintVector<CTYPE>( \ - *reinterpret_cast<const Vector<CTYPE> *>(val), \ - type, indent, opts, _text); break; + if (!PrintVector<CTYPE>( \ + *reinterpret_cast<const Vector<CTYPE> *>(val), \ + type, indent, opts, _text)) { \ + return false; \ + } \ + break; FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) #undef FLATBUFFERS_TD } break; default: assert(0); } + return true; } // Generate text for a scalar field. -template<typename T> static void GenField(const FieldDef &fd, +template<typename T> static bool GenField(const FieldDef &fd, const Table *table, bool fixed, const IDLOptions &opts, int indent, std::string *_text) { - Print(fixed ? + return Print(fixed ? reinterpret_cast<const Struct *>(table)->GetField<T>(fd.value.offset) : table->GetField<T>(fd.value.offset, 0), fd.value.type, indent, nullptr, opts, _text); } // Generate text for non-scalar field. -static void GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, +static bool GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, int indent, StructDef *union_sd, const IDLOptions &opts, std::string *_text) { const void *val = nullptr; @@ -220,12 +249,12 @@ static void GenFieldOffset(const FieldDef &fd, const Table *table, bool fixed, ? table->GetStruct<const void *>(fd.value.offset) : table->GetPointer<const void *>(fd.value.offset); } - Print(val, fd.value.type, indent, union_sd, opts, _text); + return Print(val, fd.value.type, indent, union_sd, opts, _text); } // Generate text for a struct or table, values separated by commas, indented, // and bracketed by "{}" -static void GenStruct(const StructDef &struct_def, const Table *table, +static bool GenStruct(const StructDef &struct_def, const Table *table, int indent, const IDLOptions &opts, std::string *_text) { std::string &text = *_text; @@ -253,8 +282,10 @@ static void GenStruct(const StructDef &struct_def, const Table *table, #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ PTYPE) \ case BASE_TYPE_ ## ENUM: \ - GenField<CTYPE>(fd, table, struct_def.fixed, \ - opts, indent + Indent(opts), _text); \ + if (!GenField<CTYPE>(fd, table, struct_def.fixed, \ + opts, indent + Indent(opts), _text)) { \ + return false; \ + } \ break; FLATBUFFERS_GEN_TYPES_SCALAR(FLATBUFFERS_TD) #undef FLATBUFFERS_TD @@ -264,8 +295,10 @@ static void GenStruct(const StructDef &struct_def, const Table *table, case BASE_TYPE_ ## ENUM: FLATBUFFERS_GEN_TYPES_POINTER(FLATBUFFERS_TD) #undef FLATBUFFERS_TD - GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts), - union_sd, opts, _text); + if (!GenFieldOffset(fd, table, struct_def.fixed, indent + Indent(opts), + union_sd, opts, _text)) { + return false; + } break; } if (fd.value.type.base_type == BASE_TYPE_UTYPE) { @@ -284,20 +317,24 @@ static void GenStruct(const StructDef &struct_def, const Table *table, text += NewLine(opts); text.append(indent, ' '); text += "}"; + return true; } // Generate a text representation of a flatbuffer in JSON format. -void GenerateText(const Parser &parser, const void *flatbuffer, +bool GenerateText(const Parser &parser, const void *flatbuffer, std::string *_text) { std::string &text = *_text; assert(parser.root_struct_def_); // call SetRootType() text.reserve(1024); // Reduce amount of inevitable reallocs. - GenStruct(*parser.root_struct_def_, - GetRoot<Table>(flatbuffer), - 0, - parser.opts, - _text); + if (!GenStruct(*parser.root_struct_def_, + GetRoot<Table>(flatbuffer), + 0, + parser.opts, + _text)) { + return false; + } text += NewLine(parser.opts); + return true; } std::string TextFileName(const std::string &path, @@ -310,7 +347,9 @@ bool GenerateTextFile(const Parser &parser, const std::string &file_name) { if (!parser.builder_.GetSize() || !parser.root_struct_def_) return true; std::string text; - GenerateText(parser, parser.builder_.GetBufferPointer(), &text); + if (!GenerateText(parser, parser.builder_.GetBufferPointer(), &text)) { + return false; + } return flatbuffers::SaveFile(TextFileName(path, file_name).c_str(), text, false); diff --git a/tests/FlatBuffers.Test/ByteBufferTests.cs b/tests/FlatBuffers.Test/ByteBufferTests.cs index b86c36587b2244d34d997be1b2a09b05efaa27c0..3324f12a3c23b3b1d3a189502fbf9b84c738d416 100644 --- a/tests/FlatBuffers.Test/ByteBufferTests.cs +++ b/tests/FlatBuffers.Test/ByteBufferTests.cs @@ -40,6 +40,7 @@ namespace FlatBuffers.Test Assert.AreEqual((byte)99, buffer[0]); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_PutByteCannotPutAtOffsetPastLength() { @@ -47,6 +48,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.PutByte(1, 99)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_PutShortPopulatesBufferCorrectly() @@ -60,6 +62,7 @@ namespace FlatBuffers.Test Assert.AreEqual((byte)0, buffer[1]); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_PutShortCannotPutAtOffsetPastLength() { @@ -67,7 +70,9 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.PutShort(2, 99)); } +#endif +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_PutShortChecksLength() { @@ -83,6 +88,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.PutShort(1, 99)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_PutIntPopulatesBufferCorrectly() @@ -98,6 +104,7 @@ namespace FlatBuffers.Test Assert.AreEqual(0x0A, buffer[3]); } + #if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_PutIntCannotPutAtOffsetPastLength() { @@ -121,6 +128,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.PutInt(2, 0x0A0B0C0D)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_PutLongPopulatesBufferCorrectly() @@ -140,6 +148,7 @@ namespace FlatBuffers.Test Assert.AreEqual(0x01, buffer[7]); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_PutLongCannotPutAtOffsetPastLength() { @@ -163,6 +172,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.PutLong(2, 0x010203040A0B0C0D)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_GetByteReturnsCorrectData() @@ -173,6 +183,7 @@ namespace FlatBuffers.Test Assert.AreEqual((byte)99, uut.Get(0)); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_GetByteChecksOffset() { @@ -180,6 +191,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(()=>uut.Get(1)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_GetShortReturnsCorrectData() @@ -191,6 +203,7 @@ namespace FlatBuffers.Test Assert.AreEqual(1, uut.GetShort(0)); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_GetShortChecksOffset() { @@ -206,6 +219,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.GetShort(1)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_GetIntReturnsCorrectData() @@ -219,6 +233,7 @@ namespace FlatBuffers.Test Assert.AreEqual(0x0A0B0C0D, uut.GetInt(0)); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_GetIntChecksOffset() { @@ -234,6 +249,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.GetInt(0)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_GetLongReturnsCorrectData() @@ -251,6 +267,7 @@ namespace FlatBuffers.Test Assert.AreEqual(0x010203040A0B0C0D, uut.GetLong(0)); } +#if !BYTEBUFFER_NO_BOUNDS_CHECK [FlatBuffersTestMethod] public void ByteBuffer_GetLongChecksOffset() { @@ -266,6 +283,7 @@ namespace FlatBuffers.Test var uut = new ByteBuffer(buffer); Assert.Throws<ArgumentOutOfRangeException>(() => uut.GetLong(0)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_ReverseBytesUshort() diff --git a/tests/JavaTest.java b/tests/JavaTest.java index 9f46f3fae4ce3a30f1d0db1ddfca4733d7c85e49..d53e97327ae0a10bf619ab3555905a18d8903712 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -185,6 +185,10 @@ class JavaTest { TestNestedFlatBuffer(); + TestCreateByteVector(); + + TestCreateUninitializedVector(); + System.out.println("FlatBuffers test: completed successfully"); } @@ -305,6 +309,44 @@ class JavaTest { TestEq(nestedMonsterName, nestedMonster.name()); } + static void TestCreateByteVector() { + FlatBufferBuilder fbb = new FlatBufferBuilder(16); + int str = fbb.createString("MyMonster"); + byte[] inventory = new byte[] { 0, 1, 2, 3, 4 }; + int vec = fbb.createByteVector(inventory); + Monster.startMonster(fbb); + Monster.addInventory(fbb, vec); + Monster.addName(fbb, str); + int monster1 = Monster.endMonster(fbb); + Monster.finishMonsterBuffer(fbb, monster1); + Monster monsterObject = Monster.getRootAsMonster(fbb.dataBuffer()); + + TestEq(monsterObject.inventory(1), (int)inventory[1]); + TestEq(monsterObject.inventoryLength(), inventory.length); + TestEq(ByteBuffer.wrap(inventory), monsterObject.inventoryAsByteBuffer()); + } + + static void TestCreateUninitializedVector() { + FlatBufferBuilder fbb = new FlatBufferBuilder(16); + int str = fbb.createString("MyMonster"); + byte[] inventory = new byte[] { 0, 1, 2, 3, 4 }; + ByteBuffer bb = fbb.createUnintializedVector(1, inventory.length, 1); + for (byte i:inventory) { + bb.put(i); + } + int vec = fbb.endVector(); + Monster.startMonster(fbb); + Monster.addInventory(fbb, vec); + Monster.addName(fbb, str); + int monster1 = Monster.endMonster(fbb); + Monster.finishMonsterBuffer(fbb, monster1); + Monster monsterObject = Monster.getRootAsMonster(fbb.dataBuffer()); + + TestEq(monsterObject.inventory(1), (int)inventory[1]); + TestEq(monsterObject.inventoryLength(), inventory.length); + TestEq(ByteBuffer.wrap(inventory), monsterObject.inventoryAsByteBuffer()); + } + static <T> void TestEq(T a, T b) { if (!a.equals(b)) { System.out.println("" + a.getClass().getName() + " " + b.getClass().getName()); diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index 95a303cf414fc0f39872a96ee8c412fa451998ba..1883659f6b36b345a731b9af00442812b47bdf7b 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -129,7 +129,7 @@ public sealed class Monster : Table { return new Offset<Monster>(o); } public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset<Monster> offset) { builder.Finish(offset.Value, "MONS"); } - + public static VectorOffset CreateMySortedVectorOfTables(FlatBufferBuilder builder, Offset<Monster>[] offsets) { Array.Sort(offsets, (Offset<Monster> o1, Offset<Monster> o2) => CompareStrings(__offset(10, o1.Value, builder.DataBuffer), __offset(10, o2.Value, builder.DataBuffer), builder.DataBuffer)); return builder.CreateVectorOfTables(offsets); @@ -138,19 +138,22 @@ public sealed class Monster : Table { public static Monster LookupByKey(VectorOffset vectorOffset, string key, ByteBuffer bb) { byte[] byteKey = System.Text.Encoding.UTF8.GetBytes(key); int vectorLocation = bb.Length - vectorOffset.Value; - int span = bb.GetInt(vectorLocation), middle, start = 0, comp, tableOffset; + int span = bb.GetInt(vectorLocation); + int start = 0; vectorLocation += 4; while (span != 0) { int middle = span / 2; - tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); - comp = CompareStrings(__offset(10, bb.Length - tableOffset, bb), byteKey, bb); - if (comp > 0) span = middle; - else if (comp < 0) { + int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); + int comp = CompareStrings(__offset(10, bb.Length - tableOffset, bb), byteKey, bb); + if (comp > 0) { + span = middle; + } else if (comp < 0) { middle++; start += middle; span -= middle; + } else { + return new Monster().__init(tableOffset, bb); } - else return new Monster().__init(tableOffset, bb); } return null; } diff --git a/tests/MyGame/Example/Monster.go b/tests/MyGame/Example/Monster.go index 7ba062fdca31d2f3fab1488a216126a00f40587b..8ffbb7d0714b4f38a3987b0669f80d4188c74589 100644 --- a/tests/MyGame/Example/Monster.go +++ b/tests/MyGame/Example/Monster.go @@ -131,9 +131,6 @@ func (rcv *Monster) Test4(obj *Test, j int) bool { if o != 0 { x := rcv._tab.Vector(o) x += flatbuffers.UOffsetT(j) * 4 - if obj == nil { - obj = new(Test) - } obj.Init(rcv._tab.Bytes, x) return true } @@ -173,9 +170,6 @@ func (rcv *Monster) Testarrayoftables(obj *Monster, j int) bool { x := rcv._tab.Vector(o) x += flatbuffers.UOffsetT(j) * 4 x = rcv._tab.Indirect(x) - if obj == nil { - obj = new(Monster) - } obj.Init(rcv._tab.Bytes, x) return true } diff --git a/tests/MyGame/Example/Monster.java b/tests/MyGame/Example/Monster.java index 0633dff08a51e13e91c8154597325d518a109279..a4c16d8aeea644761fa141e13ad6c409c65bc3df 100644 --- a/tests/MyGame/Example/Monster.java +++ b/tests/MyGame/Example/Monster.java @@ -140,21 +140,24 @@ public final class Monster extends Table { protected int keysCompare(Integer o1, Integer o2, ByteBuffer _bb) { return compareStrings(__offset(10, o1, _bb), __offset(10, o2, _bb), _bb); } public static Monster lookupByKey(int vectorOffset, String key, ByteBuffer bb) { - byte[] byteKey = key.getBytes(StandardCharsets.UTF_8); - int vectorLocation = bb.array().length - vectorOffset.Value; - int span = bb.getInt(vectorLocation), middle, start = 0, comp, tableOffset; + byte[] byteKey = key.getBytes(Table.UTF8_CHARSET.get()); + int vectorLocation = bb.array().length - vectorOffset; + int span = bb.getInt(vectorLocation); + int start = 0; vectorLocation += 4; while (span != 0) { int middle = span / 2; - tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); - comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), byteKey, bb); - if (comp > 0) span = middle; - else if (comp < 0) { + int tableOffset = __indirect(vectorLocation + 4 * (start + middle), bb); + int comp = compareStrings(__offset(10, bb.array().length - tableOffset, bb), byteKey, bb); + if (comp > 0) { + span = middle; + } else if (comp < 0) { middle++; start += middle; span -= middle; + } else { + return new Monster().__init(tableOffset, bb); } - else return new Monster().__init(tableOffset, bb); } return null; } diff --git a/tests/test.cpp b/tests/test.cpp index fd2352bd18bb011618e27ba1b93ba47b59456729..45eb1fe2561df7bc5b872f9a1651c14b3d3904a5 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -410,7 +410,8 @@ void ParseAndGenerateTextTest() { // to ensure it is correct, we now generate text back from the binary, // and compare the two: std::string jsongen; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, true); if (jsongen != jsonfile) { printf("%s----------------\n%s", jsongen.c_str(), jsonfile.c_str()); @@ -827,7 +828,8 @@ void FuzzTest2() { std::string jsongen; parser.opts.indent_step = 0; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, true); if (jsongen != json) { // These strings are larger than a megabyte, so we show the bytes around @@ -987,7 +989,8 @@ void UnicodeTest() { true); std::string jsongen; parser.opts.indent_step = -1; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, true); TEST_EQ(jsongen, std::string( "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC" @@ -1003,13 +1006,31 @@ void UnicodeTestAllowNonUTF8() { "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"), true); std::string jsongen; parser.opts.indent_step = -1; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, true); TEST_EQ(jsongen, std::string( "{F: \"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC" "\\u5225\\u30B5\\u30A4\\u30C8\\u0001\\x80\\u0080\\uD83D\\uDE0E\"}")); } +void UnicodeTestGenerateTextFailsOnNonUTF8() { + flatbuffers::Parser parser; + // Allow non-UTF-8 initially to model what happens when we load a binary flatbuffer from disk + // which contains non-UTF-8 strings. + parser.opts.allow_non_utf8 = true; + TEST_EQ(parser.Parse("table T { F:string; }" + "root_type T;" + "{ F:\"\\u20AC\\u00A2\\u30E6\\u30FC\\u30B6\\u30FC" + "\\u5225\\u30B5\\u30A4\\u30C8\\x01\\x80\\u0080\\uD83D\\uDE0E\" }"), true); + std::string jsongen; + parser.opts.indent_step = -1; + // Now, disallow non-UTF-8 (the default behavior) so GenerateText indicates failure. + parser.opts.allow_non_utf8 = false; + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, false); +} + void UnicodeSurrogatesTest() { flatbuffers::Parser parser; @@ -1157,7 +1178,8 @@ void UnknownFieldsTest() { std::string jsongen; parser.opts.indent_step = -1; - GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + auto result = GenerateText(parser, parser.builder_.GetBufferPointer(), &jsongen); + TEST_EQ(result, true); TEST_EQ(jsongen == "{str: \"test\",i: 10}", true); } @@ -1222,6 +1244,7 @@ int main(int /*argc*/, const char * /*argv*/[]) { IntegerOutOfRangeTest(); UnicodeTest(); UnicodeTestAllowNonUTF8(); + UnicodeTestGenerateTextFailsOnNonUTF8(); UnicodeSurrogatesTest(); UnicodeInvalidSurrogatesTest(); InvalidUTF8Test();