diff --git a/CMakeLists.txt b/CMakeLists.txt index bd0f86b24..6e8cdee40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(FlatBuffers_Library_SRCS include/flatbuffers/reflection_generated.h src/idl_parser.cpp src/idl_gen_text.cpp + src/reflection.cpp ) set(FlatBuffers_Compiler_SRCS @@ -43,14 +44,9 @@ set(FlatHash_SRCS ) set(FlatBuffers_Tests_SRCS - include/flatbuffers/flatbuffers.h - include/flatbuffers/hash.h - include/flatbuffers/idl.h - include/flatbuffers/util.h - src/idl_parser.cpp - src/idl_gen_general.cpp - src/idl_gen_text.cpp + ${FlatBuffers_Library_SRCS} src/idl_gen_fbs.cpp + src/idl_gen_general.cpp tests/test.cpp # file generate by running compiler on tests/monster_test.fbs ${CMAKE_CURRENT_BINARY_DIR}/tests/monster_test_generated.h diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 01e1141c6..e00713528 100755 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -31,7 +31,9 @@ LOCAL_SRC_FILES := main.cpp \ ../../tests/test.cpp \ ../../src/idl_parser.cpp \ ../../src/idl_gen_text.cpp \ - ../../src/idl_gen_fbs.cpp + ../../src/idl_gen_fbs.cpp \ + ../../src/idl_gen_general.cpp \ + ../../src/reflection.cpp LOCAL_LDLIBS := -llog -landroid LOCAL_STATIC_LIBRARIES := android_native_app_glue flatbuffers LOCAL_ARM_MODE := arm diff --git a/android/jni/include.mk b/android/jni/include.mk index 45edf9d1d..2acae06b7 100644 --- a/android/jni/include.mk +++ b/android/jni/include.mk @@ -66,7 +66,7 @@ FLATBUFFERS_FLATC_PATH?=$(FLATBUFFERS_CMAKELISTS_DIR) FLATBUFFERS_FLATC := $(FLATBUFFERS_FLATC_PATH)/Debug/flatc endif -FLATBUFFERS_FLATC_ARGS?=--gen-includes +FLATBUFFERS_FLATC_ARGS?= # Search for cmake. CMAKE_ROOT := $(realpath $(LOCAL_PATH)/../../../../../../prebuilts/cmake) diff --git a/build/VS2010/flattests.vcxproj b/build/VS2010/flattests.vcxproj index 447eebdf8..24dca0b36 100755 --- a/build/VS2010/flattests.vcxproj +++ b/build/VS2010/flattests.vcxproj @@ -272,6 +272,7 @@ + diff --git a/build/Xcode/FlatBuffers.xcodeproj/project.pbxproj b/build/Xcode/FlatBuffers.xcodeproj/project.pbxproj index b67ae95b5..dfa2962c7 100644 --- a/build/Xcode/FlatBuffers.xcodeproj/project.pbxproj +++ b/build/Xcode/FlatBuffers.xcodeproj/project.pbxproj @@ -12,8 +12,9 @@ 61823BBC53544106B6DBC38E /* idl_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3709AC883348409592530AE6 /* idl_parser.cpp */; settings = {COMPILER_FLAGS = ""; }; }; 61FF3C34FBEC4819A1C30F92 /* sample_text.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ECCEBFFA6977404F858F9739 /* sample_text.cpp */; settings = {COMPILER_FLAGS = ""; }; }; 8C303C591975D6A700D7C1C5 /* idl_gen_go.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */; }; - 8C6905F819F835A900CB8866 /* idl_gen_fbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6905F119F835A900CB8866 /* idl_gen_fbs.cpp */; }; 8C6905FD19F835B400CB8866 /* idl_gen_fbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */; }; + 8C8774631B703D4800E693F5 /* reflection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C8774621B703D4800E693F5 /* reflection.cpp */; }; + 8C8774641B703E1200E693F5 /* idl_gen_fbs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */; }; 8CA854B31B04244A00040A06 /* idl_gen_python.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CA854B21B04244A00040A06 /* idl_gen_python.cpp */; }; 8CD8717B19CB937D0012A827 /* idl_gen_general.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8CD8717A19CB937D0012A827 /* idl_gen_general.cpp */; }; A9C9A99F719A4ED58DC2D2FC /* idl_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3709AC883348409592530AE6 /* idl_parser.cpp */; settings = {COMPILER_FLAGS = ""; }; }; @@ -33,23 +34,15 @@ 3709AC883348409592530AE6 /* idl_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_parser.cpp; path = src/idl_parser.cpp; sourceTree = SOURCE_ROOT; }; 3863042BCEC64791BFB48625 /* flatsamplebinary */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; path = flatsamplebinary; sourceTree = BUILT_PRODUCTS_DIR; }; 420E3BC724ED4A008D79297F /* flatsampletext */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; path = flatsampletext; sourceTree = BUILT_PRODUCTS_DIR; }; - 423CA92401AE442B91546E63 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = CMakeLists.txt; path = /Users/wvo/flatbuffers_snapshot9/CMakeLists.txt; sourceTree = ""; }; 5EE44BFFAF8E43F485859145 /* sample_binary.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sample_binary.cpp; path = samples/sample_binary.cpp; sourceTree = SOURCE_ROOT; }; 6AD24EEB3D024825A37741FF /* test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = test.cpp; path = tests/test.cpp; sourceTree = SOURCE_ROOT; }; 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_go.cpp; path = src/idl_gen_go.cpp; sourceTree = ""; }; 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_fbs.cpp; path = src/idl_gen_fbs.cpp; sourceTree = ""; }; - 8C6905EF19F835A900CB8866 /* flatc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = flatc.cpp; sourceTree = ""; }; - 8C6905F019F835A900CB8866 /* idl_gen_cpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_gen_cpp.cpp; sourceTree = ""; }; - 8C6905F119F835A900CB8866 /* idl_gen_fbs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_gen_fbs.cpp; sourceTree = ""; }; - 8C6905F219F835A900CB8866 /* idl_gen_general.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_gen_general.cpp; sourceTree = ""; }; - 8C6905F319F835A900CB8866 /* idl_gen_go.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_gen_go.cpp; sourceTree = ""; }; - 8C6905F419F835A900CB8866 /* idl_gen_text.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_gen_text.cpp; sourceTree = ""; }; - 8C6905F519F835A900CB8866 /* idl_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = idl_parser.cpp; sourceTree = ""; }; + 8C8774621B703D4800E693F5 /* reflection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = reflection.cpp; path = src/reflection.cpp; sourceTree = ""; }; 8CA854B21B04244A00040A06 /* idl_gen_python.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_python.cpp; path = src/idl_gen_python.cpp; sourceTree = ""; }; 8CD8717A19CB937D0012A827 /* idl_gen_general.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_general.cpp; path = src/idl_gen_general.cpp; sourceTree = ""; }; A13F25CDAD23435DA293690D /* flattests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; path = flattests; sourceTree = BUILT_PRODUCTS_DIR; }; AB70F1FBA50E4120BCF37C8D /* monster_test_generated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = monster_test_generated.h; path = tests/monster_test_generated.h; sourceTree = SOURCE_ROOT; }; - AD3682C6E1DD4EABB822C0CC /* monster_generated.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = monster_generated.h; path = samples/monster_generated.h; sourceTree = SOURCE_ROOT; }; C0E7B66C3FF849A0AD9A7168 /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = util.h; path = include/flatbuffers/util.h; sourceTree = SOURCE_ROOT; }; CD90A7F6B2BE4D0384294DD1 /* idl_gen_cpp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = idl_gen_cpp.cpp; path = src/idl_gen_cpp.cpp; sourceTree = SOURCE_ROOT; }; DD8B353D4756412195777FBA /* flatbuffers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = flatbuffers.h; path = include/flatbuffers/flatbuffers.h; sourceTree = SOURCE_ROOT; }; @@ -58,13 +51,6 @@ /* End PBXFileReference section */ /* Begin PBXGroup section */ - 194905BCBB5C451DB092EB08 /* Resources */ = { - isa = PBXGroup; - children = ( - ); - name = Resources; - sourceTree = ""; - }; 28237E300FE042DEADA302D3 /* Source Files */ = { isa = PBXGroup; children = ( @@ -72,19 +58,10 @@ 8C6905EC19F8357300CB8866 /* idl_gen_fbs.cpp */, 8CD8717A19CB937D0012A827 /* idl_gen_general.cpp */, 8C303C581975D6A700D7C1C5 /* idl_gen_go.cpp */, - 0DFD29781D8E490284B06504 /* flatc.cpp */, - CD90A7F6B2BE4D0384294DD1 /* idl_gen_cpp.cpp */, - ); - name = "Source Files"; - sourceTree = ""; - }; - 355DCA17961E4B2FB2C71403 /* Source Files */ = { - isa = PBXGroup; - children = ( - 8C6905EE19F835A900CB8866 /* src */, F6C5D81DBF864365B12E269D /* idl_gen_text.cpp */, 3709AC883348409592530AE6 /* idl_parser.cpp */, - 6AD24EEB3D024825A37741FF /* test.cpp */, + CD90A7F6B2BE4D0384294DD1 /* idl_gen_cpp.cpp */, + 8C8774621B703D4800E693F5 /* reflection.cpp */, ); name = "Source Files"; sourceTree = ""; @@ -92,49 +69,17 @@ 378446B9D5EF46EF92B35E21 /* flatc */ = { isa = PBXGroup; children = ( - 5FEA84E7D39645988300317C /* Header Files */, - 28237E300FE042DEADA302D3 /* Source Files */, + 0DFD29781D8E490284B06504 /* flatc.cpp */, ); name = flatc; sourceTree = ""; }; - 40E30B8480BD493EA459E9B4 /* Header Files */ = { - isa = PBXGroup; - children = ( - AD3682C6E1DD4EABB822C0CC /* monster_generated.h */, - ); - name = "Header Files"; - sourceTree = ""; - }; - 4D1151F6FE594E40A1C177FF /* Header Files */ = { - isa = PBXGroup; - children = ( - ); - name = "Header Files"; - sourceTree = ""; - }; - 5235469653ED4BC88A6C504D /* Header Files */ = { - isa = PBXGroup; - children = ( - DD8B353D4756412195777FBA /* flatbuffers.h */, - 00154BD8654B4B5B9FF45FA6 /* idl.h */, - C0E7B66C3FF849A0AD9A7168 /* util.h */, - AB70F1FBA50E4120BCF37C8D /* monster_test_generated.h */, - ); - name = "Header Files"; - sourceTree = ""; - }; - 5648A71028E14478841372D3 /* Source Files */ = { - isa = PBXGroup; - children = ( - 5EE44BFFAF8E43F485859145 /* sample_binary.cpp */, - ); - name = "Source Files"; - sourceTree = ""; - }; 5FEA84E7D39645988300317C /* Header Files */ = { isa = PBXGroup; children = ( + DD8B353D4756412195777FBA /* flatbuffers.h */, + 00154BD8654B4B5B9FF45FA6 /* idl.h */, + C0E7B66C3FF849A0AD9A7168 /* util.h */, ); name = "Header Files"; sourceTree = ""; @@ -142,6 +87,8 @@ 866694F9F2F7451382D236B3 /* Sources */ = { isa = PBXGroup; children = ( + 28237E300FE042DEADA302D3 /* Source Files */, + 5FEA84E7D39645988300317C /* Header Files */, 378446B9D5EF46EF92B35E21 /* flatc */, DB9DE41C20F349F694A488F3 /* flatsamplebinary */, 8FA1F43C78914AE5AD04E24E /* flatsampletext */, @@ -153,31 +100,16 @@ 88421F5F87584EE3B67C979A /* flattests */ = { isa = PBXGroup; children = ( - 5235469653ED4BC88A6C504D /* Header Files */, - 355DCA17961E4B2FB2C71403 /* Source Files */, + AB70F1FBA50E4120BCF37C8D /* monster_test_generated.h */, + 6AD24EEB3D024825A37741FF /* test.cpp */, ); name = flattests; sourceTree = ""; }; - 8C6905EE19F835A900CB8866 /* src */ = { - isa = PBXGroup; - children = ( - 8C6905EF19F835A900CB8866 /* flatc.cpp */, - 8C6905F019F835A900CB8866 /* idl_gen_cpp.cpp */, - 8C6905F119F835A900CB8866 /* idl_gen_fbs.cpp */, - 8C6905F219F835A900CB8866 /* idl_gen_general.cpp */, - 8C6905F319F835A900CB8866 /* idl_gen_go.cpp */, - 8C6905F419F835A900CB8866 /* idl_gen_text.cpp */, - 8C6905F519F835A900CB8866 /* idl_parser.cpp */, - ); - path = src; - sourceTree = ""; - }; 8F5E926B72104F4194B3BD5A = { isa = PBXGroup; children = ( 866694F9F2F7451382D236B3 /* Sources */, - 194905BCBB5C451DB092EB08 /* Resources */, 99CC11E382B8420AA79A8A14 /* Products */, ); sourceTree = ""; @@ -185,8 +117,7 @@ 8FA1F43C78914AE5AD04E24E /* flatsampletext */ = { isa = PBXGroup; children = ( - 40E30B8480BD493EA459E9B4 /* Header Files */, - A1C826615F904FDE8F0CA154 /* Source Files */, + ECCEBFFA6977404F858F9739 /* sample_text.cpp */, ); name = flatsampletext; sourceTree = ""; @@ -202,20 +133,10 @@ name = Products; sourceTree = ""; }; - A1C826615F904FDE8F0CA154 /* Source Files */ = { - isa = PBXGroup; - children = ( - ECCEBFFA6977404F858F9739 /* sample_text.cpp */, - ); - name = "Source Files"; - sourceTree = ""; - }; DB9DE41C20F349F694A488F3 /* flatsamplebinary */ = { isa = PBXGroup; children = ( - 4D1151F6FE594E40A1C177FF /* Header Files */, - 5648A71028E14478841372D3 /* Source Files */, - 423CA92401AE442B91546E63 /* CMakeLists.txt */, + 5EE44BFFAF8E43F485859145 /* sample_binary.cpp */, ); name = flatsamplebinary; sourceTree = ""; @@ -338,8 +259,8 @@ AA9BACF55EB3456BA2F633BB /* flatc.cpp in Sources */, BE03D7B0C9584DD58B50ED34 /* idl_gen_cpp.cpp in Sources */, AD71FEBEE4E846529002C1F0 /* idl_gen_text.cpp in Sources */, + 8C8774641B703E1200E693F5 /* idl_gen_fbs.cpp in Sources */, A9C9A99F719A4ED58DC2D2FC /* idl_parser.cpp in Sources */, - 8C6905F819F835A900CB8866 /* idl_gen_fbs.cpp in Sources */, 8CA854B31B04244A00040A06 /* idl_gen_python.cpp in Sources */, 8CD8717B19CB937D0012A827 /* idl_gen_general.cpp in Sources */, ); @@ -349,6 +270,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8C8774631B703D4800E693F5 /* reflection.cpp in Sources */, 5AC48C391ACA9A0A008132C5 /* idl_gen_general.cpp in Sources */, 8C6905FD19F835B400CB8866 /* idl_gen_fbs.cpp in Sources */, E0680D6B5BFD484BA9D88EE8 /* idl_gen_text.cpp in Sources */, diff --git a/docs/header.html b/docs/header.html index de98f2f0e..0266e7c4f 100644 --- a/docs/header.html +++ b/docs/header.html @@ -21,7 +21,7 @@ $extrastylesheet
-
+
@@ -36,6 +36,9 @@ $extrastylesheet
$projectname  $projectnumber
+
+ An open source project by FPL. +
$projectbrief
diff --git a/docs/html/index.html b/docs/html/index.html index 4af6ab8e2..07d36bbc2 100644 --- a/docs/html/index.html +++ b/docs/html/index.html @@ -21,7 +21,7 @@
-
+
@@ -31,6 +31,9 @@ diff --git a/docs/html/md__benchmarks.html b/docs/html/md__benchmarks.html index feb544382..3b8ec2cd3 100644 --- a/docs/html/md__benchmarks.html +++ b/docs/html/md__benchmarks.html @@ -21,7 +21,7 @@
-
+
FlatBuffers
+
+ An open source project by FPL. +
@@ -31,6 +31,9 @@ diff --git a/docs/html/md__building.html b/docs/html/md__building.html index 62b82fc5c..8494f5d56 100644 --- a/docs/html/md__building.html +++ b/docs/html/md__building.html @@ -21,7 +21,7 @@
-
+
FlatBuffers
+
+ An open source project by FPL. +
@@ -31,6 +31,9 @@ diff --git a/docs/html/md__compiler.html b/docs/html/md__compiler.html index 8db385dc0..043cb23b0 100644 --- a/docs/html/md__compiler.html +++ b/docs/html/md__compiler.html @@ -21,7 +21,7 @@
-
+
FlatBuffers
+
+ An open source project by FPL. +
@@ -31,6 +31,9 @@ @@ -75,7 +78,7 @@ $(document).ready(function(){initNavTree('md__compiler.html','');});
  • --strict-json : Require & generate strict JSON (field names are enclosed in quotes, no trailing commas in tables/vectors). By default, no quotes are required/generated, and trailing commas are allowed.
  • --defaults-json : Output fields whose value is equal to the default value when writing JSON text.
  • --no-prefix : Don't prefix enum values in generated C++ by their enum type.
  • -
  • --gen-includes : (deprecated), instead use:
  • +
  • --gen-includes : (deprecated), this is the default behavior. If the original behavior is required (no include statements) use --no-includes.
  • --no-includes : Don't generate include statements for included schemas the generated file depends on (C++).
  • --gen-mutable : Generate additional non-const accessors for mutating FlatBuffers in-place.
  • --gen-onefile : Generate single output file (useful for C#)
  • diff --git a/docs/html/md__cpp_usage.html b/docs/html/md__cpp_usage.html index 4bc78cc1c..91bf63649 100644 --- a/docs/html/md__cpp_usage.html +++ b/docs/html/md__cpp_usage.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__go_usage.html b/docs/html/md__go_usage.html index 82cf7cdba..7dab4e911 100644 --- a/docs/html/md__go_usage.html +++ b/docs/html/md__go_usage.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__grammar.html b/docs/html/md__grammar.html index 33d6b8c15..557a95009 100644 --- a/docs/html/md__grammar.html +++ b/docs/html/md__grammar.html @@ -5,7 +5,7 @@ -FlatBuffers: Formal Grammar of the schema language +FlatBuffers: Grammar of the schema language @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ @@ -55,7 +58,7 @@ $(document).ready(function(){initNavTree('md__grammar.html','');});
    -
    Formal Grammar of the schema language
    +
    Grammar of the schema language

    schema = include* ( namespace_decl | type_decl | enum_decl | root_decl | file_extension_decl | file_identifier_decl | attribute_decl | object )*

    @@ -68,13 +71,16 @@ $(document).ready(function(){initNavTree('md__grammar.html','');});

    field_decl = ident : type [ = scalar ] metadata ;

    type = bool | byte | ubyte | short | ushort | int | uint | float | long | ulong | double | string | [ type ] | ident

    enumval_decl = ident [ = integer_constant ]

    -

    metadata = [ ( commasep( ident [ : scalar ] ) ) ]

    -

    scalar = integer_constant | float_constant | true | false

    +

    metadata = [ ( commasep( ident [ : single_value ] ) ) ]

    +

    scalar = integer_constant | float_constant

    object = { commasep( ident : value ) }

    -

    value = scalar | object | string_constant | [ commasep( value ) ]

    +

    single_value = scalar | string_constant

    +

    value = single_value | object | [ commasep( value ) ]

    commasep(x) = [ x ( , x )* ]

    file_extension_decl = file_extension string_constant ;

    -

    file_identifier_decl = file_identifier string_constant ;

    +

    file_identifier_decl = file_identifier string_constant ;

    +

    integer_constant = -?[0-9]+ | true | false

    +

    float_constant = -?[0-9]+.[0-9]+((e|E)(+|-)?[0-9]+)?

    diff --git a/docs/html/md__internals.html b/docs/html/md__internals.html index b22d36d46..c06672475 100644 --- a/docs/html/md__internals.html +++ b/docs/html/md__internals.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__java_usage.html b/docs/html/md__java_usage.html index 4535ea608..a7ed783ca 100644 --- a/docs/html/md__java_usage.html +++ b/docs/html/md__java_usage.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__python_usage.html b/docs/html/md__python_usage.html index 9cf4c8f5e..295968998 100644 --- a/docs/html/md__python_usage.html +++ b/docs/html/md__python_usage.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__schemas.html b/docs/html/md__schemas.html index 5efa5bb6a..ae74a33d4 100644 --- a/docs/html/md__schemas.html +++ b/docs/html/md__schemas.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/md__white_paper.html b/docs/html/md__white_paper.html index 4dbd95396..88b543800 100644 --- a/docs/html/md__white_paper.html +++ b/docs/html/md__white_paper.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ diff --git a/docs/html/navtree.js b/docs/html/navtree.js index 58cf1e6c5..58fba48ab 100644 --- a/docs/html/navtree.js +++ b/docs/html/navtree.js @@ -11,7 +11,7 @@ var NAVTREE = [ "Benchmarks", "md__benchmarks.html", null ], [ "FlatBuffers white paper", "md__white_paper.html", null ], [ "FlatBuffer Internals", "md__internals.html", null ], - [ "Formal Grammar of the schema language", "md__grammar.html", null ] + [ "Grammar of the schema language", "md__grammar.html", null ] ] ] ]; diff --git a/docs/html/pages.html b/docs/html/pages.html index 69d49a3b8..5cefb95b2 100644 --- a/docs/html/pages.html +++ b/docs/html/pages.html @@ -21,7 +21,7 @@
    -
    +
    FlatBuffers
    +
    + An open source project by FPL. +
    @@ -31,6 +31,9 @@ @@ -70,7 +73,7 @@ $(document).ready(function(){initNavTree('pages.html','');}); - +
    FlatBuffers
    +
    + An open source project by FPL. +
     Benchmarks
     FlatBuffers white paper
     FlatBuffer Internals
     Formal Grammar of the schema language
     Grammar of the schema language
    diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index 643f19caa..5d254dfa5 100755 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -51,7 +51,10 @@ be generated for each file processed: - `--no-prefix` : Don't prefix enum values in generated C++ by their enum type. -- `--gen-includes` : (deprecated), instead use: +- `--gen-includes` : (deprecated), this is the default behavior. + If the original behavior is required (no include + statements) use `--no-includes.` + - `--no-includes` : Don't generate include statements for included schemas the generated file depends on (C++). diff --git a/docs/source/Grammar.md b/docs/source/Grammar.md index cafc05487..bcc5f7496 100755 --- a/docs/source/Grammar.md +++ b/docs/source/Grammar.md @@ -1,4 +1,4 @@ -# Formal Grammar of the schema language +# Grammar of the schema language schema = include* ( namespace\_decl | type\_decl | enum\_decl | root\_decl | @@ -26,13 +26,15 @@ type = `bool` | `byte` | `ubyte` | `short` | `ushort` | `int` | `uint` | enumval\_decl = ident [ `=` integer\_constant ] -metadata = [ `(` commasep( ident [ `:` scalar ] ) `)` ] +metadata = [ `(` commasep( ident [ `:` single\_value ] ) `)` ] -scalar = integer\_constant | float\_constant | `true` | `false` +scalar = integer\_constant | float\_constant object = { commasep( ident `:` value ) } -value = scalar | object | string\_constant | `[` commasep( value ) `]` +single\_value = scalar | string\_constant + +value = single\_value | object | `[` commasep( value ) `]` commasep(x) = [ x ( `,` x )\* ] @@ -40,3 +42,6 @@ file_extension_decl = `file_extension` string\_constant `;` file_identifier_decl = `file_identifier` string\_constant `;` +integer\_constant = -?[0-9]+ | `true` | `false` + +float\_constant = -?[0-9]+.[0-9]+((e|E)(+|-)?[0-9]+)? diff --git a/include/flatbuffers/flatbuffers.h b/include/flatbuffers/flatbuffers.h index 0b9cf5853..3bf1f0ae2 100644 --- a/include/flatbuffers/flatbuffers.h +++ b/include/flatbuffers/flatbuffers.h @@ -293,11 +293,21 @@ public: const_iterator end() const { return const_iterator(Data(), size()); } // Change elements if you have a non-const pointer to this object. + // Scalars only. See reflection.h, and the documentation. void Mutate(uoffset_t i, T val) { assert(i < size()); WriteScalar(data() + i, val); } + // Change an element of a vector of tables (or strings). + // "val" points to the new table/string, as you can obtain from + // e.g. reflection::AddFlatBuffer(). + void MutateOffset(uoffset_t i, const uint8_t *val) { + assert(i < size()); + assert(sizeof(T) == sizeof(uoffset_t)); + WriteScalar(data() + i, val - (Data() + i * sizeof(uoffset_t))); + } + // The raw data in little endian format. Use with care. const uint8_t *Data() const { return reinterpret_cast(&length_ + 1); @@ -343,6 +353,24 @@ private: } }; +// Represent a vector much like the template above, but in this case we +// don't know what the element types are (used with reflection.h). +class VectorOfAny { +public: + uoffset_t size() const { return EndianScalar(length_); } + + const uint8_t *Data() const { + return reinterpret_cast(&length_ + 1); + } + uint8_t *Data() { + return reinterpret_cast(&length_ + 1); + } +protected: + VectorOfAny(); + + uoffset_t length_; +}; + // Convenient helper function to get the length of any vector, regardless // of wether it is null or not (the field is not set). template static inline size_t VectorLength(const Vector *v) { @@ -985,6 +1013,9 @@ class Struct FLATBUFFERS_FINAL_CLASS { return reinterpret_cast(&data_[o]); } + const uint8_t *GetAddressOf(uoffset_t o) const { return &data_[o]; } + uint8_t *GetAddressOf(uoffset_t o) { return &data_[o]; } + private: uint8_t data_[1]; }; @@ -1017,7 +1048,6 @@ class Table { ? reinterpret_cast

    (p + ReadScalar(p)) : nullptr; } - template P GetPointer(voffset_t field) const { return const_cast(this)->GetPointer

    (field); } @@ -1035,6 +1065,21 @@ class Table { return true; } + bool SetPointer(voffset_t field, const uint8_t *val) { + auto field_offset = GetOptionalFieldOffset(field); + if (!field_offset) return false; + WriteScalar(data_ + field_offset, val - (data_ + field_offset)); + return true; + } + + uint8_t *GetAddressOf(voffset_t field) { + auto field_offset = GetOptionalFieldOffset(field); + return field_offset ? data_ + field_offset : nullptr; + } + const uint8_t *GetAddressOf(voffset_t field) const { + return const_cast

    (this)->GetAddressOf(field); + } + uint8_t *GetVTable() { return data_ - ReadScalar(data_); } bool CheckField(voffset_t field) const { diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index a9082fd8a..8a912de5c 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -50,8 +50,8 @@ namespace flatbuffers { TD(FLOAT, "float", float, float, float32, float, float32) /* begin float */ \ TD(DOUBLE, "double", double, double, float64, double, float64) /* end float/scalar */ #define FLATBUFFERS_GEN_TYPES_POINTER(TD) \ - TD(STRING, "string", Offset, int, int, int, int) \ - TD(VECTOR, "", Offset, int, int, int, int) \ + TD(STRING, "string", Offset, int, int, StringOffset, int) \ + TD(VECTOR, "", Offset, int, int, VectorOffset, int) \ TD(STRUCT, "", Offset, int, int, int, int) \ TD(UNION, "", Offset, int, int, int, int) diff --git a/include/flatbuffers/reflection.h b/include/flatbuffers/reflection.h index 60e2426cc..cb18b1437 100644 --- a/include/flatbuffers/reflection.h +++ b/include/flatbuffers/reflection.h @@ -17,8 +17,6 @@ #ifndef FLATBUFFERS_REFLECTION_H_ #define FLATBUFFERS_REFLECTION_H_ -#include "flatbuffers/util.h" - // This is somewhat of a circular dependency because flatc (and thus this // file) is needed to generate this header in the first place. // Should normally not be a problem since it can be generated by the @@ -30,12 +28,28 @@ namespace flatbuffers { +// ------------------------- GETTERS ------------------------- + +// Size of a basic type, don't use with structs. inline size_t GetTypeSize(reflection::BaseType base_type) { // This needs to correspond to the BaseType enum. static size_t sizes[] = { 0, 1, 1, 1, 1, 2, 2, 4, 4, 8, 8, 4, 8, 4, 4, 4, 4 }; return sizes[base_type]; } +// Same as above, but now correctly returns the size of a struct if +// the field (or vector element) is a struct. +inline size_t GetTypeSizeInline(reflection::BaseType base_type, + int type_index, + const reflection::Schema &schema) { + if (base_type == reflection::Obj && + schema.objects()->Get(type_index)->is_struct()) { + return schema.objects()->Get(type_index)->bytesize(); + } else { + return GetTypeSize(base_type); + } +} + // Get the root, regardless of what type it is. inline Table *GetAnyRoot(uint8_t *flatbuf) { return GetMutableRoot
    (flatbuf); @@ -68,108 +82,147 @@ inline const String *GetFieldS(const Table &table, } // Get a field, if you know it's a vector. -template const Vector *GetFieldV(const Table &table, - const reflection::Field &field) { +template Vector *GetFieldV(const Table &table, + const reflection::Field &field) { assert(field.type()->base_type() == reflection::Vector && sizeof(T) == GetTypeSize(field.type()->element())); - return table.GetPointer *>(field.offset()); + return table.GetPointer *>(field.offset()); +} + +// Get a field, if you know it's a vector, generically. +// To actually access elements, use the return value together with +// field.type()->element() in any of GetAnyVectorElemI below etc. +inline VectorOfAny *GetFieldAnyV(const Table &table, + const reflection::Field &field) { + return table.GetPointer(field.offset()); } // Get a field, if you know it's a table. -inline const Table *GetFieldT(const Table &table, - const reflection::Field &field) { +inline Table *GetFieldT(const Table &table, + const reflection::Field &field) { assert(field.type()->base_type() == reflection::Obj || field.type()->base_type() == reflection::Union); - return table.GetPointer(field.offset()); + return table.GetPointer
    (field.offset()); } -// Get any field as a 64bit int, regardless of what it is (bool/int/float/str). +// Raw helper functions used below: get any value in memory as a 64bit int, a +// double or a string. +// All scalars get static_cast to an int64_t, strings use strtoull, every other +// data type returns 0. +int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data); +// All scalars static cast to double, strings use strtod, every other data +// type is 0.0. +double GetAnyValueF(reflection::BaseType type, const uint8_t *data); +// All scalars converted using stringstream, strings as-is, and all other +// data types provide some level of debug-pretty-printing. +std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, + const reflection::Schema *schema, + int type_index); + +// Get any table field as a 64bit int, regardless of what type it is. inline int64_t GetAnyFieldI(const Table &table, const reflection::Field &field) { -# define FLATBUFFERS_GET(C, T) \ - static_cast(GetField##C(table, field)) - switch (field.type()->base_type()) { - case reflection::UType: - case reflection::Bool: - case reflection::UByte: return FLATBUFFERS_GET(I, uint8_t); - case reflection::Byte: return FLATBUFFERS_GET(I, int8_t); - case reflection::Short: return FLATBUFFERS_GET(I, int16_t); - case reflection::UShort: return FLATBUFFERS_GET(I, uint16_t); - case reflection::Int: return FLATBUFFERS_GET(I, int32_t); - case reflection::UInt: return FLATBUFFERS_GET(I, uint32_t); - case reflection::Long: return FLATBUFFERS_GET(I, int64_t); - case reflection::ULong: return FLATBUFFERS_GET(I, uint64_t); - case reflection::Float: return FLATBUFFERS_GET(F, float); - case reflection::Double: return FLATBUFFERS_GET(F, double); - case reflection::String: { - auto s = GetFieldS(table, field); - return s ? StringToInt(s->c_str()) : 0; - } - default: return 0; - } -# undef FLATBUFFERS_GET + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr ? GetAnyValueI(field.type()->base_type(), field_ptr) + : field.default_integer(); } -// Get any field as a double, regardless of what it is (bool/int/float/str). +// Get any table field as a double, regardless of what type it is. inline double GetAnyFieldF(const Table &table, const reflection::Field &field) { - switch (field.type()->base_type()) { - case reflection::Float: return GetFieldF(table, field); - case reflection::Double: return GetFieldF(table, field); - case reflection::String: { - auto s = GetFieldS(table, field); - return s ? strtod(s->c_str(), nullptr) : 0.0; - } - default: return static_cast(GetAnyFieldI(table, field)); - } + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr ? GetAnyValueF(field.type()->base_type(), field_ptr) + : field.default_real(); } -// Get any field as a string, regardless of what it is (bool/int/float/str). + +// Get any table field as a string, regardless of what type it is. +// You may pass nullptr for the schema if you don't care to have fields that +// are of table type pretty-printed. inline std::string GetAnyFieldS(const Table &table, const reflection::Field &field, - const reflection::Schema &schema) { - switch (field.type()->base_type()) { - case reflection::Float: - case reflection::Double: return NumToString(GetAnyFieldF(table, field)); - case reflection::String: { - auto s = GetFieldS(table, field); - return s ? s->c_str() : ""; - } - case reflection::Obj: { - // Convert the table to a string. This is mostly for debugging purposes, - // and does NOT promise to be JSON compliant. - // Also prefixes the type. - auto &objectdef = *schema.objects()->Get(field.type()->index()); - auto s = objectdef.name()->str(); - if (objectdef.is_struct()) { - s += "(struct)"; // TODO: implement this as well. - } else { - auto table_field = GetFieldT(table, field); - s += " { "; - auto fielddefs = objectdef.fields(); - for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { - auto &fielddef = **it; - if (!table.CheckField(fielddef.offset())) continue; - auto val = GetAnyFieldS(*table_field, fielddef, schema); - if (fielddef.type()->base_type() == reflection::String) - val = "\"" + val + "\""; // Doesn't deal with escape codes etc. - s += fielddef.name()->str(); - s += ": "; - s += val; - s += ", "; - } - s += "}"; - } - return s; - } - case reflection::Vector: - return "[(elements)]"; // TODO: implement this as well. - case reflection::Union: - return "(union)"; // TODO: implement this as well. - default: return NumToString(GetAnyFieldI(table, field)); - } + const reflection::Schema *schema) { + auto field_ptr = table.GetAddressOf(field.offset()); + return field_ptr ? GetAnyValueS(field.type()->base_type(), field_ptr, schema, + field.type()->index()) + : ""; } +// Get any struct field as a 64bit int, regardless of what type it is. +inline int64_t GetAnyFieldI(const Struct &st, + const reflection::Field &field) { + return GetAnyValueI(field.type()->base_type(), + st.GetAddressOf(field.offset())); +} + +// Get any struct field as a double, regardless of what type it is. +inline double GetAnyFieldF(const Struct &st, + const reflection::Field &field) { + return GetAnyValueF(field.type()->base_type(), + st.GetAddressOf(field.offset())); +} + +// Get any struct field as a string, regardless of what type it is. +inline std::string GetAnyFieldS(const Struct &st, + const reflection::Field &field) { + return GetAnyValueS(field.type()->base_type(), + st.GetAddressOf(field.offset()), nullptr, -1); +} + +// Get any vector element as a 64bit int, regardless of what type it is. +inline int64_t GetAnyVectorElemI(const VectorOfAny *vec, + reflection::BaseType elem_type, size_t i) { + return GetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i); +} + +// Get any vector element as a double, regardless of what type it is. +inline double GetAnyVectorElemF(const VectorOfAny *vec, + reflection::BaseType elem_type, size_t i) { + return GetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i); +} + +// Get any vector element as a string, regardless of what type it is. +inline std::string GetAnyVectorElemS(const VectorOfAny *vec, + reflection::BaseType elem_type, size_t i) { + return GetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, + nullptr, -1); +} + +// Get a vector element that's a table/string/vector from a generic vector. +// Pass Table/String/VectorOfAny as template parameter. +// Warning: does no typechecking. +template T *GetAnyVectorElemPointer(const VectorOfAny *vec, + size_t i) { + auto elem_ptr = vec->Data() + sizeof(uoffset_t) * i; + return (T *)(elem_ptr + ReadScalar(elem_ptr)); +} + +// Get the inline-address of a vector element. Useful for Structs (pass Struct +// as template arg), or being able to address a range of scalars in-line. +// Get elem_size from GetTypeSizeInline(). +// Note: little-endian data on all platforms, use EndianScalar() instead of +// raw pointer access with scalars). +template T *GetAnyVectorElemAddressOf(const VectorOfAny *vec, + size_t i, + size_t elem_size) { + // C-cast to allow const conversion. + return (T *)(vec->Data() + elem_size * i); +} + +// Similarly, for elements of tables. +template T *GetAnyFieldAddressOf(const Table &table, + const reflection::Field &field) { + return (T *)table.GetAddressOf(field.offset()); +} + +// Similarly, for elements of structs. +template T *GetAnyFieldAddressOf(const Struct &st, + const reflection::Field &field) { + return (T *)st.GetAddressOf(field.offset()); +} + +// ------------------------- SETTERS ------------------------- + // Set any scalar field, if you know its exact type. template bool SetField(Table *table, const reflection::Field &field, T val) { @@ -177,77 +230,108 @@ template bool SetField(Table *table, const reflection::Field &field, return table->SetField(field.offset(), val); } -// Set any field as a 64bit int, regardless of what it is (bool/int/float/str). -inline void SetAnyFieldI(Table *table, const reflection::Field &field, +// Raw helper functions used below: set any value in memory as a 64bit int, a +// double or a string. +// These work for all scalar values, but do nothing for other data types. +// To set a string, see SetString below. +void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val); +void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val); +void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val); + +// Set any table field as a 64bit int, regardless of type what it is. +inline bool SetAnyFieldI(Table *table, const reflection::Field &field, int64_t val) { -# define FLATBUFFERS_SET(T) SetField(table, field, static_cast(val)) - switch (field.type()->base_type()) { - case reflection::UType: - case reflection::Bool: - case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break; - case reflection::Byte: FLATBUFFERS_SET(int8_t ); break; - case reflection::Short: FLATBUFFERS_SET(int16_t ); break; - case reflection::UShort: FLATBUFFERS_SET(uint16_t ); break; - case reflection::Int: FLATBUFFERS_SET(int32_t ); break; - case reflection::UInt: FLATBUFFERS_SET(uint32_t ); break; - case reflection::Long: FLATBUFFERS_SET(int64_t ); break; - case reflection::ULong: FLATBUFFERS_SET(uint64_t ); break; - case reflection::Float: FLATBUFFERS_SET(float ); break; - case reflection::Double: FLATBUFFERS_SET(double ); break; - // TODO: support strings - default: break; - } -# undef FLATBUFFERS_SET + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) return false; + SetAnyValueI(field.type()->base_type(), field_ptr, val); + return true; } -// Set any field as a double, regardless of what it is (bool/int/float/str). -inline void SetAnyFieldF(Table *table, const reflection::Field &field, +// Set any table field as a double, regardless of what type it is. +inline bool SetAnyFieldF(Table *table, const reflection::Field &field, double val) { - switch (field.type()->base_type()) { - case reflection::Float: SetField (table, field, - static_cast(val)); break; - case reflection::Double: SetField(table, field, val); break; - // TODO: support strings. - default: SetAnyFieldI(table, field, static_cast(val)); break; - } + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) return false; + SetAnyValueF(field.type()->base_type(), field_ptr, val); + return true; } -// Set any field as a string, regardless of what it is (bool/int/float/str). -inline void SetAnyFieldS(Table *table, const reflection::Field &field, - const char *val) { - switch (field.type()->base_type()) { - case reflection::Float: - case reflection::Double: SetAnyFieldF(table, field, strtod(val, nullptr)); - // TODO: support strings. - default: SetAnyFieldI(table, field, StringToInt(val)); break; - } +// Set any table field as a string, regardless of what type it is. +inline bool SetAnyFieldS(Table *table, const reflection::Field &field, + const char *val) { + auto field_ptr = table->GetAddressOf(field.offset()); + if (!field_ptr) return false; + SetAnyValueS(field.type()->base_type(), field_ptr, val); + return true; } +// Set any struct field as a 64bit int, regardless of type what it is. +inline void SetAnyFieldI(Struct *st, const reflection::Field &field, + int64_t val) { + SetAnyValueI(field.type()->base_type(), st->GetAddressOf(field.offset()), + val); +} + +// Set any struct field as a double, regardless of type what it is. +inline void SetAnyFieldF(Struct *st, const reflection::Field &field, + double val) { + SetAnyValueF(field.type()->base_type(), st->GetAddressOf(field.offset()), + val); +} + +// Set any struct field as a string, regardless of type what it is. +inline void SetAnyFieldS(Struct *st, const reflection::Field &field, + const char *val) { + SetAnyValueS(field.type()->base_type(), st->GetAddressOf(field.offset()), + val); +} + +// Set any vector element as a 64bit int, regardless of type what it is. +inline void SetAnyVectorElemI(VectorOfAny *vec, reflection::BaseType elem_type, + size_t i, int64_t val) { + SetAnyValueI(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + +// Set any vector element as a double, regardless of type what it is. +inline void SetAnyVectorElemF(VectorOfAny *vec, reflection::BaseType elem_type, + size_t i, double val) { + SetAnyValueF(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + +// Set any vector element as a string, regardless of type what it is. +inline void SetAnyVectorElemS(VectorOfAny *vec, reflection::BaseType elem_type, + size_t i, const char *val) { + SetAnyValueS(elem_type, vec->Data() + GetTypeSize(elem_type) * i, val); +} + + +// ------------------------- RESIZING SETTERS ------------------------- + // "smart" pointer for use with resizing vectors: turns a pointer inside // a vector into a relative offset, such that it is not affected by resizes. template class pointer_inside_vector { public: - pointer_inside_vector(const T *ptr, const std::vector &vec) - : offset_(reinterpret_cast(ptr) - - reinterpret_cast(vec.data())), + pointer_inside_vector(T *ptr, std::vector &vec) + : offset_(reinterpret_cast(ptr) - + reinterpret_cast(vec.data())), vec_(vec) {} - const T *operator*() const { - return reinterpret_cast( - reinterpret_cast(vec_.data()) + offset_); + T *operator*() const { + return reinterpret_cast( + reinterpret_cast(vec_.data()) + offset_); } - const T *operator->() const { + T *operator->() const { return operator*(); } void operator=(const pointer_inside_vector &piv); private: size_t offset_; - const std::vector &vec_; + std::vector &vec_; }; // Helper to create the above easily without specifying template args. -template pointer_inside_vector piv( - const T *ptr, const std::vector &vec) { +template pointer_inside_vector piv(T *ptr, + std::vector &vec) { return pointer_inside_vector(ptr, vec); } @@ -265,194 +349,66 @@ inline const reflection::Object &GetUnionType( return *enumval->object(); } -// Resize a FlatBuffer in-place by iterating through all offsets in the buffer -// and adjusting them by "delta" if they straddle the start offset. -// Once that is done, bytes can now be inserted/deleted safely. -// "delta" may be negative (shrinking). -// Unless "delta" is a multiple of the largest alignment, you'll create a small -// amount of garbage space in the buffer (usually 0..7 bytes). -// If your FlatBuffer's root table is not the schema's root table, you should -// pass in your root_table type as well. -class ResizeContext { - public: - ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta, - std::vector *flatbuf, - const reflection::Object *root_table = nullptr) - : schema_(schema), startptr_(flatbuf->data() + start), - delta_(delta), buf_(*flatbuf), - dag_check_(flatbuf->size() / sizeof(uoffset_t), false) { - auto mask = static_cast(sizeof(largest_scalar_t) - 1); - delta_ = (delta_ + mask) & ~mask; - if (!delta_) return; // We can't shrink by less than largest_scalar_t. - // Now change all the offsets by delta_. - auto root = GetAnyRoot(buf_.data()); - Straddle(buf_.data(), root, buf_.data()); - ResizeTable(root_table ? *root_table : *schema.root_table(), root); - // We can now add or remove bytes at start. - if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0); - else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_); - } - - // Check if the range between first (lower address) and second straddles - // the insertion point. If it does, change the offset at offsetloc (of - // type T, with direction D). - template void Straddle(void *first, void *second, - void *offsetloc) { - if (first <= startptr_ && second >= startptr_) { - WriteScalar(offsetloc, ReadScalar(offsetloc) + delta_ * D); - DagCheck(offsetloc) = true; - } - } - - // This returns a boolean that records if the corresponding offset location - // has been modified already. If so, we can't even read the corresponding - // offset, since it is pointing to a location that is illegal until the - // resize actually happens. - // This must be checked for every offset, since we can't know which offsets - // will straddle and which won't. - uint8_t &DagCheck(void *offsetloc) { - auto dag_idx = reinterpret_cast(offsetloc) - - reinterpret_cast(buf_.data()); - return dag_check_[dag_idx]; - } - - void ResizeTable(const reflection::Object &objectdef, Table *table) { - if (DagCheck(table)) - return; // Table already visited. - auto vtable = table->GetVTable(); - // Check if the vtable offset points beyond the insertion point. - Straddle(table, vtable, table); - // This direction shouldn't happen because vtables that sit before tables - // are always directly adjacent, but check just in case we ever change the - // way flatbuffers are built. - Straddle(vtable, table, table); - // Early out: since all fields inside the table must point forwards in - // memory, if the insertion point is before the table we can stop here. - auto tableloc = reinterpret_cast(table); - if (startptr_ <= tableloc) return; - // Check each field. - auto fielddefs = objectdef.fields(); - for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { - auto &fielddef = **it; - auto base_type = fielddef.type()->base_type(); - // Ignore scalars. - if (base_type <= reflection::Double) continue; - // Ignore fields that are not stored. - auto offset = table->GetOptionalFieldOffset(fielddef.offset()); - if (!offset) continue; - // Ignore structs. - auto subobjectdef = base_type == reflection::Obj ? - schema_.objects()->Get(fielddef.type()->index()) : nullptr; - if (subobjectdef && subobjectdef->is_struct()) continue; - // Get this fields' offset, and read it if safe. - auto offsetloc = tableloc + offset; - if (DagCheck(offsetloc)) - continue; // This offset already visited. - auto ref = offsetloc + ReadScalar(offsetloc); - Straddle(offsetloc, ref, offsetloc); - // Recurse. - switch (base_type) { - case reflection::Obj: { - ResizeTable(*subobjectdef, reinterpret_cast
    (ref)); - break; - } - case reflection::Vector: { - auto elem_type = fielddef.type()->element(); - if (elem_type != reflection::Obj && elem_type != reflection::String) - break; - auto vec = reinterpret_cast *>(ref); - auto elemobjectdef = elem_type == reflection::Obj - ? schema_.objects()->Get(fielddef.type()->index()) - : nullptr; - if (elemobjectdef && elemobjectdef->is_struct()) break; - for (uoffset_t i = 0; i < vec->size(); i++) { - auto loc = vec->Data() + i * sizeof(uoffset_t); - if (DagCheck(loc)) - continue; // This offset already visited. - auto dest = loc + vec->Get(i); - Straddle(loc, dest ,loc); - if (elemobjectdef) - ResizeTable(*elemobjectdef, reinterpret_cast
    (dest)); - } - break; - } - case reflection::Union: { - ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table), - reinterpret_cast
    (ref)); - break; - } - case reflection::String: - break; - default: - assert(false); - } - } - } - - void operator=(const ResizeContext &rc); - - private: - const reflection::Schema &schema_; - uint8_t *startptr_; - int delta_; - std::vector &buf_; - std::vector dag_check_; -}; - // Changes the contents of a string inside a FlatBuffer. FlatBuffer must // live inside a std::vector so we can resize the buffer if needed. // "str" must live inside "flatbuf" and may be invalidated after this call. // If your FlatBuffer's root table is not the schema's root table, you should // pass in your root_table type as well. -inline void SetString(const reflection::Schema &schema, const std::string &val, - const String *str, std::vector *flatbuf, - const reflection::Object *root_table = nullptr) { - auto delta = static_cast(val.size()) - static_cast(str->Length()); - auto start = static_cast(reinterpret_cast(str) - - flatbuf->data() + - sizeof(uoffset_t)); - if (delta) { - // Different size, we must expand (or contract). - ResizeContext(schema, start, delta, flatbuf, root_table); - if (delta < 0) { - // Clear the old string, since we don't want parts of it remaining. - memset(flatbuf->data() + start, 0, str->Length()); - } - } - // Copy new data. Safe because we created the right amount of space. - memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1); -} +void SetString(const reflection::Schema &schema, const std::string &val, + const String *str, std::vector *flatbuf, + const reflection::Object *root_table = nullptr); // Resizes a flatbuffers::Vector inside a FlatBuffer. FlatBuffer must // live inside a std::vector so we can resize the buffer if needed. // "vec" must live inside "flatbuf" and may be invalidated after this call. // If your FlatBuffer's root table is not the schema's root table, you should // pass in your root_table type as well. +uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, + const VectorOfAny *vec, uoffset_t num_elems, + uoffset_t elem_size, std::vector *flatbuf, + const reflection::Object *root_table = nullptr); + template void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, const Vector *vec, std::vector *flatbuf, const reflection::Object *root_table = nullptr) { auto delta_elem = static_cast(newsize) - static_cast(vec->size()); - auto delta_bytes = delta_elem * static_cast(sizeof(T)); - auto vec_start = reinterpret_cast(vec) - flatbuf->data(); - auto start = static_cast(vec_start + sizeof(uoffset_t) + - sizeof(T) * vec->size()); - if (delta_bytes) { - ResizeContext(schema, start, delta_bytes, flatbuf, root_table); - WriteScalar(flatbuf->data() + vec_start, newsize); // Length field. - // Set new elements to "val". - for (int i = 0; i < delta_elem; i++) { - auto loc = flatbuf->data() + start + i * sizeof(T); - auto is_scalar = std::is_scalar::value; - if (is_scalar) { - WriteScalar(loc, val); - } else { // struct - *reinterpret_cast(loc) = val; - } + auto newelems = ResizeAnyVector(schema, newsize, + reinterpret_cast(vec), + vec->size(), + static_cast(sizeof(T)), flatbuf, + root_table); + // Set new elements to "val". + for (int i = 0; i < delta_elem; i++) { + auto loc = newelems + i * sizeof(T); + auto is_scalar = std::is_scalar::value; + if (is_scalar) { + WriteScalar(loc, val); + } else { // struct + *reinterpret_cast(loc) = val; } } } +// Adds any new data (in the form of a new FlatBuffer) to an existing +// FlatBuffer. This can be used when any of the above methods are not +// sufficient, in particular for adding new tables and new fields. +// This is potentially slightly less efficient than a FlatBuffer constructed +// in one piece, since the new FlatBuffer doesn't share any vtables with the +// existing one. +// The return value can now be set using Vector::MutateOffset or SetFieldT +// below. +const uint8_t *AddFlatBuffer(std::vector &flatbuf, + const uint8_t *newbuf, size_t newlen); + +inline bool SetFieldT(Table *table, const reflection::Field &field, + const uint8_t *val) { + assert(sizeof(uoffset_t) == GetTypeSize(field.type()->base_type())); + return table->SetPointer(field.offset(), val); +} + +// ------------------------- COPYING ------------------------- + // Generic copying of tables from a FlatBuffer into a FlatBuffer builder. // Can be used to do any kind of merging/selecting you may want to do out // of existing buffers. Also useful to reconstruct a whole buffer if the @@ -461,134 +417,10 @@ void ResizeVector(const reflection::Schema &schema, uoffset_t newsize, T val, // Note: this does not deal with DAGs correctly. If the table passed forms a // DAG, the copy will be a tree instead (with duplicates). -inline void CopyInline(FlatBufferBuilder &fbb, - const reflection::Field &fielddef, - const Table &table, - size_t align, size_t size) { - fbb.Align(align); - fbb.PushBytes(table.GetStruct(fielddef.offset()), size); - fbb.TrackField(fielddef.offset(), fbb.GetSize()); -} - -inline Offset CopyTable(FlatBufferBuilder &fbb, - const reflection::Schema &schema, - const reflection::Object &objectdef, - const Table &table) { - // Before we can construct the table, we have to first generate any - // subobjects, and collect their offsets. - std::vector offsets; - auto fielddefs = objectdef.fields(); - for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { - auto &fielddef = **it; - // Skip if field is not present in the source. - if (!table.CheckField(fielddef.offset())) continue; - uoffset_t offset = 0; - switch (fielddef.type()->base_type()) { - case reflection::String: { - offset = fbb.CreateString(GetFieldS(table, fielddef)).o; - break; - } - case reflection::Obj: { - auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); - if (!subobjectdef.is_struct()) { - offset = CopyTable(fbb, schema, subobjectdef, - *GetFieldT(table, fielddef)).o; - } - break; - } - case reflection::Union: { - auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table); - offset = CopyTable(fbb, schema, subobjectdef, - *GetFieldT(table, fielddef)).o; - break; - } - case reflection::Vector: { - auto vec = table.GetPointer> *>( - fielddef.offset()); - auto element_base_type = fielddef.type()->element(); - auto elemobjectdef = element_base_type == reflection::Obj - ? schema.objects()->Get(fielddef.type()->index()) - : nullptr; - switch (element_base_type) { - case reflection::String: { - std::vector> elements(vec->size()); - auto vec_s = reinterpret_cast> *>(vec); - for (uoffset_t i = 0; i < vec_s->size(); i++) { - elements[i] = fbb.CreateString(vec_s->Get(i)).o; - } - offset = fbb.CreateVector(elements).o; - break; - } - case reflection::Obj: { - if (!elemobjectdef->is_struct()) { - std::vector> elements(vec->size()); - for (uoffset_t i = 0; i < vec->size(); i++) { - elements[i] = - CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i)); - } - offset = fbb.CreateVector(elements).o; - break; - } - // FALL-THRU: - } - default: { // Scalars and structs. - auto element_size = GetTypeSize(element_base_type); - if (elemobjectdef && elemobjectdef->is_struct()) - element_size = elemobjectdef->bytesize(); - fbb.StartVector(element_size, vec->size()); - fbb.PushBytes(vec->Data(), element_size * vec->size()); - offset = fbb.EndVector(vec->size()); - break; - } - } - break; - } - default: // Scalars. - break; - } - if (offset) { - offsets.push_back(offset); - } - } - // Now we can build the actual table from either offsets or scalar data. - auto start = objectdef.is_struct() - ? fbb.StartStruct(objectdef.minalign()) - : fbb.StartTable(); - size_t offset_idx = 0; - for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { - auto &fielddef = **it; - if (!table.CheckField(fielddef.offset())) continue; - auto base_type = fielddef.type()->base_type(); - switch (base_type) { - case reflection::Obj: { - auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); - if (subobjectdef.is_struct()) { - CopyInline(fbb, fielddef, table, subobjectdef.minalign(), - subobjectdef.bytesize()); - break; - } - // else: FALL-THRU: - } - case reflection::Union: - case reflection::String: - case reflection::Vector: - fbb.AddOffset(fielddef.offset(), Offset(offsets[offset_idx++])); - break; - default: { // Scalars. - auto size = GetTypeSize(base_type); - CopyInline(fbb, fielddef, table, size, size); - break; - } - } - } - assert(offset_idx == offsets.size()); - if (objectdef.is_struct()) { - fbb.ClearOffsets(); - return fbb.EndStruct(); - } else { - return fbb.EndTable(start, static_cast(fielddefs->size())); - } -} +Offset CopyTable(FlatBufferBuilder &fbb, + const reflection::Schema &schema, + const reflection::Object &objectdef, + const Table &table); } // namespace flatbuffers diff --git a/net/FlatBuffers/ByteBuffer.cs b/net/FlatBuffers/ByteBuffer.cs index fcdf8a1da..50c6fa960 100755 --- a/net/FlatBuffers/ByteBuffer.cs +++ b/net/FlatBuffers/ByteBuffer.cs @@ -48,6 +48,11 @@ namespace FlatBuffers set { _pos = value; } } + public void Reset() + { + _pos = 0; + } + // Pre-allocated helper arrays for convertion. private float[] floathelper = new[] { 0.0f }; private int[] inthelper = new[] { 0 }; diff --git a/net/FlatBuffers/FlatBufferBuilder.cs b/net/FlatBuffers/FlatBufferBuilder.cs index 8008f5ce7..39d386f40 100644 --- a/net/FlatBuffers/FlatBufferBuilder.cs +++ b/net/FlatBuffers/FlatBufferBuilder.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright 2014 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -49,6 +49,17 @@ namespace FlatBuffers _bb = new ByteBuffer(new byte[initialSize]); } + public void Clear() + { + _space = _bb.Length; + _bb.Reset(); + _minAlign = 1; + _vtable = null; + _objectStart = 0; + _vtables = new int[16]; + _numVtables = 0; + _vectorNumElems = 0; + } public int Offset { get { return _bb.Length - _space; } } @@ -195,10 +206,10 @@ namespace FlatBuffers Prep(alignment, elemSize * count); // Just in case alignment > int. } - public int EndVector() + public VectorOffset EndVector() { PutInt(_vectorNumElems); - return Offset; + return new VectorOffset(Offset); } public void Nested(int obj) @@ -249,7 +260,7 @@ namespace FlatBuffers public void AddDouble(int o, double x, double d) { if (x != d) { AddDouble(x); Slot(o); } } public void AddOffset(int o, int x, int d) { if (x != d) { AddOffset(x); Slot(o); } } - public int CreateString(string s) + public StringOffset CreateString(string s) { NotNested(); byte[] utf8 = Encoding.UTF8.GetBytes(s); @@ -257,7 +268,7 @@ namespace FlatBuffers StartVector(1, utf8.Length, 1); Buffer.BlockCopy(utf8, 0, _bb.Data, _space -= utf8.Length, utf8.Length); - return EndVector(); + return new StringOffset(EndVector().Value); } // Structs are stored inline, so nothing additional is being added. diff --git a/net/FlatBuffers/FlatBuffers.csproj b/net/FlatBuffers/FlatBuffers.csproj index 9307c33f9..3ae938a15 100644 --- a/net/FlatBuffers/FlatBuffers.csproj +++ b/net/FlatBuffers/FlatBuffers.csproj @@ -37,6 +37,7 @@ + diff --git a/net/FlatBuffers/Offset.cs b/net/FlatBuffers/Offset.cs new file mode 100644 index 000000000..a1524df0b --- /dev/null +++ b/net/FlatBuffers/Offset.cs @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace FlatBuffers +{ + /// + /// Offset class for typesafe assignments. + /// + public struct Offset where T : class + { + public int Value; + public Offset(int value) + { + Value = value; + } + } + + public struct StringOffset + { + public int Value; + public StringOffset(int value) + { + Value = value; + } + } + + public struct VectorOffset + { + public int Value; + public VectorOffset(int value) + { + Value = value; + } + } +} diff --git a/src/flatc.cpp b/src/flatc.cpp index 91ecb25b5..9b8b50d70 100755 --- a/src/flatc.cpp +++ b/src/flatc.cpp @@ -90,7 +90,9 @@ static void Error(const std::string &err, bool usage, bool show_exe_name) { " --defaults-json Output fields whose value is the default when\n" " writing JSON\n" " --no-prefix Don\'t prefix enum values with the enum type in C++.\n" - " --gen-includes (deprecated), instead use:\n" + " --gen-includes (deprecated), this is the default behavior.\n" + " If the original behavior is required (no include\n" + " statements) use --no-includes.\n" " --no-includes Don\'t generate include statements for included\n" " schemas the generated file depends on (C++).\n" " --gen-mutable Generate accessors that can mutate buffers in-place.\n" diff --git a/src/idl_gen_general.cpp b/src/idl_gen_general.cpp index faa97059a..fb55a80cf 100644 --- a/src/idl_gen_general.cpp +++ b/src/idl_gen_general.cpp @@ -19,6 +19,7 @@ #include "flatbuffers/flatbuffers.h" #include "flatbuffers/idl.h" #include "flatbuffers/util.h" +#include namespace flatbuffers { @@ -202,6 +203,10 @@ static std::string GenTypeBasic(const LanguageParameters &lang, #undef FLATBUFFERS_TD }; + if(lang.language == GeneratorOptions::kCSharp && type.base_type == BASE_TYPE_STRUCT) { + return "Offset<" + type.struct_def->name + ">"; + } + return gtypename[type.base_type * GeneratorOptions::kMAX + lang.language]; } @@ -258,6 +263,32 @@ static Type DestinationType(const LanguageParameters &lang, const Type &type, } } +static std::string GenOffsetType(const LanguageParameters &lang, const StructDef &struct_def) { + if(lang.language == GeneratorOptions::kCSharp) { + return "Offset<" + struct_def.name + ">"; + } else { + return "int"; + } +} + +static std::string GenOffsetConstruct(const LanguageParameters &lang, + const StructDef &struct_def, + const std::string &variable_name) +{ + if(lang.language == GeneratorOptions::kCSharp) { + return "new Offset<" + struct_def.name + ">(" + variable_name + ")"; + } + return variable_name; +} + +static std::string GenVectorOffsetType(const LanguageParameters &lang) { + if(lang.language == GeneratorOptions::kCSharp) { + return "VectorOffset"; + } else { + return "int"; + } +} + // Generate destination type name static std::string GenTypeNameDest(const LanguageParameters &lang, const Type &type) { @@ -353,7 +384,20 @@ static std::string SourceCast(const LanguageParameters &lang, return ""; } -static std::string GenDefaultValue(const Value &value) { +static std::string GenDefaultValue(const LanguageParameters &lang, const Value &value, bool for_buffer) { + if(lang.language == GeneratorOptions::kCSharp && !for_buffer) { + switch(value.type.base_type) { + case BASE_TYPE_STRING: + return "default(StringOffset)"; + case BASE_TYPE_STRUCT: + return "default(Offset<" + value.type.struct_def->name + ">)"; + case BASE_TYPE_VECTOR: + return "default(VectorOffset)"; + default: + break; + } + } + return value.type.base_type == BASE_TYPE_BOOL ? (value.constant == "0" ? "false" : "true") : value.constant; @@ -658,7 +702,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } else { code += offset_prefix + getter; code += "(o + bb_pos)" + dest_mask + " : " + default_cast; - code += GenDefaultValue(field.value); + code += GenDefaultValue(lang, field.value, false); } } else { switch (field.value.type.base_type) { @@ -765,13 +809,14 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "\n"; if (struct_def.fixed) { // create a struct constructor function - code += " public static int " + FunctionStart(lang, 'C') + "reate"; + code += " public static " + GenOffsetType(lang, struct_def) + " "; + code += FunctionStart(lang, 'C') + "reate"; code += struct_def.name + "(FlatBufferBuilder builder"; GenStructArgs(lang, struct_def, code_ptr, ""); code += ") {\n"; GenStructBody(lang, struct_def, code_ptr, ""); - code += " return builder."; - code += lang.get_fbb_offset; + code += " return "; + code += GenOffsetConstruct(lang, struct_def, "builder." + std::string(lang.get_fbb_offset)); code += ";\n }\n"; } else { // Generate a method that creates a table in one go. This is only possible @@ -791,9 +836,9 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, } if (has_no_struct_fields && num_fields) { // Generate a table constructor of the form: - // public static void createName(FlatBufferBuilder builder, args...) - code += " public static int " + FunctionStart(lang, 'C') + "reate"; - code += struct_def.name; + // public static int createName(FlatBufferBuilder builder, args...) + code += " public static " + GenOffsetType(lang, struct_def) + " "; + code += FunctionStart(lang, 'C') + "reate" + struct_def.name; code += "(FlatBufferBuilder builder"; for (auto it = struct_def.fields.vec.begin(); it != struct_def.fields.vec.end(); ++it) { @@ -807,7 +852,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, // Java doesn't have defaults, which means this method must always // supply all arguments, and thus won't compile when fields are added. if (lang.language != GeneratorOptions::kJava) { - code += " = " + GenDefaultValue(field.value); + code += " = " + GenDefaultValue(lang, field.value, false); } } code += ") {\n builder."; @@ -857,7 +902,10 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += GenMethod(lang, field.value.type) + "("; code += NumToString(it - struct_def.fields.vec.begin()) + ", "; code += DestinationValue(lang, argname, field.value.type); - code += ", " + GenDefaultValue(field.value); + if(!IsScalar(field.value.type.base_type) && field.value.type.base_type != BASE_TYPE_UNION && lang.language == GeneratorOptions::kCSharp) { + code += ".Value"; + } + code += ", " + GenDefaultValue(lang, field.value, true); code += "); }\n"; if (field.value.type.base_type == BASE_TYPE_VECTOR) { auto vector_type = field.value.type.VectorType(); @@ -865,7 +913,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, auto elem_size = InlineSize(vector_type); if (!IsStruct(vector_type)) { // Generate a method to create a vector from a Java array. - code += " public static int " + FunctionStart(lang, 'C') + "reate"; + code += " public static " + GenVectorOffsetType(lang) + " " + FunctionStart(lang, 'C') + "reate"; code += MakeCamel(field.name); code += "Vector(FlatBufferBuilder builder, "; code += GenTypeBasic(lang, vector_type) + "[] data) "; @@ -877,8 +925,12 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += FunctionStart(lang, 'L') + "ength - 1; i >= 0; i--) builder."; code += FunctionStart(lang, 'A') + "dd"; code += GenMethod(lang, vector_type); - code += "(data[i]); return builder."; - code += FunctionStart(lang, 'E') + "ndVector(); }\n"; + code += "(data[i]"; + if(lang.language == GeneratorOptions::kCSharp && + (vector_type.base_type == BASE_TYPE_STRUCT || vector_type.base_type == BASE_TYPE_STRING)) + code += ".Value"; + code += "); return "; + code += "builder." + FunctionStart(lang, 'E') + "ndVector(); }\n"; } // Generate a method to start a vector, data to be added manually after. code += " public static void " + FunctionStart(lang, 'S') + "tart"; @@ -890,7 +942,7 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "); }\n"; } } - code += " public static int "; + code += " public static " + GenOffsetType(lang, struct_def) + " "; code += FunctionStart(lang, 'E') + "nd" + struct_def.name; code += "(FlatBufferBuilder builder) {\n int o = builder."; code += FunctionStart(lang, 'E') + "ndObject();\n"; @@ -904,12 +956,16 @@ static void GenStruct(const LanguageParameters &lang, const Parser &parser, code += "); // " + field.name + "\n"; } } - code += " return o;\n }\n"; + code += " return " + GenOffsetConstruct(lang, struct_def, "o") + ";\n }\n"; if (parser.root_struct_def_ == &struct_def) { code += " public static void "; code += FunctionStart(lang, 'F') + "inish" + struct_def.name; - code += "Buffer(FlatBufferBuilder builder, int offset) { "; - code += "builder." + FunctionStart(lang, 'F') + "inish(offset"; + code += "Buffer(FlatBufferBuilder builder, " + GenOffsetType(lang, struct_def) + " offset) {"; + code += " builder." + FunctionStart(lang, 'F') + "inish(offset"; + if (lang.language == GeneratorOptions::kCSharp) { + code += ".Value"; + } + if (parser.file_identifier_.length()) code += ", \"" + parser.file_identifier_ + "\""; code += "); }\n"; diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index bf4b931db..ad5ff2ac6 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -918,10 +918,10 @@ void Parser::ParseDecl() { auto align = static_cast(atoi(force_align->constant.c_str())); if (force_align->type.base_type != BASE_TYPE_INT || align < struct_def.minalign || - align > 256 || + align > 16 || align & (align - 1)) Error("force_align must be a power of two integer ranging from the" - "struct\'s natural alignment to 256"); + "struct\'s natural alignment to 16"); struct_def.minalign = align; } struct_def.PadLastField(struct_def.minalign); diff --git a/src/reflection.cpp b/src/reflection.cpp new file mode 100644 index 000000000..35ae16feb --- /dev/null +++ b/src/reflection.cpp @@ -0,0 +1,471 @@ +/* + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "flatbuffers/reflection.h" +#include "flatbuffers/util.h" + +// Helper functionality for reflection. + +namespace flatbuffers { + +int64_t GetAnyValueI(reflection::BaseType type, const uint8_t *data) { +# define FLATBUFFERS_GET(T) static_cast(ReadScalar(data)) + switch (type) { + case reflection::UType: + case reflection::Bool: + case reflection::UByte: return FLATBUFFERS_GET(uint8_t); + case reflection::Byte: return FLATBUFFERS_GET(int8_t); + case reflection::Short: return FLATBUFFERS_GET(int16_t); + case reflection::UShort: return FLATBUFFERS_GET(uint16_t); + case reflection::Int: return FLATBUFFERS_GET(int32_t); + case reflection::UInt: return FLATBUFFERS_GET(uint32_t); + case reflection::Long: return FLATBUFFERS_GET(int64_t); + case reflection::ULong: return FLATBUFFERS_GET(uint64_t); + case reflection::Float: return FLATBUFFERS_GET(float); + case reflection::Double: return FLATBUFFERS_GET(double); + case reflection::String: { + auto s = reinterpret_cast(ReadScalar(data) + + data); + return s ? StringToInt(s->c_str()) : 0; + } + default: return 0; // Tables & vectors do not make sense. + } +# undef FLATBUFFERS_GET +} + +double GetAnyValueF(reflection::BaseType type, const uint8_t *data) { + switch (type) { + case reflection::Float: return static_cast(ReadScalar(data)); + case reflection::Double: return ReadScalar(data); + case reflection::String: { + auto s = reinterpret_cast(ReadScalar(data) + + data); + return s ? strtod(s->c_str(), nullptr) : 0.0; + } + default: return static_cast(GetAnyValueI(type, data)); + } +} + +std::string GetAnyValueS(reflection::BaseType type, const uint8_t *data, + const reflection::Schema *schema, int type_index) { + switch (type) { + case reflection::Float: + case reflection::Double: return NumToString(GetAnyValueF(type, data)); + case reflection::String: { + auto s = reinterpret_cast(ReadScalar(data) + + data); + return s ? s->c_str() : ""; + } + case reflection::Obj: + if (schema) { + // Convert the table to a string. This is mostly for debugging purposes, + // and does NOT promise to be JSON compliant. + // Also prefixes the type. + auto &objectdef = *schema->objects()->Get(type_index); + auto s = objectdef.name()->str(); + if (objectdef.is_struct()) { + s += "(struct)"; // TODO: implement this as well. + } else { + auto table_field = reinterpret_cast( + ReadScalar(data) + data); + s += " { "; + auto fielddefs = objectdef.fields(); + for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { + auto &fielddef = **it; + if (!table_field->CheckField(fielddef.offset())) continue; + auto val = GetAnyFieldS(*table_field, fielddef, schema); + if (fielddef.type()->base_type() == reflection::String) + val = "\"" + val + "\""; // Doesn't deal with escape codes etc. + s += fielddef.name()->str(); + s += ": "; + s += val; + s += ", "; + } + s += "}"; + } + return s; + } else { + return "(table)"; + } + case reflection::Vector: + return "[(elements)]"; // TODO: implement this as well. + case reflection::Union: + return "(union)"; // TODO: implement this as well. + default: return NumToString(GetAnyValueI(type, data)); + } +} + +void SetAnyValueI(reflection::BaseType type, uint8_t *data, int64_t val) { +# define FLATBUFFERS_SET(T) WriteScalar(data, static_cast(val)) + switch (type) { + case reflection::UType: + case reflection::Bool: + case reflection::UByte: FLATBUFFERS_SET(uint8_t ); break; + case reflection::Byte: FLATBUFFERS_SET(int8_t ); break; + case reflection::Short: FLATBUFFERS_SET(int16_t ); break; + case reflection::UShort: FLATBUFFERS_SET(uint16_t); break; + case reflection::Int: FLATBUFFERS_SET(int32_t ); break; + case reflection::UInt: FLATBUFFERS_SET(uint32_t); break; + case reflection::Long: FLATBUFFERS_SET(int64_t ); break; + case reflection::ULong: FLATBUFFERS_SET(uint64_t); break; + case reflection::Float: FLATBUFFERS_SET(float ); break; + case reflection::Double: FLATBUFFERS_SET(double ); break; + // TODO: support strings + default: break; + } +# undef FLATBUFFERS_SET +} + +void SetAnyValueF(reflection::BaseType type, uint8_t *data, double val) { + switch (type) { + case reflection::Float: WriteScalar(data, static_cast(val)); break; + case reflection::Double: WriteScalar(data, val); break; + // TODO: support strings. + default: SetAnyValueI(type, data, static_cast(val)); break; + } +} + +void SetAnyValueS(reflection::BaseType type, uint8_t *data, const char *val) { + switch (type) { + case reflection::Float: + case reflection::Double: + SetAnyValueF(type, data, strtod(val, nullptr)); + break; + // TODO: support strings. + default: SetAnyValueI(type, data, StringToInt(val)); break; + } +} + +// Resize a FlatBuffer in-place by iterating through all offsets in the buffer +// and adjusting them by "delta" if they straddle the start offset. +// Once that is done, bytes can now be inserted/deleted safely. +// "delta" may be negative (shrinking). +// Unless "delta" is a multiple of the largest alignment, you'll create a small +// amount of garbage space in the buffer (usually 0..7 bytes). +// If your FlatBuffer's root table is not the schema's root table, you should +// pass in your root_table type as well. +class ResizeContext { + public: + ResizeContext(const reflection::Schema &schema, uoffset_t start, int delta, + std::vector *flatbuf, + const reflection::Object *root_table = nullptr) + : schema_(schema), startptr_(flatbuf->data() + start), + delta_(delta), buf_(*flatbuf), + dag_check_(flatbuf->size() / sizeof(uoffset_t), false) { + auto mask = static_cast(sizeof(largest_scalar_t) - 1); + delta_ = (delta_ + mask) & ~mask; + if (!delta_) return; // We can't shrink by less than largest_scalar_t. + // Now change all the offsets by delta_. + auto root = GetAnyRoot(buf_.data()); + Straddle(buf_.data(), root, buf_.data()); + ResizeTable(root_table ? *root_table : *schema.root_table(), root); + // We can now add or remove bytes at start. + if (delta_ > 0) buf_.insert(buf_.begin() + start, delta_, 0); + else buf_.erase(buf_.begin() + start, buf_.begin() + start - delta_); + } + + // Check if the range between first (lower address) and second straddles + // the insertion point. If it does, change the offset at offsetloc (of + // type T, with direction D). + template void Straddle(void *first, void *second, + void *offsetloc) { + if (first <= startptr_ && second >= startptr_) { + WriteScalar(offsetloc, ReadScalar(offsetloc) + delta_ * D); + DagCheck(offsetloc) = true; + } + } + + // This returns a boolean that records if the corresponding offset location + // has been modified already. If so, we can't even read the corresponding + // offset, since it is pointing to a location that is illegal until the + // resize actually happens. + // This must be checked for every offset, since we can't know which offsets + // will straddle and which won't. + uint8_t &DagCheck(void *offsetloc) { + auto dag_idx = reinterpret_cast(offsetloc) - + reinterpret_cast(buf_.data()); + return dag_check_[dag_idx]; + } + + void ResizeTable(const reflection::Object &objectdef, Table *table) { + if (DagCheck(table)) + return; // Table already visited. + auto vtable = table->GetVTable(); + // Check if the vtable offset points beyond the insertion point. + Straddle(table, vtable, table); + // This direction shouldn't happen because vtables that sit before tables + // are always directly adjacent, but check just in case we ever change the + // way flatbuffers are built. + Straddle(vtable, table, table); + // Early out: since all fields inside the table must point forwards in + // memory, if the insertion point is before the table we can stop here. + auto tableloc = reinterpret_cast(table); + if (startptr_ <= tableloc) return; + // Check each field. + auto fielddefs = objectdef.fields(); + for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { + auto &fielddef = **it; + auto base_type = fielddef.type()->base_type(); + // Ignore scalars. + if (base_type <= reflection::Double) continue; + // Ignore fields that are not stored. + auto offset = table->GetOptionalFieldOffset(fielddef.offset()); + if (!offset) continue; + // Ignore structs. + auto subobjectdef = base_type == reflection::Obj ? + schema_.objects()->Get(fielddef.type()->index()) : nullptr; + if (subobjectdef && subobjectdef->is_struct()) continue; + // Get this fields' offset, and read it if safe. + auto offsetloc = tableloc + offset; + if (DagCheck(offsetloc)) + continue; // This offset already visited. + auto ref = offsetloc + ReadScalar(offsetloc); + Straddle(offsetloc, ref, offsetloc); + // Recurse. + switch (base_type) { + case reflection::Obj: { + ResizeTable(*subobjectdef, reinterpret_cast
    (ref)); + break; + } + case reflection::Vector: { + auto elem_type = fielddef.type()->element(); + if (elem_type != reflection::Obj && elem_type != reflection::String) + break; + auto vec = reinterpret_cast *>(ref); + auto elemobjectdef = elem_type == reflection::Obj + ? schema_.objects()->Get(fielddef.type()->index()) + : nullptr; + if (elemobjectdef && elemobjectdef->is_struct()) break; + for (uoffset_t i = 0; i < vec->size(); i++) { + auto loc = vec->Data() + i * sizeof(uoffset_t); + if (DagCheck(loc)) + continue; // This offset already visited. + auto dest = loc + vec->Get(i); + Straddle(loc, dest ,loc); + if (elemobjectdef) + ResizeTable(*elemobjectdef, reinterpret_cast
    (dest)); + } + break; + } + case reflection::Union: { + ResizeTable(GetUnionType(schema_, objectdef, fielddef, *table), + reinterpret_cast
    (ref)); + break; + } + case reflection::String: + break; + default: + assert(false); + } + } + } + + void operator=(const ResizeContext &rc); + + private: + const reflection::Schema &schema_; + uint8_t *startptr_; + int delta_; + std::vector &buf_; + std::vector dag_check_; +}; + +void SetString(const reflection::Schema &schema, const std::string &val, + const String *str, std::vector *flatbuf, + const reflection::Object *root_table) { + auto delta = static_cast(val.size()) - static_cast(str->Length()); + auto start = static_cast(reinterpret_cast(str) - + flatbuf->data() + + sizeof(uoffset_t)); + if (delta) { + // Clear the old string, since we don't want parts of it remaining. + memset(flatbuf->data() + start, 0, str->Length()); + // Different size, we must expand (or contract). + ResizeContext(schema, start, delta, flatbuf, root_table); + } + // Copy new data. Safe because we created the right amount of space. + memcpy(flatbuf->data() + start, val.c_str(), val.size() + 1); +} + +uint8_t *ResizeAnyVector(const reflection::Schema &schema, uoffset_t newsize, + const VectorOfAny *vec, uoffset_t num_elems, + uoffset_t elem_size, std::vector *flatbuf, + const reflection::Object *root_table) { + auto delta_elem = static_cast(newsize) - static_cast(num_elems); + auto delta_bytes = delta_elem * static_cast(elem_size); + auto vec_start = reinterpret_cast(vec) - flatbuf->data(); + auto start = static_cast(vec_start + sizeof(uoffset_t) + + elem_size * num_elems); + if (delta_bytes) { + if (delta_elem < 0) { + // Clear elements we're throwing away, since some might remain in the + // buffer. + auto size_clear = -delta_elem * elem_size; + memset(flatbuf->data() + start - size_clear, 0, size_clear); + } + ResizeContext(schema, start, delta_bytes, flatbuf, root_table); + WriteScalar(flatbuf->data() + vec_start, newsize); // Length field. + // Set new elements to 0.. this can be overwritten by the caller. + if (delta_elem > 0) { + memset(flatbuf->data() + start, 0, delta_elem * elem_size); + } + } + return flatbuf->data() + start; +} + +const uint8_t *AddFlatBuffer(std::vector &flatbuf, + const uint8_t *newbuf, size_t newlen) { + // Align to sizeof(uoffset_t) past sizeof(largest_scalar_t) since we're + // going to chop off the root offset. + while ((flatbuf.size() & (sizeof(uoffset_t) - 1)) || + !(flatbuf.size() & (sizeof(largest_scalar_t) - 1))) { + flatbuf.push_back(0); + } + auto insertion_point = static_cast(flatbuf.size()); + // Insert the entire FlatBuffer minus the root pointer. + flatbuf.insert(flatbuf.end(), newbuf + sizeof(uoffset_t), newbuf + newlen); + auto root_offset = ReadScalar(newbuf) - sizeof(uoffset_t); + return flatbuf.data() + insertion_point + root_offset; +} + +void CopyInline(FlatBufferBuilder &fbb, const reflection::Field &fielddef, + const Table &table, size_t align, size_t size) { + fbb.Align(align); + fbb.PushBytes(table.GetStruct(fielddef.offset()), size); + fbb.TrackField(fielddef.offset(), fbb.GetSize()); +} + +Offset CopyTable(FlatBufferBuilder &fbb, + const reflection::Schema &schema, + const reflection::Object &objectdef, + const Table &table) { + // Before we can construct the table, we have to first generate any + // subobjects, and collect their offsets. + std::vector offsets; + auto fielddefs = objectdef.fields(); + for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { + auto &fielddef = **it; + // Skip if field is not present in the source. + if (!table.CheckField(fielddef.offset())) continue; + uoffset_t offset = 0; + switch (fielddef.type()->base_type()) { + case reflection::String: { + offset = fbb.CreateString(GetFieldS(table, fielddef)).o; + break; + } + case reflection::Obj: { + auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); + if (!subobjectdef.is_struct()) { + offset = CopyTable(fbb, schema, subobjectdef, + *GetFieldT(table, fielddef)).o; + } + break; + } + case reflection::Union: { + auto &subobjectdef = GetUnionType(schema, objectdef, fielddef, table); + offset = CopyTable(fbb, schema, subobjectdef, + *GetFieldT(table, fielddef)).o; + break; + } + case reflection::Vector: { + auto vec = table.GetPointer> *>( + fielddef.offset()); + auto element_base_type = fielddef.type()->element(); + auto elemobjectdef = element_base_type == reflection::Obj + ? schema.objects()->Get(fielddef.type()->index()) + : nullptr; + switch (element_base_type) { + case reflection::String: { + std::vector> elements(vec->size()); + auto vec_s = reinterpret_cast> *>(vec); + for (uoffset_t i = 0; i < vec_s->size(); i++) { + elements[i] = fbb.CreateString(vec_s->Get(i)).o; + } + offset = fbb.CreateVector(elements).o; + break; + } + case reflection::Obj: { + if (!elemobjectdef->is_struct()) { + std::vector> elements(vec->size()); + for (uoffset_t i = 0; i < vec->size(); i++) { + elements[i] = + CopyTable(fbb, schema, *elemobjectdef, *vec->Get(i)); + } + offset = fbb.CreateVector(elements).o; + break; + } + // FALL-THRU: + } + default: { // Scalars and structs. + auto element_size = GetTypeSize(element_base_type); + if (elemobjectdef && elemobjectdef->is_struct()) + element_size = elemobjectdef->bytesize(); + fbb.StartVector(element_size, vec->size()); + fbb.PushBytes(vec->Data(), element_size * vec->size()); + offset = fbb.EndVector(vec->size()); + break; + } + } + break; + } + default: // Scalars. + break; + } + if (offset) { + offsets.push_back(offset); + } + } + // Now we can build the actual table from either offsets or scalar data. + auto start = objectdef.is_struct() + ? fbb.StartStruct(objectdef.minalign()) + : fbb.StartTable(); + size_t offset_idx = 0; + for (auto it = fielddefs->begin(); it != fielddefs->end(); ++it) { + auto &fielddef = **it; + if (!table.CheckField(fielddef.offset())) continue; + auto base_type = fielddef.type()->base_type(); + switch (base_type) { + case reflection::Obj: { + auto &subobjectdef = *schema.objects()->Get(fielddef.type()->index()); + if (subobjectdef.is_struct()) { + CopyInline(fbb, fielddef, table, subobjectdef.minalign(), + subobjectdef.bytesize()); + break; + } + // else: FALL-THRU: + } + case reflection::Union: + case reflection::String: + case reflection::Vector: + fbb.AddOffset(fielddef.offset(), Offset(offsets[offset_idx++])); + break; + default: { // Scalars. + auto size = GetTypeSize(base_type); + CopyInline(fbb, fielddef, table, size, size); + break; + } + } + } + assert(offset_idx == offsets.size()); + if (objectdef.is_struct()) { + fbb.ClearOffsets(); + return fbb.EndStruct(); + } else { + return fbb.EndTable(start, static_cast(fielddefs->size())); + } +} + +} // namespace flatbuffers diff --git a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj index 1a30cd424..fd1c60205 100644 --- a/tests/FlatBuffers.Test/FlatBuffers.Test.csproj +++ b/tests/FlatBuffers.Test/FlatBuffers.Test.csproj @@ -41,6 +41,9 @@ FlatBuffers\ByteBuffer.cs + + FlatBuffers\Offset.cs + FlatBuffers\FlatBufferBuilder.cs diff --git a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs index ddeda7a66..436180caf 100644 --- a/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs +++ b/tests/FlatBuffers.Test/FlatBuffersExampleTests.cs @@ -61,8 +61,8 @@ namespace FlatBuffers.Test var test4 = fbb.EndVector(); Monster.StartTestarrayofstringVector(fbb, 2); - fbb.AddOffset(test2); - fbb.AddOffset(test1); + fbb.AddOffset(test2.Value); + fbb.AddOffset(test1.Value); var testArrayOfString = fbb.EndVector(); Monster.StartMonster(fbb); @@ -72,7 +72,7 @@ namespace FlatBuffers.Test Monster.AddName(fbb, str); Monster.AddInventory(fbb, inv); Monster.AddTestType(fbb, Any.Monster); - Monster.AddTest(fbb, mon2); + Monster.AddTest(fbb, mon2.Value); Monster.AddTest4(fbb, test4); Monster.AddTestarrayofstring(fbb, testArrayOfString); Monster.AddTestbool(fbb, false); @@ -80,6 +80,7 @@ namespace FlatBuffers.Test Monster.FinishMonsterBuffer(fbb, mon); + // Dump to output directory so we can inspect later, if needed using (var ms = new MemoryStream(fbb.DataBuffer.Data, fbb.DataBuffer.Position, fbb.Offset)) { diff --git a/tests/MyGame/Example/Monster.cs b/tests/MyGame/Example/Monster.cs index e97ae4af6..ef0ef92ec 100644 --- a/tests/MyGame/Example/Monster.cs +++ b/tests/MyGame/Example/Monster.cs @@ -61,29 +61,29 @@ public sealed class Monster : Table { public bool MutateTesthashu64Fnv1a(ulong testhashu64_fnv1a) { int o = __offset(50); if (o != 0) { bb.PutUlong(o + bb_pos, testhashu64_fnv1a); return true; } else { return false; } } public static void StartMonster(FlatBufferBuilder builder) { builder.StartObject(24); } - public static void AddPos(FlatBufferBuilder builder, int posOffset) { builder.AddStruct(0, posOffset, 0); } + public static void AddPos(FlatBufferBuilder builder, Offset posOffset) { builder.AddStruct(0, posOffset.Value, 0); } public static void AddMana(FlatBufferBuilder builder, short mana) { builder.AddShort(1, mana, 150); } public static void AddHp(FlatBufferBuilder builder, short hp) { builder.AddShort(2, hp, 100); } - public static void AddName(FlatBufferBuilder builder, int nameOffset) { builder.AddOffset(3, nameOffset, 0); } - public static void AddInventory(FlatBufferBuilder builder, int inventoryOffset) { builder.AddOffset(5, inventoryOffset, 0); } - public static int CreateInventoryVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); } + public static void AddName(FlatBufferBuilder builder, StringOffset nameOffset) { builder.AddOffset(3, nameOffset.Value, 0); } + public static void AddInventory(FlatBufferBuilder builder, VectorOffset inventoryOffset) { builder.AddOffset(5, inventoryOffset.Value, 0); } + public static VectorOffset CreateInventoryVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); } public static void StartInventoryVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); } public static void AddColor(FlatBufferBuilder builder, Color color) { builder.AddSbyte(6, (sbyte)(color), 8); } public static void AddTestType(FlatBufferBuilder builder, Any testType) { builder.AddByte(7, (byte)(testType), 0); } public static void AddTest(FlatBufferBuilder builder, int testOffset) { builder.AddOffset(8, testOffset, 0); } - public static void AddTest4(FlatBufferBuilder builder, int test4Offset) { builder.AddOffset(9, test4Offset, 0); } + public static void AddTest4(FlatBufferBuilder builder, VectorOffset test4Offset) { builder.AddOffset(9, test4Offset.Value, 0); } public static void StartTest4Vector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 2); } - public static void AddTestarrayofstring(FlatBufferBuilder builder, int testarrayofstringOffset) { builder.AddOffset(10, testarrayofstringOffset, 0); } - public static int CreateTestarrayofstringVector(FlatBufferBuilder builder, int[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i]); return builder.EndVector(); } + public static void AddTestarrayofstring(FlatBufferBuilder builder, VectorOffset testarrayofstringOffset) { builder.AddOffset(10, testarrayofstringOffset.Value, 0); } + public static VectorOffset CreateTestarrayofstringVector(FlatBufferBuilder builder, StringOffset[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); } public static void StartTestarrayofstringVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } - public static void AddTestarrayoftables(FlatBufferBuilder builder, int testarrayoftablesOffset) { builder.AddOffset(11, testarrayoftablesOffset, 0); } - public static int CreateTestarrayoftablesVector(FlatBufferBuilder builder, int[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i]); return builder.EndVector(); } + public static void AddTestarrayoftables(FlatBufferBuilder builder, VectorOffset testarrayoftablesOffset) { builder.AddOffset(11, testarrayoftablesOffset.Value, 0); } + public static VectorOffset CreateTestarrayoftablesVector(FlatBufferBuilder builder, Offset[] data) { builder.StartVector(4, data.Length, 4); for (int i = data.Length - 1; i >= 0; i--) builder.AddOffset(data[i].Value); return builder.EndVector(); } public static void StartTestarrayoftablesVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(4, numElems, 4); } - public static void AddEnemy(FlatBufferBuilder builder, int enemyOffset) { builder.AddOffset(12, enemyOffset, 0); } - public static void AddTestnestedflatbuffer(FlatBufferBuilder builder, int testnestedflatbufferOffset) { builder.AddOffset(13, testnestedflatbufferOffset, 0); } - public static int CreateTestnestedflatbufferVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); } + public static void AddEnemy(FlatBufferBuilder builder, Offset enemyOffset) { builder.AddOffset(12, enemyOffset.Value, 0); } + public static void AddTestnestedflatbuffer(FlatBufferBuilder builder, VectorOffset testnestedflatbufferOffset) { builder.AddOffset(13, testnestedflatbufferOffset.Value, 0); } + public static VectorOffset CreateTestnestedflatbufferVector(FlatBufferBuilder builder, byte[] data) { builder.StartVector(1, data.Length, 1); for (int i = data.Length - 1; i >= 0; i--) builder.AddByte(data[i]); return builder.EndVector(); } public static void StartTestnestedflatbufferVector(FlatBufferBuilder builder, int numElems) { builder.StartVector(1, numElems, 1); } - public static void AddTestempty(FlatBufferBuilder builder, int testemptyOffset) { builder.AddOffset(14, testemptyOffset, 0); } + public static void AddTestempty(FlatBufferBuilder builder, Offset testemptyOffset) { builder.AddOffset(14, testemptyOffset.Value, 0); } public static void AddTestbool(FlatBufferBuilder builder, bool testbool) { builder.AddBool(15, testbool, false); } public static void AddTesthashs32Fnv1(FlatBufferBuilder builder, int testhashs32Fnv1) { builder.AddInt(16, testhashs32Fnv1, 0); } public static void AddTesthashu32Fnv1(FlatBufferBuilder builder, uint testhashu32Fnv1) { builder.AddUint(17, testhashu32Fnv1, 0); } @@ -93,12 +93,12 @@ public sealed class Monster : Table { public static void AddTesthashu32Fnv1a(FlatBufferBuilder builder, uint testhashu32Fnv1a) { builder.AddUint(21, testhashu32Fnv1a, 0); } public static void AddTesthashs64Fnv1a(FlatBufferBuilder builder, long testhashs64Fnv1a) { builder.AddLong(22, testhashs64Fnv1a, 0); } public static void AddTesthashu64Fnv1a(FlatBufferBuilder builder, ulong testhashu64Fnv1a) { builder.AddUlong(23, testhashu64Fnv1a, 0); } - public static int EndMonster(FlatBufferBuilder builder) { + public static Offset EndMonster(FlatBufferBuilder builder) { int o = builder.EndObject(); builder.Required(o, 10); // name - return o; + return new Offset(o); } - public static void FinishMonsterBuffer(FlatBufferBuilder builder, int offset) { builder.Finish(offset, "MONS"); } + public static void FinishMonsterBuffer(FlatBufferBuilder builder, Offset offset) { builder.Finish(offset.Value, "MONS"); } }; diff --git a/tests/MyGame/Example/Stat.cs b/tests/MyGame/Example/Stat.cs index a1f310e39..f3250e2fa 100644 --- a/tests/MyGame/Example/Stat.cs +++ b/tests/MyGame/Example/Stat.cs @@ -16,8 +16,8 @@ public sealed class Stat : Table { public ushort Count { get { int o = __offset(8); return o != 0 ? bb.GetUshort(o + bb_pos) : (ushort)0; } } public bool MutateCount(ushort count) { int o = __offset(8); if (o != 0) { bb.PutUshort(o + bb_pos, count); return true; } else { return false; } } - public static int CreateStat(FlatBufferBuilder builder, - int id = 0, + public static Offset CreateStat(FlatBufferBuilder builder, + StringOffset id = default(StringOffset), long val = 0, ushort count = 0) { builder.StartObject(3); @@ -28,12 +28,12 @@ public sealed class Stat : Table { } public static void StartStat(FlatBufferBuilder builder) { builder.StartObject(3); } - public static void AddId(FlatBufferBuilder builder, int idOffset) { builder.AddOffset(0, idOffset, 0); } + public static void AddId(FlatBufferBuilder builder, StringOffset idOffset) { builder.AddOffset(0, idOffset.Value, 0); } public static void AddVal(FlatBufferBuilder builder, long val) { builder.AddLong(1, val, 0); } public static void AddCount(FlatBufferBuilder builder, ushort count) { builder.AddUshort(2, count, 0); } - public static int EndStat(FlatBufferBuilder builder) { + public static Offset EndStat(FlatBufferBuilder builder) { int o = builder.EndObject(); - return o; + return new Offset(o); } }; diff --git a/tests/MyGame/Example/Test.cs b/tests/MyGame/Example/Test.cs index ff81c4595..182c26b7b 100644 --- a/tests/MyGame/Example/Test.cs +++ b/tests/MyGame/Example/Test.cs @@ -13,12 +13,12 @@ public sealed class Test : Struct { public sbyte B { get { return bb.GetSbyte(bb_pos + 2); } } public void MutateB(sbyte b) { bb.PutSbyte(bb_pos + 2, b); } - public static int CreateTest(FlatBufferBuilder builder, short A, sbyte B) { + public static Offset CreateTest(FlatBufferBuilder builder, short A, sbyte B) { builder.Prep(2, 4); builder.Pad(1); builder.PutSbyte(B); builder.PutShort(A); - return builder.Offset; + return new Offset(builder.Offset); } }; diff --git a/tests/MyGame/Example/Vec3.cs b/tests/MyGame/Example/Vec3.cs index 55c466d42..9f643f4fa 100644 --- a/tests/MyGame/Example/Vec3.cs +++ b/tests/MyGame/Example/Vec3.cs @@ -21,7 +21,7 @@ public sealed class Vec3 : Struct { public Test Test3 { get { return GetTest3(new Test()); } } public Test GetTest3(Test obj) { return obj.__init(bb_pos + 26, bb); } - public static int CreateVec3(FlatBufferBuilder builder, float X, float Y, float Z, double Test1, Color Test2, short test3_A, sbyte test3_B) { + public static Offset CreateVec3(FlatBufferBuilder builder, float X, float Y, float Z, double Test1, Color Test2, short test3_A, sbyte test3_B) { builder.Prep(16, 32); builder.Pad(2); builder.Prep(2, 4); @@ -35,7 +35,7 @@ public sealed class Vec3 : Struct { builder.PutFloat(Z); builder.PutFloat(Y); builder.PutFloat(X); - return builder.Offset; + return new Offset(builder.Offset); } }; diff --git a/tests/test.cpp b/tests/test.cpp index 46f9a2d57..116068edd 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -324,7 +324,7 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) { TEST_EQ(hp_int64, 80); auto hp_double = flatbuffers::GetAnyFieldF(root, hp_field); TEST_EQ(hp_double, 80.0); - auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, schema); + auto hp_string = flatbuffers::GetAnyFieldS(root, hp_field, &schema); TEST_EQ_STR(hp_string.c_str(), "80"); // We can also modify it. @@ -370,8 +370,44 @@ void ReflectionTest(uint8_t *flatbuf, size_t length) { // rinventory still valid, so lets read from it. TEST_EQ(rinventory->Get(10), 50); - // Using reflection, we can also copy tables and other things out of - // other FlatBuffers into a new one, either part or whole. + // For reflection uses not covered already, there is a more powerful way: + // we can simply generate whatever object we want to add/modify in a + // FlatBuffer of its own, then add that to an existing FlatBuffer: + // As an example, let's add a string to an array of strings. + // First, find our field: + auto &testarrayofstring_field = *fields->LookupByKey("testarrayofstring"); + // Find the vector value: + auto rtestarrayofstring = flatbuffers::piv( + flatbuffers::GetFieldV>( + **rroot, testarrayofstring_field), + resizingbuf); + // It's a vector of 2 strings, to which we add one more, initialized to + // offset 0. + flatbuffers::ResizeVector>( + schema, 3, 0, *rtestarrayofstring, &resizingbuf); + // Here we just create a buffer that contans a single string, but this + // could also be any complex set of tables and other values. + flatbuffers::FlatBufferBuilder stringfbb; + stringfbb.Finish(stringfbb.CreateString("hank")); + // Add the contents of it to our existing FlatBuffer. + // We do this last, so the pointer doesn't get invalidated (since it is + // at the end of the buffer): + auto string_ptr = flatbuffers::AddFlatBuffer(resizingbuf, + stringfbb.GetBufferPointer(), + stringfbb.GetSize()); + // Finally, set the new value in the vector. + rtestarrayofstring->MutateOffset(2, string_ptr); + TEST_EQ_STR(rtestarrayofstring->Get(0)->c_str(), "bob"); + TEST_EQ_STR(rtestarrayofstring->Get(2)->c_str(), "hank"); + // As an additional test, also set it on the name field. + // Note: unlike the name change above, this just overwrites the offset, + // rather than changing the string in-place. + SetFieldT(*rroot, name_field, string_ptr); + TEST_EQ_STR(GetFieldS(**rroot, name_field)->c_str(), "hank"); + + // Using reflection, rather than mutating binary FlatBuffers, we can also copy + // tables and other things out of other FlatBuffers into a FlatBufferBuilder, + // either part or whole. flatbuffers::FlatBufferBuilder fbb; auto root_offset = flatbuffers::CopyTable(fbb, schema, *root_table, *flatbuffers::GetAnyRoot(flatbuf));