diff --git a/CMake/PackageDebian.cmake b/CMake/PackageDebian.cmake new file mode 100644 index 000000000..ebe893140 --- /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 ") + + # 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 81c9e5837..1af3d3334 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/grpc/src/compiler/cpp_generator.cc b/grpc/src/compiler/cpp_generator.cc index 9319c4193..e8ad49e70 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 T *array_end(T (&array)[N]) { return array + N; } -void PrintIncludes(Printer *printer, const std::vector& headers, const Parameters ¶ms) { +void PrintIncludes(grpc_generator::Printer *printer, + const std::vector& headers, const Parameters ¶ms) { std::map vars; vars["l"] = params.use_system_headers ? '<' : '"'; @@ -86,7 +87,7 @@ void PrintIncludes(Printer *printer, const std::vector& 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 *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 *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 *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 *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 *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 *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 *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 *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 *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 *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 953ddfd56..a4adee70e 100644 --- a/grpc/src/compiler/cpp_generator.h +++ b/grpc/src/compiler/cpp_generator.h @@ -41,16 +41,7 @@ #include #include -#ifndef GRPC_CUSTOM_STRING -#include -#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 method(int i) const = 0; -}; - -struct Printer { - virtual ~Printer() {} - - virtual void Print(const std::map &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 package_parts() const = 0; - virtual grpc::string additional_headers() const = 0; - - virtual int service_count() const = 0; - virtual std::unique_ptr service(int i) const = 0; - - virtual std::unique_ptr 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 000000000..332a51a54 --- /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 + #include + + #ifndef GRPC_CUSTOM_STRING + #include + #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 method(int i) const = 0; + }; + + struct Printer { + virtual ~Printer() {} + + virtual void Print(const std::map &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 package_parts() const = 0; + virtual grpc::string additional_headers() const = 0; + + virtual int service_count() const = 0; + virtual std::unique_ptr service(int i) const = 0; + + virtual std::unique_ptr 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 4644f9c60..b193b1791 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -1436,11 +1436,6 @@ class Struct FLATBUFFERS_FINAL_CLASS { return ReadScalar(&data_[o]); } - template T GetPointer(uoffset_t o) const { - auto p = &data_[o]; - return reinterpret_cast(p + ReadScalar(p)); - } - template T GetStruct(uoffset_t o) const { return reinterpret_cast(&data_[o]); } diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 5909a4e20..1c1e63494 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -596,7 +596,9 @@ extern void GenComment(const std::vector &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 c2186fa7b..f69fbcf96 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; + } + /** * Encode the string `s` in the buffer using UTF-8. If {@code s} is * already a {@link CharBuffer}, this method is allocation free. @@ -413,6 +435,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/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs index 5fa1ac7a5..37a2c7e6e 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 { /// /// 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. /// 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/samples/sample_text.cpp b/samples/sample_text.cpp index 557077d4b..d851120d1 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/idl_gen_go.cpp b/src/idl_gen_go.cpp index 573300980..2df8c7b62 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 6ada3e873..9bcd5bcfa 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(service_->calls.vec.size()); }; - std::unique_ptr method(int i) const { - return std::unique_ptr( + std::unique_ptr method(int i) const { + return std::unique_ptr( 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(parser_.services_.vec.size()); }; - std::unique_ptr service(int i) const { - return std::unique_ptr ( + std::unique_ptr service(int i) const { + return std::unique_ptr ( new FlatBufService(parser_.services_.vec[i])); } - std::unique_ptr CreatePrinter(std::string *str) const { - return std::unique_ptr( + std::unique_ptr CreatePrinter(std::string *str) const { + return std::unique_ptr( new FlatBufPrinter(str)); } diff --git a/src/idl_gen_text.cpp b/src/idl_gen_text.cpp index 3e41a0a76..4ff13c8a9 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 void Print(T val, Type type, int /*indent*/, +template bool Print(T val, Type type, int /*indent*/, StructDef * /*union_sd*/, const IDLOptions &opts, std::string *_text) { @@ -57,7 +57,7 @@ template void Print(T val, Type type, int /*indent*/, auto enum_val = type.enum_def->ReverseLookup(static_cast(val)); if (enum_val) { OutputIdentifier(enum_val->name, opts, _text); - return; + return true; } } @@ -66,10 +66,12 @@ template 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 void PrintVector(const Vector &v, Type type, +template bool PrintVector(const Vector &v, Type type, int indent, const IDLOptions &opts, std::string *_text) { std::string &text = *_text; @@ -81,19 +83,25 @@ template void PrintVector(const Vector &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(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 *val, +template<> bool Print(const void *val, Type type, int indent, StructDef *union_sd, const IDLOptions &opts, @@ -158,21 +177,27 @@ template<> void Print(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(val), - indent, - opts, - _text); + if (!GenStruct(*union_sd, + reinterpret_cast(val), + indent, + opts, + _text)) { + return false; + } break; case BASE_TYPE_STRUCT: - GenStruct(*type.struct_def, - reinterpret_cast(val), - indent, - opts, - _text); + if (!GenStruct(*type.struct_def, + reinterpret_cast(val), + indent, + opts, + _text)) { + return false; + } break; case BASE_TYPE_STRING: { - EscapeString(*reinterpret_cast(val), _text, opts); + if (!EscapeString(*reinterpret_cast(val), _text, opts)) { + return false; + } break; } case BASE_TYPE_VECTOR: @@ -182,31 +207,35 @@ template<> void Print(const void *val, #define FLATBUFFERS_TD(ENUM, IDLTYPE, CTYPE, JTYPE, GTYPE, NTYPE, \ PTYPE) \ case BASE_TYPE_ ## ENUM: \ - PrintVector( \ - *reinterpret_cast *>(val), \ - type, indent, opts, _text); break; + if (!PrintVector( \ + *reinterpret_cast *>(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 static void GenField(const FieldDef &fd, +template 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(table)->GetField(fd.value.offset) : table->GetField(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(fd.value.offset) : table->GetPointer(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(fd, table, struct_def.fixed, \ - opts, indent + Indent(opts), _text); \ + if (!GenField(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(flatbuffer), - 0, - parser.opts, - _text); + if (!GenStruct(*parser.root_struct_def_, + GetRoot
(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 b86c36587..3324f12a3 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(() => 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(() => 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(() => 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(() => 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(() => 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(()=>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(() => 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(() => 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(() => uut.GetLong(0)); } +#endif [FlatBuffersTestMethod] public void ByteBuffer_ReverseBytesUshort() diff --git a/tests/JavaTest.java b/tests/JavaTest.java index 154fdec67..c0d7d03fb 100755 --- a/tests/JavaTest.java +++ b/tests/JavaTest.java @@ -161,6 +161,10 @@ class JavaTest { TestNestedFlatBuffer(); + TestCreateByteVector(); + + TestCreateUninitializedVector(); + System.out.println("FlatBuffers test: completed successfully"); } @@ -281,6 +285,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 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.go b/tests/MyGame/Example/Monster.go index 7ba062fdc..8ffbb7d07 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/test.cpp b/tests/test.cpp index fd2352bd1..45eb1fe25 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();