diff --git a/.gitignore b/.gitignore index d6ec9fd87..bfe065bc4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,12 @@ *_wire.txt *_wire.bin .DS_Store +**/.build +**/Packages +/*.xcodeproj +**/xcuserdata/ +**/xcshareddata/ +**/.swiftpm/ *.o *.o.d *.class diff --git a/CMakeLists.txt b/CMakeLists.txt index 815dadfbc..a1c7c00ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(FlatBuffers_Compiler_SRCS src/idl_gen_fbs.cpp src/idl_gen_grpc.cpp src/idl_gen_json_schema.cpp + src/idl_gen_swift.cpp src/flatc.cpp src/flatc_main.cpp include/flatbuffers/code_generators.h diff --git a/docs/source/Compiler.md b/docs/source/Compiler.md index c5768bd2d..9061051bd 100644 --- a/docs/source/Compiler.md +++ b/docs/source/Compiler.md @@ -47,6 +47,8 @@ For any schema input files, one or more generators can be specified: - `--rust`, `-r` : Generate Rust code. +- `--swift`: Generate Swift code. + For any data input files: - `--binary`, `-b` : If data is contained in this file, generate a diff --git a/docs/source/FlatBuffers.md b/docs/source/FlatBuffers.md index a9d637aca..45ffbf22b 100644 --- a/docs/source/FlatBuffers.md +++ b/docs/source/FlatBuffers.md @@ -4,7 +4,7 @@ FlatBuffers {#flatbuffers_index} # Overview {#flatbuffers_overview} [FlatBuffers](@ref flatbuffers_overview) is an efficient cross platform -serialization library for C++, C#, C, Go, Java, Kotlin, JavaScript, Lobster, Lua, TypeScript, PHP, Python, and Rust. +serialization library for C++, C#, C, Go, Java, Kotlin, JavaScript, Lobster, Lua, TypeScript, PHP, Python, Rust and Swift. It was originally created at Google for game development and other performance-critical applications. @@ -148,6 +148,8 @@ sections provide a more in-depth usage guide. own programs. - How to [use the generated Rust code](@ref flatbuffers_guide_use_rust) in your own programs. +- How to [use the generated Swift code](@ref flatbuffers_guide_use_swift) in your + own programs. - [Support matrix](@ref flatbuffers_support) for platforms/languages/features. - Some [benchmarks](@ref flatbuffers_benchmarks) showing the advantage of using FlatBuffers. diff --git a/docs/source/Support.md b/docs/source/Support.md index c8ac7f7e8..1e435a663 100644 --- a/docs/source/Support.md +++ b/docs/source/Support.md @@ -18,23 +18,23 @@ In general: NOTE: this table is a start, it needs to be extended. -Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Dart | Lobster | Rust ------------------------------- | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ------- | ------- | ---- -Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | Yes | Yes | Yes -JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No | Yes | No -Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No | No | No -Reflection | Yes | No | No | No | No | No | No | Basic | No | No | No | No -Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No | No | No -Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | Yes | Yes | Yes -Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | No | No | Yes -Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? | Great | Superb -Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | Yes | Yes | Yes -Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes -Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes -Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ? -Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ? -Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? | No | ? -Primary authors (github) | aard* | aard* | ev*/js*| rw | rw | evanw/ev* | kr* | mik* | ch* | dnfield | aard* | rw +Feature | C++ | Java | C# | Go | Python | JS | TS | C | PHP | Dart | Lobster | Rust | Swift +------------------------------ | ------ | ------ | ------ | ------ | ------ | --------- | --------- | ------ | --- | ------- | ------- | ------- | ------ +Codegen for all basic features | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | WiP | Yes | Yes | Yes | Yes +JSON parsing | Yes | No | No | No | No | No | No | Yes | No | No | Yes | No | No +Simple mutation | Yes | Yes | Yes | Yes | No | No | No | No | No | No | No | No | No +Reflection | Yes | No | No | No | No | No | No | Basic | No | No | No | No | No +Buffer verifier | Yes | No | No | No | No | No | No | Yes | No | No | No | No | No +Testing: basic | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | ? | Yes | Yes | Yes | Yes +Testing: fuzz | Yes | No | No | Yes | Yes | No | No | No | ? | No | No | Yes | No +Performance: | Superb | Great | Great | Great | Ok | ? | ? | Superb | ? | ? | Great | Superb | ? +Platform: Windows | VS2010 | Yes | Yes | ? | ? | ? | Yes | VS2010 | ? | Yes | Yes | Yes | No +Platform: Linux | GCC282 | Yes | ? | Yes | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes | Yes +Platform: OS X | Xcode4 | ? | ? | ? | Yes | ? | Yes | Yes | ? | Yes | Yes | Yes | Yes +Platform: Android | NDK10d | Yes | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ? | No +Platform: iOS | ? | ? | ? | ? | ? | ? | ? | ? | ? | Flutter | Yes | ? | Yes +Engine: Unity | ? | ? | Yes | ? | ? | ? | ? | ? | ? | ? | No | ? | No +Primary authors (github) | aard* | aard* | ev*/js*| rw | rw | evanw/ev* | kr* | mik* | ch* | dnfield | aard* | rw | mi*/mz* * aard = aardappel (previously: gwvo) * ev = evolutional @@ -42,5 +42,7 @@ Primary authors (github) | aard* | aard* | ev*/js*| rw | rw | ev * mik = mikkelfj * ch = chobie * kr = krojew + * mi = mustiikhalil + * mz = mzaks
diff --git a/docs/source/SwiftUsage.md b/docs/source/SwiftUsage.md new file mode 100644 index 000000000..fb8b1fd9b --- /dev/null +++ b/docs/source/SwiftUsage.md @@ -0,0 +1,93 @@ +Use in Swift {#flatbuffers_guide_use_swift} +========= + +## Before you get started + +Before diving into the FlatBuffers usage in Swift, it should be noted that +the [Tutorial](@ref flatbuffers_guide_tutorial) page has a complete guide +to general FlatBuffers usage in all of the supported languages (including Swift). +This page is designed to cover the nuances of FlatBuffers usage, specific to +Swift. + +You should also have read the [Building](@ref flatbuffers_guide_building) +documentation to build `flatc` and should be familiar with +[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler) and +[Writing a schema](@ref flatbuffers_guide_writing_schema). + +## FlatBuffers Swift library code location + +The code for the FlatBuffers Swift library can be found at +`flatbuffers/swift`. You can browse the library code on the [FlatBuffers +GitHub page](https://github.com/google/flatbuffers/tree/master/swift). + +## Testing the FlatBuffers Swift library + +The code to test the Swift library can be found at `flatbuffers/Flatbuffers.Test.Swift`. +The test code itself is located in [Flatbuffers.Test.Swift](https://github.com/google/ +flatbuffers/blob/master/tests/FlatBuffers.Test.Swift). + +To run the tests, use the [SwiftTest.sh](https://github.com/google/flatbuffers/ +blob/master/tests/FlatBuffers.Test.Swift/SwiftTest.sh) shell script. + +*Note: The shell script requires [Swift](https://swift.org) to +be installed.* + +## Using the FlatBuffers Swift library + +*Note: See [Tutorial](@ref flatbuffers_guide_tutorial) for a more in-depth +example of how to use FlatBuffers in Swift.* + +FlatBuffers supports reading and writing binary FlatBuffers in Swift. + +To use FlatBuffers in your own code, first generate Swift structs from your +schema with the `--swift` option to `flatc`. Then include FlatBuffers using `SPM` in +by adding the path to `FlatBuffers/swift` into it. The generated code should also be +added to xcode or the path of the package you will be using. Note: sometimes xcode cant +and wont see the generated files, so it's better that you copy them to xcode. + +For example, here is how you would read a FlatBuffer binary file in Swift: First, +include the library and copy thegenerated code. Then read a FlatBuffer binary file or +a data object from the server, which you can pass into the `GetRootAsMonster` function. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.swift} + import FlatBuffers + + typealias Monster1 = MyGame.Sample.Monster + typealias Vec3 = MyGame.Sample.Vec3 + + let path = FileManager.default.currentDirectoryPath + let url = URL(fileURLWithPath: path, isDirectory: true).appendingPathComponent("monsterdata_test").appendingPathExtension("mon") + guard let data = try? Data(contentsOf: url) else { return } + + let monster = Monster.getRootAsMonster(bb: ByteBuffer(data: data)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Now you can access values like this: + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.swift} + let hp = monster.hp + let pos = monster.pos +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +In some cases it's necessary to modify values in an existing FlatBuffer in place (without creating a copy). For this reason, scalar fields of a Flatbuffer table or struct can be mutated. + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.swift} + let monster = Monster.getRootAsMonster(bb: ByteBuffer(data: data)) + + if !monster.mutate(hp: 10) { + fatalError("couldn't mutate") + } + // mutate a struct field + let vec = monster.pos.mutate(z: 4) + + // This mutation will fail because the mana field is not available in + // the buffer. It should be set when creating the buffer. + if !monster.mutate(mana: 20) { + fatalError("couldn't mutate") + } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The term `mutate` is used instead of `set` to indicate that this is a special use case. All mutate functions return a boolean value which is false if the field we're trying to mutate is not available in the buffer. + +
diff --git a/docs/source/Tutorial.md b/docs/source/Tutorial.md index e8d8519be..00408b52e 100644 --- a/docs/source/Tutorial.md +++ b/docs/source/Tutorial.md @@ -35,6 +35,7 @@ Please select your desired language for our quest: Lua Lobster Rust + Swift \endhtmlonly @@ -152,6 +153,9 @@ For your chosen language, please cross-reference with:
[sample_binary.rs](https://github.com/google/flatbuffers/blob/master/samples/sample_binary.rs)
+
+[sample_binary.swift](https://github.com/google/flatbuffers/blob/master/samples/sample_binary.swift) +
## Writing the Monsters' FlatBuffer Schema @@ -363,6 +367,12 @@ Please be aware of the difference between `flatc` and `flatcc` tools. ./../flatc --rust monster.fbs ~~~ +
+~~~{.sh} + cd flatbuffers/samples + ./../flatc --swift monster.fbs +~~~ +
For a more complete guide to using the `flatc` compiler, please read the [Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler) @@ -523,6 +533,21 @@ The first step is to import/include the library, generated files, etc. ~~~ +
+~~~{.swift} + /** + // make sure that monster_generated.swift is included in your project + */ + import Flatbuffers + + // typealiases for convenience + typealias Monster = MyGame1.Sample.Monster + typealias Weapon = MyGame1.Sample.Weapon + typealias Color = MyGame1.Sample.Color + typealias Vec3 = MyGame1.Sample.Vec3 +~~~ +
+ Now we are ready to start building some buffers. In order to start, we need to create an instance of the `FlatBufferBuilder`, which will contain the buffer as it grows. You can pass an initial size of the buffer (here 1024 bytes), @@ -627,6 +652,12 @@ which will grow automatically if needed: let mut builder = flatbuffers::FlatBufferBuilder::new_with_capacity(1024); ~~~ +
+~~~{.swift} + // create a `FlatBufferBuilder`, which will be used to serialize objects + let builder = FlatBufferBuilder(initialSize: 1024) +~~~ +
After creating the `builder`, we can start serializing our data. Before we make our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`. @@ -878,6 +909,25 @@ our `orc` Monster, lets create some `Weapon`s: a `Sword` and an `Axe`. ~~~ +
+~~~{.swift} + let weapon1Name = builder.create(string: "Sword") + let weapon2Name = builder.create(string: "Axe") + + // start creating the weapon by calling startWeapon + let weapon1Start = Weapon.startWeapon(builder) + Weapon.add(name: weapon1Name, builder) + Weapon.add(damage: 3, builder) + // end the object by passing the start point for the weapon 1 + let sword = Weapon.endWeapon(builder, start: weapon1Start) + + let weapon2Start = Weapon.startWeapon(builder) + Weapon.add(name: weapon2Name, builder) + Weapon.add(damage: 5, builder) + let axe = Weapon.endWeapon(builder, start: weapon2Start) +~~~ +
+ Now let's create our monster, the `orc`. For this `orc`, lets make him `red` with rage, positioned at `(1.0, 2.0, 3.0)`, and give him a large pool of hit points with `300`. We can give him a vector of weapons @@ -1068,6 +1118,16 @@ traversal. This is generally easy to do on any tree structures. let inventory = builder.create_vector(&[0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9]); ~~~ +
+~~~{.swift} + // Name of the Monster. + let name = builder.create(string: "Orc") + + // create inventory + let inventory: [Byte] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + let inventoryOffset = builder.createVector(inventory) +~~~ +
We serialized two built-in data types (`string` and `vector`) and captured their return values. These values are offsets into the serialized data, @@ -1212,6 +1272,13 @@ offsets. let weapons = builder.create_vector(&[sword, axe]); ~~~ +
+~~~{.swift} + // Create a FlatBuffer `vector` that contains offsets to the sword and axe + // we created above. + let weaponsOffset = builder.createVector(ofOffsets: [sword, axe]) +~~~ +

Note there's additional convenience overloads of `CreateVector`, allowing you @@ -1348,6 +1415,15 @@ for the `path` field above: // let path = builder.create_vector(&[&x, &y]); ~~~ +
+~~~{.swift} + // + let points = builder.createVector(structs: [MyGame.Sample.createVec3(x: 1, y: 2, z: 3), + MyGame.Sample.createVec3(x: 4, y: 5, z: 6)], + type: Vec3.self) +~~~ +
+ We have now serialized the non-scalar components of the orc, so we can serialize the monster itself: @@ -1621,6 +1697,20 @@ can serialize the monster itself: }); ~~~ +
+~~~{.swift} + let start = Monster.startMonster(builder) + Monster.add(pos: pos, builder) + Monster.add(hp: 300, builder) + Monster.add(name: name, builder) + Monster.add(inventory: inventoryOffset, builder) + Monster.add(color: .red, builder) + Monster.add(weapons: weaponsOffset, builder) + Monster.add(equippedType: .weapon, builder) + Monster.add(equipped: axe, builder) + var orc = Monster.endMonster(builder, start: start) +~~~ +
Note how we create `Vec3` struct in-line in the table. Unlike tables, structs are simple combinations of scalars that are always stored inline, just like @@ -1789,6 +1879,13 @@ Here is a repetition these lines, to help highlight them more clearly: monster_builder.add_equipped(axe.as_union_value()); // Union data ~~~ +
+ ~~~{.swift} + Monster.add(equippedType: .weapon, builder) // Type of union + Monster.add(equipped: axe, builder) // Union data + ~~~ +
+ After you have created your buffer, you will have the offset to the root of the data in the `orc` variable, so you can finish the buffer by calling the @@ -1884,6 +1981,12 @@ appropriate `finish` method. builder.finish(orc, None); ~~~ +
+~~~{.swift} + // Call `finish(offset:)` to instruct the builder that this monster is complete. + builder.finish(offset: orc) +~~~ +
The buffer is now ready to be stored somewhere, sent over the network, be compressed, or whatever you'd like to do with it. You can access the buffer @@ -2012,6 +2115,15 @@ like so: ~~~ +
+~~~{.swift} + // This must be called after `finish()`. + // `sizedByteArray` returns the finished buf of type [UInt8]. + let buf = builder.sizedByteArray + // or you can use to get an object of type Data + let bufData = ByteBuffer(data: builder.data) +~~~ +
Now you can write the bytes to a file, send them over the network.. **Make sure your file mode (or transfer protocol) is set to BINARY, not text.** @@ -2312,6 +2424,16 @@ myGame.Monster monster = new myGame.Monster(data); ~~~ +
+~~~{.swift} + // create a ByteBuffer(:) from an [UInt8] or Data() + let buf = // Get your data + + // Get an accessor to the root object inside the buffer. + let monster = Monster.getRootAsMonster(bb: ByteBuffer(bytes: buf)) +~~~ +
+ If you look in the generated files from the schema compiler, you will see it generated accessors for all non-`deprecated` fields. For example: @@ -2419,6 +2541,14 @@ accessors for all non-`deprecated` fields. For example: ~~~ +
+~~~{.swift} + let hp = monster.hp + let mana = monster.mana + let name = monster.name // returns an optional string +~~~ +
+ These should hold `300`, `150`, and `"Orc"` respectively. *Note: The default value `150` wasn't stored in `mana`, but we are still able to retrieve it.* @@ -2544,6 +2674,15 @@ To access sub-objects, in the case of our `pos`, which is a `Vec3`: ~~~ +
+~~~{.swift} + let pos = monster.pos + let x = pos.x + let y = pos.y + let z = pos.z +~~~ +
+ `x`, `y`, and `z` will contain `1.0`, `2.0`, and `3.0`, respectively. *Note: Had we not set `pos` during serialization, it would be a `NULL`-value.* @@ -2644,6 +2783,20 @@ FlatBuffers `vector`. ~~~ +
+~~~{.swift} + // Get a the count of objects in the vector + let count = monster.inventoryCount + + // get item at index 4 + let object = monster.inventory(at: 4) + + // or you can fetch the entire array + let inv = monster.inventory + // inv[4] should equal object +~~~ +
+ For `vector`s of `table`s, you can access the elements like any other vector, except your need to handle the result as a FlatBuffer `table`: @@ -2756,6 +2909,16 @@ except your need to handle the result as a FlatBuffer `table`: let second_weapon_damage = wep2.damage(); ~~~ +
+~~~{.swift} + // Get the count of weapon objects + let wepsCount = monster.weaponsCount + + let weapon2 = monster.weapons(at: 1) + let weaponName = weapon2.name + let weaponDmg = weapon2.damage +~~~ +
Last, we can access our `Equipped` FlatBuffer `union`. Just like when we created the `union`, we need to get both parts of the `union`: the type and the data. @@ -2943,6 +3106,17 @@ We can access the type to dynamically cast the data as needed (since the ~~~ +
+~~~{.swift} + // Get and check if the monster has an equipped item + if monster.equippedType == .weapon { + let _weapon = monster.equipped(type: Weapon.self) + let name = _weapon.name // should return "Axe" + let dmg = _weapon.damage // should return 5 + } +~~~ +
+ ## Mutating FlatBuffers As you saw above, typically once you have created a FlatBuffer, it is read-only @@ -3046,6 +3220,15 @@ mutators like so: ~~~ +
+~~~{.swift} + let monster = Monster.getRootAsMonster(bb: ByteBuffer(bytes: buf)) + monster.mutate(hp: 10) // mutates a value in a table + monster.pos.mutate(z: 4) // mutates a value in a struct + monster.mutate(inventory: 6, at index: 0) // mutates a value in an Scalar array +~~~ +
+ We use the somewhat verbose term `mutate` instead of `set` to indicate that this is a special use case, not to be confused with the default way of constructing FlatBuffer data. @@ -3215,5 +3398,7 @@ For your chosen language, see:
[Use in Rust](@ref flatbuffers_guide_use_rust)
- +
+[Use in Swift](@ref flatbuffers_guide_use_swift) +

diff --git a/include/flatbuffers/idl.h b/include/flatbuffers/idl.h index 1f013a8b6..eac70d9ed 100644 --- a/include/flatbuffers/idl.h +++ b/include/flatbuffers/idl.h @@ -47,26 +47,26 @@ namespace flatbuffers { // of type tokens. // clang-format off #define FLATBUFFERS_GEN_TYPES_SCALAR(TD) \ - TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8, UByte) \ - TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8, UByte) /* begin scalar/int */ \ - TD(BOOL, "bool", uint8_t, boolean,bool, bool, bool, bool, Boolean) \ - TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8, Byte) \ - TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8, UByte) \ - TD(SHORT, "short", int16_t, short, int16, short, int16, i16, Short) \ - TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16, UShort) \ - TD(INT, "int", int32_t, int, int32, int, int32, i32, Int) \ - TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32, UInt) \ - TD(LONG, "long", int64_t, long, int64, long, int64, i64, Long) \ - TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64, ULong) /* end int */ \ - TD(FLOAT, "float", float, float, float32, float, float32, f32, Float) /* begin float */ \ - TD(DOUBLE, "double", double, double, float64, double, float64, f64, Double) /* end float/scalar */ + TD(NONE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ + TD(UTYPE, "", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) /* begin scalar/int */ \ + TD(BOOL, "bool", uint8_t, boolean,bool, bool, bool, bool, Boolean, Bool) \ + TD(CHAR, "byte", int8_t, byte, int8, sbyte, int8, i8, Byte, Int8) \ + TD(UCHAR, "ubyte", uint8_t, byte, byte, byte, uint8, u8, UByte, UInt8) \ + TD(SHORT, "short", int16_t, short, int16, short, int16, i16, Short, Int16) \ + TD(USHORT, "ushort", uint16_t, short, uint16, ushort, uint16, u16, UShort, UInt16) \ + TD(INT, "int", int32_t, int, int32, int, int32, i32, Int, Int32) \ + TD(UINT, "uint", uint32_t, int, uint32, uint, uint32, u32, UInt, UInt32) \ + TD(LONG, "long", int64_t, long, int64, long, int64, i64, Long, Int64) \ + TD(ULONG, "ulong", uint64_t, long, uint64, ulong, uint64, u64, ULong, UInt64) /* end int */ \ + TD(FLOAT, "float", float, float, float32, float, float32, f32, Float, Float32) /* begin float */ \ + TD(DOUBLE, "double", double, double, float64, double, float64, f64, Double, Double) /* end float/scalar */ #define FLATBUFFERS_GEN_TYPES_POINTER(TD) \ - TD(STRING, "string", Offset, int, int, StringOffset, int, unused, Int) \ - TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused, Int) \ - TD(STRUCT, "", Offset, int, int, int, int, unused, Int) \ - TD(UNION, "", Offset, int, int, int, int, unused, Int) + TD(STRING, "string", Offset, int, int, StringOffset, int, unused, Int, Offset) \ + TD(VECTOR, "", Offset, int, int, VectorOffset, int, unused, Int, Offset) \ + TD(STRUCT, "", Offset, int, int, int, int, unused, Int, Offset) \ + TD(UNION, "", Offset, int, int, int, int, unused, Int, Offset) #define FLATBUFFERS_GEN_TYPE_ARRAY(TD) \ - TD(ARRAY, "", int, int, int, int, int, unused, Int) + TD(ARRAY, "", int, int, int, int, int, unused, Int, Offset) // The fields are: // - enum // - FlatBuffers schema type. @@ -577,6 +577,7 @@ struct IDLOptions { kLobster = 1 << 13, kRust = 1 << 14, kKotlin = 1 << 15, + kSwift = 1 << 16, kMAX }; @@ -1051,6 +1052,11 @@ extern bool GenerateJsonSchema(const Parser &parser, const std::string &path, extern bool GenerateKotlin(const Parser &parser, const std::string &path, const std::string &file_name); +// Generate Swift classes. +// See idl_gen_swift.cpp +extern bool GenerateSwift(const Parser &parser, const std::string &path, + const std::string &file_name); + // Generate a schema file from the internal representation, useful after // parsing a .proto schema. extern std::string GenerateFBS(const Parser &parser, diff --git a/samples/sample_binary.swift b/samples/sample_binary.swift new file mode 100644 index 000000000..e334fab70 --- /dev/null +++ b/samples/sample_binary.swift @@ -0,0 +1,68 @@ + // THIS IS JUST TO SHOW THE CODE, PLEASE DO IMPORT FLATBUFFERS WITH SPM.. +import Flatbuffers + +typealias Monster = MyGame1.Sample.Monster +typealias Weapon = MyGame1.Sample.Weapon +typealias Color = MyGame1.Sample.Color +typealias Vec3 = MyGame1.Sample.Vec3 + +func main() { + let expectedDMG: [Int16] = [3, 5] + let expectedNames = ["Sword", "Axe"] + + let builder = FlatBufferBuilder(initialSize: 1024) + let weapon1Name = builder.create(string: expectedNames[0]) + let weapon2Name = builder.create(string: expectedNames[1]) + + let weapon1Start = Weapon.startWeapon(builder) + Weapon.add(name: weapon1Name, builder) + Weapon.add(damage: expectedDMG[0], builder) + let sword = Weapon.endWeapon(builder, start: weapon1Start) + let weapon2Start = Weapon.startWeapon(builder) + Weapon.add(name: weapon2Name, builder) + Weapon.add(damage: expectedDMG[1], builder) + let axe = Weapon.endWeapon(builder, start: weapon2Start) + + let name = builder.create(string: "Orc") + let inventory: [Byte] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + let inventoryOffset = builder.createVector(inventory) + + let weaponsOffset = builder.createVector(ofOffsets: [sword, axe]) + let pos = builder.create(struct: MyGame.Sample.createVec3(x: 1, y: 2, z: 3), type: Vec3.self) + + let start = Monster.startMonster(builder) + Monster.add(pos: pos, builder) + Monster.add(hp: 300, builder) + Monster.add(name: name, builder) + Monster.add(inventory: inventoryOffset, builder) + Monster.add(color: .red, builder) + Monster.add(weapons: weaponsOffset, builder) + Monster.add(equippedType: .weapon, builder) + Monster.add(equipped: axe, builder) + var orc = Monster.endMonster(builder, start: start) + builder.finish(offset: orc) + + var buf = builder.sizedByteArray + var monster = Monster.getRootAsMonster(bb: ByteBuffer(bytes: buf)) + + assert(monster.mana == 150) + assert(monster.hp == 300) + assert(monster.name == "Orc") + assert(monster.color == MyGame1.Sample.Color.red) + assert(monster.pos != nil) + for i in 0.. + +#include "flatbuffers/code_generators.h" +#include "flatbuffers/flatbuffers.h" +#include "flatbuffers/idl.h" +#include "flatbuffers/util.h" + +namespace flatbuffers { + +namespace swift { + +inline std::string GenIndirect(const std::string &reading) { + return "{{ACCESS}}.indirect(" + reading + ")"; +} + +inline std::string GenArrayMainBody(const std::string &optional) { + return "\tpublic func {{VALUENAME}}(at index: Int32) -> {{VALUETYPE}}" + + optional + " { "; +} + +inline char LowerCase(char c) { + return static_cast(::tolower(static_cast(c))); +} + +class SwiftGenerator : public BaseGenerator { + private: + const Namespace *cur_name_space_; + CodeWriter code_; + std::unordered_set keywords_; + std::set namespaces_; + int namespace_depth; + + public: + SwiftGenerator(const Parser &parser, const std::string &path, + const std::string &file_name) + : BaseGenerator(parser, path, file_name, "", "."), + cur_name_space_(nullptr) { + namespace_depth = 0; + static const char *const keywords[] = { + "enum", "private", "public", "internal", "fileprivate", "static", "var", + "URL", "struct", "let", "class", "Any", "nil", nullptr, + }; + for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw); + } + + bool generate() { + code_.Clear(); + code_.SetValue("ACCESS", "_accessor"); + code_ += "// " + std::string(FlatBuffersGeneratedWarning()) + "\n"; + code_ += "import FlatBuffers\n"; + // Generate code for all the enum declarations. + + for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); + ++it) { + const auto &enum_def = **it; + if (!enum_def.generated) { + SetNameSpace(enum_def.defined_namespace); + GenEnum(enum_def); + } + } + + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + const auto &struct_def = **it; + if (struct_def.fixed && !struct_def.generated) { + SetNameSpace(struct_def.defined_namespace); + GenStructReader(struct_def); + } + } + + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + const auto &struct_def = **it; + if (struct_def.fixed && !struct_def.generated) { + SetNameSpace(struct_def.defined_namespace); + GenStructWriter(struct_def); + } + } + + for (auto it = parser_.structs_.vec.begin(); + it != parser_.structs_.vec.end(); ++it) { + const auto &struct_def = **it; + if (!struct_def.fixed && !struct_def.generated) { + SetNameSpace(struct_def.defined_namespace); + GenTable(struct_def); + } + } + + if (cur_name_space_) SetNameSpace(nullptr); + + const auto filename = GeneratedFileName(path_, file_name_); + const auto final_code = code_.ToString(); + return SaveFile(filename.c_str(), final_code, false); + } + + void mark(const std::string &str) { + code_.SetValue("MARKVALUE", str); + code_ += "\n// MARK: - {{MARKVALUE}}\n"; + } + + // Generates the create function for swift + void GenStructWriter(const StructDef &struct_def) { + code_.SetValue("STRUCTNAME", Name(struct_def)); + std::string static_type = this->namespace_depth == 0 ? "" : "static "; + code_ += "public " + static_type + "func create{{STRUCTNAME}}(\\"; + std::string func_header = ""; + GenerateStructArgs(struct_def, &func_header, ""); + code_ += func_header.substr(0, func_header.size() - 2) + "\\"; + code_ += ") -> UnsafeMutableRawPointer {"; + code_ += + "\tlet memory = UnsafeMutableRawPointer.allocate(byteCount: " + "{{STRUCTNAME}}.size, alignment: {{STRUCTNAME}}.alignment)"; + code_ += + "\tmemory.initializeMemory(as: UInt8.self, repeating: 0, count: " + "{{STRUCTNAME}}.size)"; + GenerateStructBody(struct_def, ""); + code_ += "\treturn memory"; + code_ += "}\n"; + } + + void GenerateStructBody(const StructDef &struct_def, + const std::string &nameprefix, int offset = 0) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + auto name = nameprefix + Name(field); + const auto &field_type = field.value.type; + auto type = GenTypeBasic(field_type, false); + if (IsStruct(field.value.type)) { + GenerateStructBody(*field_type.struct_def, (nameprefix + field.name), + static_cast(field.value.offset)); + } else { + auto off = NumToString(offset + field.value.offset); + code_ += "\tmemory.storeBytes(of: " + name + + (field_type.enum_def ? ".rawValue" : "") + + ", toByteOffset: " + off + ", as: " + type + ".self)"; + } + } + } + + void GenerateStructArgs(const StructDef &struct_def, std::string *code_ptr, + const std::string &nameprefix) { + auto &code = *code_ptr; + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + const auto &field_type = field.value.type; + if (IsStruct(field.value.type)) { + GenerateStructArgs(*field_type.struct_def, code_ptr, + (nameprefix + field.name)); + } else { + auto name = Name(field); + auto type = GenType(field.value.type); + code += nameprefix + name + ": " + type; + code += ", "; + } + } + } + + void GenObjectHeader(const StructDef &struct_def) { + code_.SetValue("STRUCTNAME", Name(struct_def)); + code_.SetValue("PROTOCOL", + struct_def.fixed ? "Readable" : "FlatBufferObject"); + code_.SetValue("OBJECTTYPE", struct_def.fixed ? "Struct" : "Table"); + code_ += "public struct {{STRUCTNAME}}: {{PROTOCOL}} {"; + code_ += "\tprivate var {{ACCESS}}: {{OBJECTTYPE}}"; + if (struct_def.fixed) { + code_.SetValue("BYTESIZE", NumToString(struct_def.bytesize)); + code_.SetValue("MINALIGN", NumToString(struct_def.minalign)); + code_ += "\tpublic static var size = {{BYTESIZE}}"; + code_ += "\tpublic static var alignment = {{MINALIGN}}\t"; + } else { + if (parser_.file_identifier_.length()) { + code_.SetValue("FILENAME", parser_.file_identifier_); + code_ += + "\tpublic static func finish(_ fbb: FlatBufferBuilder, end: " + "Offset, prefix: Bool = false) { fbb.finish(offset: end, " + "fileId: " + "\"{{FILENAME}}\", addPrefix: prefix) }"; + } + code_ += + "\tpublic static func getRootAs{{STRUCTNAME}}(bb: ByteBuffer) -> " + "{{STRUCTNAME}} { return {{STRUCTNAME}}(Table(bb: bb, position: " + "Int32(bb.read(def: UOffset.self, position: bb.reader)) + " + "Int32(bb.reader))) }\n"; + code_ += "\tprivate init(_ t: Table) { {{ACCESS}} = t }"; + } + code_ += + "\tpublic init(_ bb: ByteBuffer, o: Int32) { {{ACCESS}} = " + "{{OBJECTTYPE}}(bb: " + "bb, position: o) }"; + code_ += ""; + } + + // Generates the reader for swift + void GenTable(const StructDef &struct_def) { + GenObjectHeader(struct_def); + GenTableReader(struct_def); + GenTableWriter(struct_def); + code_ += "}\n"; + } + + void GenTableReader(const StructDef &struct_def) { + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + GenTableReaderFields(field); + } + } + + void GenTableWriter(const StructDef &struct_def) { + flatbuffers::FieldDef *key_field = nullptr; + + code_.SetValue("NUMBEROFFIELDS", NumToString(struct_def.fields.vec.size())); + code_ += + "\tpublic static func start{{STRUCTNAME}}(_ fbb: FlatBufferBuilder) -> " + "UOffset { fbb.startTable(with: {{NUMBEROFFIELDS}}) }"; + + std::vector require_fields; + + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + if (field.key) key_field = &field; + if (field.required) + require_fields.push_back(NumToString(field.value.offset)); + + GenTableWriterFields( + field, static_cast(it - struct_def.fields.vec.begin())); + } + code_ += + "\tpublic static func end{{STRUCTNAME}}(_ fbb: FlatBufferBuilder, " + "start: " + "UOffset) -> Offset { let end = Offset(offset: " + "fbb.endTable(at: start))\\"; + if (require_fields.capacity() != 0) { + std::string fields = ""; + for (auto it = require_fields.begin(); it != require_fields.end(); ++it) + fields += *it + ", "; + code_.SetValue("FIELDS", fields.substr(0, fields.size() - 2)); + code_ += "; fbb.require(table: end, fields: [{{FIELDS}}])\\"; + } + code_ += "; return end }"; + + std::string spacing = "\t\t"; + + if (key_field != nullptr && !struct_def.fixed && struct_def.has_key) { + code_.SetValue("VALUENAME", struct_def.name); + code_.SetValue("VOFFSET", NumToString(key_field->value.offset)); + + code_ += + "\tpublic static func " + "sortVectorOf{{VALUENAME}}(offsets:[Offset], " + "_ fbb: FlatBufferBuilder) -> Offset {"; + code_ += spacing + "var off = offsets"; + code_ += + spacing + + "off.sort { Table.compare(Table.offset(Int32($1.o), vOffset: " + "{{VOFFSET}}, fbb: fbb.buffer), Table.offset(Int32($0.o), vOffset: " + "{{VOFFSET}}, fbb: fbb.buffer), fbb: fbb.buffer) < 0 } "; + code_ += spacing + "return fbb.createVector(ofOffsets: off)"; + code_ += "\t}"; + GenLookup(*key_field); + } + } + + void GenTableWriterFields(const FieldDef &field, const int position) { + std::string builder_string = ", _ fbb: FlatBufferBuilder) { fbb.add("; + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + code_.SetValue("VALUETYPE", type); + code_.SetValue("OFFSET", NumToString(position)); + code_.SetValue("CONSTANT", field.value.constant); + std::string check_if_vector = + (field.value.type.base_type == BASE_TYPE_VECTOR || + field.value.type.base_type == BASE_TYPE_ARRAY) + ? "VectorOf" + : ""; + code_ += + "\tpublic static func add" + check_if_vector + "({{VALUENAME}}: \\"; + + if (IsScalar(field.value.type.base_type) && + !IsBool(field.value.type.base_type)) { + auto is_enum = IsEnum(field.value.type) ? ".rawValue" : ""; + code_ += "{{VALUETYPE}}" + builder_string + "element: {{VALUENAME}}" + + is_enum + ", def: {{CONSTANT}}, at: {{OFFSET}}) }"; + return; + } + if (IsBool(field.value.type.base_type)) { + code_.SetValue("VALUETYPE", "Bool"); + code_.SetValue("CONSTANT", + "0" == field.value.constant ? "false" : "true"); + code_ += "{{VALUETYPE}}" + builder_string + + "condition: {{VALUENAME}}, def: {{CONSTANT}}, at: {{OFFSET}}) }"; + return; + } + + auto offset_type = field.value.type.base_type == BASE_TYPE_STRING + ? "Offset" + : "Offset"; + auto reader_type = + IsStruct(field.value.type) && field.value.type.struct_def->fixed + ? "structOffset: {{OFFSET}}) }" + : "offset: {{VALUENAME}}, at: {{OFFSET}}) }"; + code_ += offset_type + builder_string + reader_type; + } + + void GenTableReaderFields(const FieldDef &field) { + auto offset = NumToString(field.value.offset); + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + code_.SetValue("VALUETYPE", type); + code_.SetValue("OFFSET", offset); + code_.SetValue("CONSTANT", field.value.constant); + std::string const_string = "return o == 0 ? {{CONSTANT}} : "; + + if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type) && + !IsBool(field.value.type.base_type)) { + code_ += GenReaderMainBody() + GenOffset() + const_string + + GenReader("VALUETYPE", "o") + " }"; + if (parser_.opts.mutable_buffer) code_ += GenMutate("o", GenOffset()); + return; + } + + if (IsBool(field.value.type.base_type)) { + code_.SetValue("VALUETYPE", "Bool"); + code_ += GenReaderMainBody() + "\\"; + code_.SetValue("VALUETYPE", "Byte"); + code_ += GenOffset() + + "return o == 0 ? false : 0 != " + GenReader("VALUETYPE", "o") + + " }"; + if (parser_.opts.mutable_buffer) code_ += GenMutate("o", GenOffset()); + return; + } + + if (IsEnum(field.value.type)) { + auto default_value = GenEnumDefaultValue(field); + code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); + code_ += GenReaderMainBody() + "\\"; + code_ += GenOffset() + "return o == 0 ? " + default_value + " : " + + GenEnumConstructor("o") + "?? " + default_value + " }"; + if (parser_.opts.mutable_buffer && !IsUnion(field.value.type)) + code_ += GenMutate("o", GenOffset(), true); + return; + } + + if (IsStruct(field.value.type) && field.value.type.struct_def->fixed) { + code_.SetValue("VALUETYPE", GenType(field.value.type)); + code_.SetValue("CONSTANT", "nil"); + code_ += GenReaderMainBody("?") + GenOffset() + const_string + + GenConstructor("o + {{ACCESS}}.postion"); + return; + } + switch (field.value.type.base_type) { + case BASE_TYPE_STRUCT: + code_.SetValue("VALUETYPE", GenType(field.value.type)); + code_.SetValue("CONSTANT", "nil"); + code_ += GenReaderMainBody("?") + GenOffset() + const_string + + GenConstructor(GenIndirect("o + {{ACCESS}}.postion")); + break; + + case BASE_TYPE_STRING: + code_.SetValue("VALUETYPE", GenType(field.value.type)); + code_.SetValue("CONSTANT", "nil"); + code_ += GenReaderMainBody("?") + GenOffset() + const_string + + "{{ACCESS}}.string(at: o) }"; + code_ += + "\tpublic var {{VALUENAME}}SegmentArray: [UInt8]? { return " + "{{ACCESS}}.getVector(at: {{OFFSET}}) }"; + break; + + case BASE_TYPE_ARRAY: FLATBUFFERS_FALLTHROUGH(); // fall thru + case BASE_TYPE_VECTOR: + GenTableReaderVectorFields(field, const_string); + break; + case BASE_TYPE_UNION: + code_.SetValue("CONSTANT", "nil"); + code_ += + "\tpublic func {{VALUENAME}}(type: " + "T.Type) -> T? { " + + GenOffset() + const_string + "{{ACCESS}}.union(o) }"; + break; + default: FLATBUFFERS_ASSERT(0); + } + } + + void GenTableReaderVectorFields(const FieldDef &field, + const std::string &const_string) { + auto vectortype = field.value.type.VectorType(); + code_.SetValue("SIZE", NumToString(InlineSize(vectortype))); + code_ += "\tpublic var {{VALUENAME}}Count: Int32 { " + GenOffset() + + const_string + "{{ACCESS}}.vector(count: o) }"; + code_.SetValue("CONSTANT", IsScalar(vectortype.base_type) == true + ? field.value.constant + : "nil"); + auto nullable = IsScalar(vectortype.base_type) == true ? "" : "?"; + nullable = IsEnum(vectortype) == true ? "?" : nullable; + if (vectortype.base_type != BASE_TYPE_UNION) { + code_ += GenArrayMainBody(nullable) + GenOffset() + "\\"; + } else { + code_ += + "\tpublic func {{VALUENAME}}(at index: " + "Int32, type: T.Type) -> T? { " + + GenOffset() + "\\"; + } + + if (IsBool(vectortype.base_type)) { + code_.SetValue("CONSTANT", field.value.offset == 0 ? "false" : "true"); + code_.SetValue("VALUETYPE", "Byte"); + } + if (!IsEnum(vectortype)) + code_ += + const_string + (IsBool(vectortype.base_type) ? "0 != " : "") + "\\"; + + if (IsScalar(vectortype.base_type) && !IsEnum(vectortype) && + !IsBool(field.value.type.base_type)) { + code_ += + "{{ACCESS}}.directRead(of: {{VALUETYPE}}.self, offset: " + "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; + code_ += + "\tpublic var {{VALUENAME}}: [{{VALUETYPE}}] { return " + "{{ACCESS}}.getVector(at: {{OFFSET}}) ?? [] }"; + if (parser_.opts.mutable_buffer) code_ += GenMutateArray(); + return; + } + if (vectortype.base_type == BASE_TYPE_STRUCT && + field.value.type.struct_def->fixed) { + code_ += GenConstructor("{{ACCESS}}.vector(at: o) + index * {{SIZE}}"); + return; + } + + if (vectortype.base_type == BASE_TYPE_STRING) { + code_ += + "{{ACCESS}}.directString(at: {{ACCESS}}.vector(at: o) + " + "index * {{SIZE}}) }"; + return; + } + + if (IsEnum(vectortype)) { + code_.SetValue("BASEVALUE", GenTypeBasic(vectortype, false)); + code_ += "return o == 0 ? " + GenEnumDefaultValue(field) + + " : {{VALUETYPE}}(rawValue: {{ACCESS}}.directRead(of: " + "{{BASEVALUE}}.self, offset: {{ACCESS}}.vector(at: o) + " + "index * {{SIZE}})) }"; + return; + } + if (vectortype.base_type == BASE_TYPE_UNION) { + code_ += + "{{ACCESS}}.directUnion({{ACCESS}}.vector(at: o) + " + "index * {{SIZE}}) }"; + return; + } + + if (vectortype.base_type == BASE_TYPE_STRUCT && + !field.value.type.struct_def->fixed) { + code_ += GenConstructor( + "{{ACCESS}}.indirect({{ACCESS}}.vector(at: o) + index * " + "{{SIZE}})"); + auto &sd = *field.value.type.struct_def; + auto &fields = sd.fields.vec; + for (auto kit = fields.begin(); kit != fields.end(); ++kit) { + auto &key_field = **kit; + if (key_field.key) { + GenByKeyFunctions(key_field); + break; + } + } + } + } + + void GenByKeyFunctions(const FieldDef &key_field) { + code_.SetValue("TYPE", GenType(key_field.value.type)); + code_ += + "\tpublic func {{VALUENAME}}By(key: {{TYPE}}) -> {{VALUETYPE}}? { \\"; + code_ += GenOffset() + + "return o == 0 ? nil : {{VALUETYPE}}.lookupByKey(vector: " + "{{ACCESS}}.vector(at: o), key: key, fbb: {{ACCESS}}.bb) }"; + } + + // Generates the reader for swift + void GenStructReader(const StructDef &struct_def) { + GenObjectHeader(struct_def); + for (auto it = struct_def.fields.vec.begin(); + it != struct_def.fields.vec.end(); ++it) { + auto &field = **it; + if (field.deprecated) continue; + auto offset = NumToString(field.value.offset); + auto name = Name(field); + auto type = GenType(field.value.type); + code_.SetValue("VALUENAME", name); + code_.SetValue("VALUETYPE", type); + code_.SetValue("OFFSET", offset); + if (IsScalar(field.value.type.base_type) && !IsEnum(field.value.type)) { + code_ += + GenReaderMainBody() + "return " + GenReader("VALUETYPE") + " }"; + if (parser_.opts.mutable_buffer) code_ += GenMutate("{{OFFSET}}", ""); + } else if (IsEnum(field.value.type)) { + code_.SetValue("BASEVALUE", GenTypeBasic(field.value.type, false)); + code_ += GenReaderMainBody() + "return " + + GenEnumConstructor("{{OFFSET}}") + "?? " + + GenEnumDefaultValue(field) + " }"; + } else if (IsStruct(field.value.type)) { + code_.SetValue("VALUETYPE", GenType(field.value.type)); + code_ += GenReaderMainBody() + "return " + + GenConstructor("{{ACCESS}}.postion + {{OFFSET}}"); + } + } + + code_ += "}\n"; + } + + void GenEnum(const EnumDef &enum_def) { + if (enum_def.generated) return; + code_.SetValue("ENUM_NAME", GenEnumDecl(enum_def)); + code_.SetValue("BASE_TYPE", GenTypeBasic(enum_def.underlying_type, false)); + code_ += "public {{ENUM_NAME}}: {{BASE_TYPE}}, Enum { "; + code_ += "\tpublic typealias T = {{BASE_TYPE}}"; + code_ += + "\tpublic static var byteSize: Int { return " + "MemoryLayout<{{BASE_TYPE}}>.size " + "}"; + code_ += "\tpublic var value: {{BASE_TYPE}} { return self.rawValue }"; + + std::string enum_code = "\tcase "; + int keyCount = 0; + for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { + const auto &ev = **it; + auto key = "KEY" + NumToString(keyCount); + auto value = "VALUE" + NumToString(keyCount); + auto name = Name(ev); + std::transform(name.begin(), name.end(), name.begin(), LowerCase); + code_.SetValue(key, name); + code_.SetValue(value, enum_def.ToString(ev)); + enum_code += "{{" + key + "}} = {{" + value + "}}, "; + keyCount++; + } + code_ += enum_code.substr(0, enum_code.size() - 2); + code_ += "}\n"; + } + + void GenLookup(const FieldDef &key_field) { + code_.SetValue("OFFSET", NumToString(key_field.value.offset)); + auto offset_reader = + "Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: {{OFFSET}}, " + "fbb: fbb)"; + std::string spacing = "\t\t"; + std::string double_spacing = spacing + "\t"; + + code_.SetValue("TYPE", GenType(key_field.value.type)); + code_ += + "\tfileprivate static func lookupByKey(vector: Int32, key: {{TYPE}}, " + "fbb: " + "ByteBuffer) -> {{VALUENAME}}? {"; + + if (key_field.value.type.base_type == BASE_TYPE_STRING) + code_ += spacing + "let key = key.utf8.map { $0 }"; + code_ += spacing + + "var span = fbb.read(def: Int32.self, position: Int(vector - 4))"; + code_ += spacing + "var start: Int32 = 0"; + code_ += spacing + "while span != 0 {"; + code_ += double_spacing + "var middle = span / 2"; + code_ += + double_spacing + + "let tableOffset = Table.indirect(vector + 4 * (start + middle), fbb)"; + if (key_field.value.type.base_type == BASE_TYPE_STRING) { + code_ += double_spacing + "let comp = Table.compare(" + offset_reader + + ", key, fbb: fbb)"; + } else { + code_ += double_spacing + + "let comp = fbb.read(def: {{TYPE}}.self, position: Int(" + + offset_reader + "))"; + } + + code_ += double_spacing + "if comp > 0 {"; + code_ += double_spacing + "\tspan = middle"; + code_ += double_spacing + "} else if comp < 0 {"; + code_ += double_spacing + "\tmiddle += 1"; + code_ += double_spacing + "\tstart += middle"; + code_ += double_spacing + "\tspan -= middle"; + code_ += double_spacing + "} else {"; + code_ += double_spacing + "\treturn {{VALUENAME}}(fbb, o: tableOffset)"; + code_ += double_spacing + "}"; + code_ += spacing + "}"; + code_ += spacing + "return nil"; + code_ += "\t}"; + } + + std::string GenOffset() { return "let o = {{ACCESS}}.offset({{OFFSET}}); "; } + + std::string GenReaderMainBody(const std::string &optional = "") { + return "\tpublic var {{VALUENAME}}: {{VALUETYPE}}" + optional + " { "; + } + + std::string GenReader(const std::string &type, + const std::string &at = "{{OFFSET}}") { + return "{{ACCESS}}.readBuffer(of: {{" + type + "}}.self, at: " + at + ")"; + } + + std::string GenConstructor(const std::string &offset) { + return "{{VALUETYPE}}({{ACCESS}}.bb, o: " + offset + ") }"; + } + + std::string GenMutate(const std::string &offset, + const std::string &get_offset, bool isRaw = false) { + return "\tpublic func mutate({{VALUENAME}}: {{VALUETYPE}}) -> Bool {" + + get_offset + " return {{ACCESS}}.mutate({{VALUENAME}}" + + (isRaw ? ".rawValue" : "") + ", index: " + offset + ") }"; + } + + std::string GenMutateArray() { + return "\tpublic func mutate({{VALUENAME}}: {{VALUETYPE}}, at index: " + "Int32) -> Bool { " + + GenOffset() + + "return {{ACCESS}}.directMutate({{VALUENAME}}, index: " + "{{ACCESS}}.vector(at: o) + index * {{SIZE}}) }"; + } + + std::string GenEnumDefaultValue(const FieldDef &field) { + auto &value = field.value; + FLATBUFFERS_ASSERT(value.type.enum_def); + auto &enum_def = *value.type.enum_def; + auto enum_val = enum_def.FindByValue(value.constant); + std::string name; + if (enum_val) { + name = enum_val->name; + } else { + const auto &ev = **enum_def.Vals().begin(); + name = ev.name; + } + std::transform(name.begin(), name.end(), name.begin(), LowerCase); + return "{{VALUETYPE}}." + name; + } + + std::string GenEnumConstructor(const std::string &at) { + return "{{VALUETYPE}}(rawValue: " + GenReader("BASEVALUE", at) + ") "; + } + + std::string GenType(const Type &type) const { + return IsScalar(type.base_type) + ? GenTypeBasic(type) + : (IsArray(type) ? GenType(type.VectorType()) + : GenTypePointer(type)); + } + + std::string GenTypePointer(const Type &type) const { + switch (type.base_type) { + case BASE_TYPE_STRING: return "String"; + case BASE_TYPE_VECTOR: return GenType(type.VectorType()); + case BASE_TYPE_STRUCT: return WrapInNameSpace(*type.struct_def); + case BASE_TYPE_UNION: + default: return "FlatBufferObject"; + } + } + + std::string GenTypeBasic(const Type &type) const { + return GenTypeBasic(type, true); + } + + std::string GenTypeBasic(const Type &type, bool can_override) const { + // clang-format off + static const char * const swift_type[] = { + #define FLATBUFFERS_TD(ENUM, IDLTYPE, \ + CTYPE, JTYPE, GTYPE, NTYPE, PTYPE, RTYPE, KTYPE, STYPE) \ + #STYPE, + FLATBUFFERS_GEN_TYPES(FLATBUFFERS_TD) + #undef FLATBUFFERS_TD + }; + // clang-format on + if (can_override) { + if (type.enum_def) + return WrapInNameSpace(type.enum_def->defined_namespace, + Name(*type.enum_def)); + if (type.base_type == BASE_TYPE_BOOL) return "Bool"; + } + return swift_type[static_cast(type.base_type)]; + } + + std::string GenEnumDecl(const EnumDef &enum_def) const { + return "enum " + Name(enum_def); + } + + std::string EscapeKeyword(const std::string &name) const { + return keywords_.find(name) == keywords_.end() ? name : name + "_"; + } + + std::string Name(const EnumVal &ev) const { return EscapeKeyword(ev.name); } + + std::string Name(const Definition &def) const { + return EscapeKeyword(MakeCamel(def.name, false)); + } + + static std::string GeneratedFileName(const std::string &path, + const std::string &file_name) { + return path + file_name + "_generated.swift"; + } + // MARK: - Copied from the cpp implementation, needs revisiting + void SetNameSpace(const Namespace *ns) { + if (cur_name_space_ == ns) { return; } + // Compute the size of the longest common namespace prefix. + // If cur_name_space is A::B::C::D and ns is A::B::E::F::G, + // the common prefix is A::B:: and we have old_size = 4, new_size = 5 + // and common_prefix_size = 2 + size_t old_size = cur_name_space_ ? cur_name_space_->components.size() : 0; + size_t new_size = ns ? ns->components.size() : 0; + + size_t common_prefix_size = 0; + while (common_prefix_size < old_size && common_prefix_size < new_size && + ns->components[common_prefix_size] == + cur_name_space_->components[common_prefix_size]) { + common_prefix_size++; + } + + // Close cur_name_space in reverse order to reach the common prefix. + // In the previous example, D then C are closed. + for (size_t j = old_size; j > common_prefix_size; --j) { + if (namespace_depth != 0) { + code_ += "}"; + namespace_depth -= 1; + } + mark(cur_name_space_->components[j - 1]); + } + if (old_size != common_prefix_size) { code_ += ""; } + + // open namespace parts to reach the ns namespace + // in the previous example, E, then F, then G are opened + bool is_extension = false; + for (auto j = common_prefix_size; j < new_size; ++j) { + std::string name = ns->components[j]; + if (namespaces_.find(name) == namespaces_.end()) { + code_ += "public enum " + name + " {"; + namespace_depth += 1; + namespaces_.insert(name); + } else { + code_ += "}"; + is_extension = true; + } + } + if (is_extension) { + code_.SetValue("EXTENSION", FullNamespace(".", *ns)); + code_ += "extension {{EXTENSION}} {"; + } + if (new_size != common_prefix_size) { code_ += ""; } + cur_name_space_ = ns; + } +}; +} // namespace swift +bool GenerateSwift(const Parser &parser, const std::string &path, + const std::string &file_name) { + swift::SwiftGenerator generator(parser, path, file_name); + return generator.generate(); +} +} // namespace flatbuffers diff --git a/src/idl_parser.cpp b/src/idl_parser.cpp index 7c3c46dc1..13c8c3add 100644 --- a/src/idl_parser.cpp +++ b/src/idl_parser.cpp @@ -2195,7 +2195,8 @@ bool Parser::SupportsAdvancedUnionFeatures() const { (opts.lang_to_generate & ~(IDLOptions::kCpp | IDLOptions::kJs | IDLOptions::kTs | IDLOptions::kPhp | IDLOptions::kJava | IDLOptions::kCSharp | - IDLOptions::kKotlin | IDLOptions::kBinary)) == 0; + IDLOptions::kKotlin | IDLOptions::kBinary | IDLOptions::kSwift)) == + 0; } bool Parser::SupportsAdvancedArrayFeatures() const { diff --git a/swift/Package.swift b/swift/Package.swift new file mode 100644 index 000000000..18bb90a4e --- /dev/null +++ b/swift/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "FlatBuffers", + platforms: [ + .iOS(.v11), + .macOS(.v10_14), + ], + products: [ + .library( + name: "FlatBuffers", + targets: ["FlatBuffers"]), + ], + targets: [ + .target( + name: "FlatBuffers", + dependencies: []), + ] +) diff --git a/swift/Sources/FlatBuffers/ByteBuffer.swift b/swift/Sources/FlatBuffers/ByteBuffer.swift new file mode 100644 index 000000000..654b265cf --- /dev/null +++ b/swift/Sources/FlatBuffers/ByteBuffer.swift @@ -0,0 +1,243 @@ +import Foundation + +public final class ByteBuffer { + + /// pointer to the start of the buffer object in memory + private var _memory: UnsafeMutableRawPointer + /// The size of the elements written to the buffer + their paddings + private var _writerSize: Int = 0 + /// Capacity of UInt8 the buffer can hold + private var _capacity: Int + + /// Aliginment of the current memory being written to the buffer + internal var alignment = 1 + /// Current Index which is being used to write to the buffer, it is written from the end to the start of the buffer + internal var writerIndex: Int { return _capacity - _writerSize } + + /// Reader is the position of the current Writer Index (capacity - size) + public var reader: Int { return writerIndex } + /// Current size of the buffer + public var size: UOffset { return UOffset(_writerSize) } + /// Public Pointer to the buffer object in memory. This should NOT be modified for any reason + public var memory: UnsafeMutableRawPointer { return _memory } + /// Current capacity for the buffer + public var capacity: Int { return _capacity } + + /// Constructor that creates a Flatbuffer object from a UInt8 + /// - Parameter bytes: Array of UInt8 + public init(bytes: [UInt8]) { + let ptr = UnsafePointer(bytes) + _memory = UnsafeMutableRawPointer.allocate(byteCount: bytes.count, alignment: alignment) + _memory.copyMemory(from: ptr, byteCount: bytes.count) + _capacity = bytes.count + _writerSize = _capacity + } + + /// Constructor that creates a Flatbuffer from the Swift Data type object + /// - Parameter data: Swift data Object + public init(data: Data) { + let pointer = UnsafeMutablePointer.allocate(capacity: data.count) + data.copyBytes(to: pointer, count: data.count) + _memory = UnsafeMutableRawPointer(pointer) + _capacity = data.count + _writerSize = _capacity + } + + /// Constructor that creates a Flatbuffer instance with a size + /// - Parameter size: Length of the buffer + init(initialSize size: Int) { + let size = size.convertToPowerofTwo + _memory = UnsafeMutableRawPointer.allocate(byteCount: size, alignment: alignment) + _memory.initializeMemory(as: UInt8.self, repeating: 0, count: size) + _capacity = size + } + + /// Creates a copy of the existing flatbuffer, by copying it to a different memory. + /// - Parameters: + /// - memory: Current memory of the buffer + /// - count: count of bytes + /// - removeBytes: Removes a number of bytes from the current size + internal init(memory: UnsafeMutableRawPointer, count: Int, removing removeBytes: Int) { + _memory = UnsafeMutableRawPointer.allocate(byteCount: count, alignment: alignment) + _memory.copyMemory(from: memory, byteCount: count) + _capacity = count + _writerSize = removeBytes + } + + deinit { _memory.deallocate() } + + /// Fills the buffer with padding by adding to the writersize + /// - Parameter padding: Amount of padding between two to be serialized objects + func fill(padding: UInt32) { + ensureSpace(size: UInt8(padding)) + _writerSize += (MemoryLayout.size * Int(padding)) + } + + ///Adds an array of type Scalar to the buffer memory + /// - Parameter elements: An array of Scalars + func push(elements: [T]) { + let size = elements.count * MemoryLayout.size + ensureSpace(size: UInt8(size)) + elements.lazy.reversed().forEach { (s) in + push(value: s, len: MemoryLayout.size(ofValue: s)) + } + } + + /// A custom type of structs that are padded according to the flatbuffer padding, + /// - Parameters: + /// - value: Pointer to the object in memory + /// - size: Size of Value being written to the buffer + func push(struct value: UnsafeMutableRawPointer, size: Int) { + ensureSpace(size: UInt8(size)) + _memory.advanced(by: writerIndex - size).copyMemory(from: value, byteCount: size) + defer { value.deallocate() } + _writerSize += size + } + + /// Adds an object of type Scalar into the buffer + /// - Parameters: + /// - value: Object that will be written to the buffer + /// - len: Offset to subtract from the WriterIndex + func push(value: T, len: Int) { + ensureSpace(size: UInt8(len)) + var v = value.convertedEndian + memcpy(_memory.advanced(by: writerIndex - len), &v, len) + _writerSize += len + } + + /// Adds a string to the buffer using swift.utf8 object + /// - Parameter str: String that will be added to the buffer + /// - Parameter len: length of the string + func push(string str: String, len: Int) { + ensureSpace(size: UInt8(len)) + if str.utf8.withContiguousStorageIfAvailable({ self.push(bytes: $0, len: len) }) != nil { + } else { + let utf8View = str.utf8 + for c in utf8View.lazy.reversed() { + push(value: c, len: 1) + } + } + } + + /// Writes a string to Bytebuffer using UTF8View + /// - Parameters: + /// - bytes: Pointer to the view + /// - len: Size of string + private func push(bytes: UnsafeBufferPointer, len: Int) -> Bool { + _memory.advanced(by: writerIndex - len).copyMemory(from: + UnsafeRawPointer(bytes.baseAddress!), byteCount: len) + _writerSize += len + return true + } + + /// Write stores an object into the buffer directly or indirectly. + /// + /// Direct: ignores the capacity of buffer which would mean we are referring to the direct point in memory + /// indirect: takes into respect the current capacity of the buffer (capacity - index), writing to the buffer from the end + /// - Parameters: + /// - value: Value that needs to be written to the buffer + /// - index: index to write to + /// - direct: Should take into consideration the capacity of the buffer + func write(value: T, index: Int, direct: Bool = false) { + var index = index + if !direct { + index = _capacity - index + } + _memory.storeBytes(of: value, toByteOffset: index, as: T.self) + } + + /// Makes sure that buffer has enouch space for each of the objects that will be written into it + /// - Parameter size: size of object + @discardableResult + func ensureSpace(size: UInt8) -> UInt8 { + if Int(size) + _writerSize > _capacity { reallocate(size) } + assert(size < FlatBufferMaxSize, "Buffer can't grow beyond 2 Gigabytes") + return size + } + + /// Reallocates the buffer incase the object to be written doesnt fit in the current buffer + /// - Parameter size: Size of the current object + fileprivate func reallocate(_ size: UInt8) { + let currentWritingIndex = writerIndex + while _capacity <= _writerSize + Int(size) { + _capacity = _capacity << 1 + } + + /// solution take from Apple-NIO + _capacity = _capacity.convertToPowerofTwo + + let newData = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment) + newData.initializeMemory(as: UInt8.self, repeating: 0, count: _capacity) + newData + .advanced(by: writerIndex) + .copyMemory(from: _memory.advanced(by: currentWritingIndex), byteCount: _writerSize) + _memory.deallocate() + _memory = newData + } + + /// Clears the current size of the buffer + public func clearSize() { + _writerSize = 0 + } + + /// Clears the current instance of the buffer, replacing it with new memory + public func clear() { + _writerSize = 0 + alignment = 1 + _memory.deallocate() + _memory = UnsafeMutableRawPointer.allocate(byteCount: _capacity, alignment: alignment) + } + + /// Resizes the buffer size + /// - Parameter size: new size for the buffer + internal func resize(_ size: Int) { + _writerSize = size + } + + /// Reads an object from the buffer + /// - Parameters: + /// - def: Type of the object + /// - position: the index of the object in the buffer + public func read(def: T.Type, position: Int) -> T { + return _memory.advanced(by: position).load(as: T.self) + } + + /// Reads a slice from the memory assuming a type of T + /// - Parameters: + /// - index: index of the object to be read from the buffer + /// - count: count of bytes in memory + public func readSlice(index: Int32, + count: Int32) -> [T] { + let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: T.self) + let array = UnsafeBufferPointer(start: start, count: Int(count)) + return Array(array) + } + + /// Reads a string from the buffer and encodes it to a swift string + /// - Parameters: + /// - index: index of the string in the buffer + /// - count: length of the string + /// - type: Encoding of the string + public func readString(at index: Int32, + count: Int32, + type: String.Encoding = .utf8) -> String? { + let start = _memory.advanced(by: Int(index)).assumingMemoryBound(to: UInt8.self) + let bufprt = UnsafeBufferPointer(start: start, count: Int(count)) + return String(bytes: Array(bufprt), encoding: type) + } + + /// Creates a new Flatbuffer object that's duplicated from the current one + /// - Parameter removeBytes: the amount of bytes to remove from the current Size + public func duplicate(removing removeBytes: Int = 0) -> ByteBuffer { + return ByteBuffer(memory: _memory, count: _capacity, removing: _writerSize - removeBytes) + } + + #if DEBUG + func debugMemory(str: String) { + let bufprt = UnsafeBufferPointer(start: _memory.assumingMemoryBound(to: UInt8.self), + count: _capacity) + let a = Array(bufprt) + print(str, a, " \nwith buffer size: \(a.count) and writer size: \(_writerSize)") + } + #endif +} diff --git a/swift/Sources/FlatBuffers/Constants.swift b/swift/Sources/FlatBuffers/Constants.swift new file mode 100644 index 000000000..889b0cc3c --- /dev/null +++ b/swift/Sources/FlatBuffers/Constants.swift @@ -0,0 +1,89 @@ +#if os(Linux) +import CoreFoundation +#else +import Foundation +#endif + +/// A boolean to see if the system is littleEndian +let isLitteEndian = CFByteOrderGetCurrent() == Int(CFByteOrderLittleEndian.rawValue) +/// Constant for the file id length +let FileIdLength = 4 +/// Type aliases +public typealias Byte = UInt8 +public typealias UOffset = UInt32 +public typealias SOffset = Int32 +public typealias VOffset = UInt16 +/// Maximum size for a buffer +public let FlatBufferMaxSize = UInt32.max << ((MemoryLayout.size * 8 - 1) - 1) + +/// Protocol that confirms all the numbers +/// +/// Scalar is used to confirm all the numbers that can be represented in a FlatBuffer. It's used to write/read from the buffer. +public protocol Scalar: Equatable { + associatedtype NumericValue + var convertedEndian: NumericValue { get } +} + +extension Scalar where Self: FixedWidthInteger { + /// Converts the value from BigEndian to LittleEndian + /// + /// Converts values to little endian on machines that work with BigEndian, however this is NOT TESTED yet. + public var convertedEndian: NumericValue { + if isLitteEndian { return self as! Self.NumericValue } + fatalError("This is not tested! please report an issue on the offical flatbuffers repo") + } +} + +extension Double: Scalar { + public typealias NumericValue = UInt64 + + public var convertedEndian: UInt64 { + if isLitteEndian { return self.bitPattern } + return self.bitPattern.littleEndian + } +} + +extension Float32: Scalar { + public typealias NumericValue = UInt32 + + public var convertedEndian: UInt32 { + if isLitteEndian { return self.bitPattern } + return self.bitPattern.littleEndian + } +} + +extension Int: Scalar { + public typealias NumericValue = Int +} + +extension Int8: Scalar { + public typealias NumericValue = Int8 +} + +extension Int16: Scalar { + public typealias NumericValue = Int16 +} + +extension Int32: Scalar { + public typealias NumericValue = Int32 +} + +extension Int64: Scalar { + public typealias NumericValue = Int64 +} + +extension UInt8: Scalar { + public typealias NumericValue = UInt8 +} + +extension UInt16: Scalar { + public typealias NumericValue = UInt16 +} + +extension UInt32: Scalar { + public typealias NumericValue = UInt32 +} + +extension UInt64: Scalar { + public typealias NumericValue = UInt64 +} diff --git a/swift/Sources/FlatBuffers/FlatBufferBuilder.swift b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift new file mode 100644 index 000000000..84fba1be9 --- /dev/null +++ b/swift/Sources/FlatBuffers/FlatBufferBuilder.swift @@ -0,0 +1,486 @@ +import Foundation + +public final class FlatBufferBuilder { + + /// Vtables used in the buffer are stored in here, so they would be written later in EndTable + private var _vtable: [UInt32] = [] + /// Reference Vtables that were already written to the buffer + private var _vtables: [UOffset] = [] + /// Flatbuffer data will be written into + private var _bb: ByteBuffer + /// A check if the buffer is being written into by a different table + private var isNested = false + /// Dictonary that stores a map of all the strings that were written to the buffer + private var stringOffsetMap: [String: Offset] = [:] + /// A check to see if finish(::) was ever called to retreive data object + private var finished = false + /// A check to see if the buffer should serialize Default values + private var serializeDefaults: Bool + + /// Current alignment for the buffer + var _minAlignment: Int = 0 { + didSet { + _bb.alignment = _minAlignment + } + } + + /// Gives a read access to the buffer's size + public var size: UOffset { return _bb.size } + /// Data representation of the buffer + public var data: Data { + assert(finished, "Data shouldn't be called before finish()") + return Data(bytes: _bb.memory.advanced(by: _bb.writerIndex), + count: _bb.capacity - _bb.writerIndex) + } + /// Get's the fully sized buffer stored in memory + public var fullSizedByteArray: [UInt8] { + let ptr = UnsafeBufferPointer(start: _bb.memory.assumingMemoryBound(to: UInt8.self), + count: _bb.capacity) + return Array(ptr) + } + /// Returns the written size of the buffer + public var sizedByteArray: [UInt8] { + let cp = _bb.capacity - _bb.writerIndex + let start = _bb.memory.advanced(by: _bb.writerIndex) + .bindMemory(to: UInt8.self, capacity: cp) + + let ptr = UnsafeBufferPointer(start: start, count: cp) + return Array(ptr) + } + /// Returns the buffer + public var buffer: ByteBuffer { return _bb } + + // MARK: - Init + + /// initialize the buffer with a size + /// - Parameters: + /// - initialSize: Initial size for the buffer + /// - force: Allows default to be serialized into the buffer + public init(initialSize: Int32 = 1024, serializeDefaults force: Bool = false) { + assert(initialSize > 0, "Size should be greater than zero!") + guard isLitteEndian else { + fatalError("Reading/Writing a buffer in big endian machine is not supported on swift") + } + serializeDefaults = force + _bb = ByteBuffer(initialSize: Int(initialSize)) + } + + /// Clears the buffer and the builder from it's data + public func clear() { + _minAlignment = 0 + isNested = false + _bb.clear() + stringOffsetMap = [:] + } + + /// Removes all the offsets from the VTable + public func clearOffsets() { + _vtable = [] + } +} + +// MARK: - Create Tables + +extension FlatBufferBuilder { + + /// Checks if the required fields were serialized into the buffer + /// - Parameters: + /// - table: offset for the table + /// - fields: Array of all the important fields to be serialized + public func require(table: Offset, fields: [Int32]) { + for field in fields { + let start = _bb.capacity - Int(table.o) + let startTable = start - Int(_bb.read(def: Int32.self, position: start)) + let isOkay = _bb.read(def: VOffset.self, position: startTable + Int(field)) != 0 + assert(isOkay, "Flatbuffers requires the following field") + } + } + + /// Finished the buffer by adding the file id and then calling finish + /// - Parameters: + /// - offset: Offset of the table + /// - fileId: Takes the fileId + /// - prefix: if false it wont add the size of the buffer + public func finish(offset: Offset, fileId: String, addPrefix prefix: Bool = false) { + let size = MemoryLayout.size + preAlign(len: size + (prefix ? size : 0) + FileIdLength, alignment: _minAlignment) + assert(fileId.count == FileIdLength, "Flatbuffers requires file id to be 4") + _bb.push(string: fileId, len: 4) + finish(offset: offset, addPrefix: prefix) + } + + /// Finished the buffer by adding the file id, offset, and prefix to it. + /// - Parameters: + /// - offset: Offset of the table + /// - prefix: if false it wont add the size of the buffer + public func finish(offset: Offset, addPrefix prefix: Bool = false) { + notNested() + let size = MemoryLayout.size + preAlign(len: size + (prefix ? size : 0), alignment: _minAlignment) + push(element: refer(to: offset.o)) + if prefix { push(element: _bb.size) } + clearOffsets() + finished = true + } + + /// starttable will let the builder know, that a new object is being serialized. + /// + /// The function will fatalerror if called while there is another object being serialized + /// - Parameter numOfFields: Number of elements to be written to the buffer + public func startTable(with numOfFields: Int) -> UOffset { + notNested() + isNested = true + _vtable = [UInt32](repeating: 0, count: numOfFields) + return _bb.size + } + + + /// Endtable will let the builder know that the object that's written to it is completed + /// + /// This would be called after all the elements are serialized, it will add the vtable into the buffer. + /// it will fatalError in case the object is called without starttable, or the object has exceeded the limit of + /// 2GB, + /// - Parameter startOffset:Start point of the object written + /// - returns: The root of the table + public func endTable(at startOffset: UOffset) -> UOffset { + assert(isNested, "Calling endtable without calling starttable") + let sizeofVoffset = MemoryLayout.size + let vTableOffset = push(element: SOffset(0)) + + let tableObjectSize = vTableOffset - startOffset + assert(tableObjectSize < 0x10000, "Buffer can't grow beyond 2 Gigabytes") + + var writeIndex = 0 + for (index,j) in _vtable.lazy.reversed().enumerated() { + if j != 0 { + writeIndex = _vtable.count - index + break + } + } + + for i in stride(from: writeIndex - 1, to: -1, by: -1) { + let off = _vtable[i] == 0 ? 0 : vTableOffset - _vtable[i] + _bb.push(value: VOffset(off), len: sizeofVoffset) + } + + _bb.push(value: VOffset(tableObjectSize), len: sizeofVoffset) + _bb.push(value: (UInt16(writeIndex + 2) * UInt16(sizeofVoffset)), len: sizeofVoffset) + + clearOffsets() + let vt_use = _bb.size + + var isAlreadyAdded: Int? + + mainLoop: for table in _vtables { + let vt1 = _bb.capacity - Int(table) + let vt2 = _bb.writerIndex + let len = _bb.read(def: Int16.self, position: vt1) + guard len == _bb.read(def: Int16.self, position: vt2) else { break } + for i in stride(from: sizeofVoffset, to: Int(len), by: sizeofVoffset) { + let vt1ReadValue = _bb.read(def: Int16.self, position: vt1 + i) + let vt2ReadValue = _bb.read(def: Int16.self, position: vt2 + i) + if vt1ReadValue != vt2ReadValue { + break mainLoop + } + } + isAlreadyAdded = Int(table) + } + + if let offset = isAlreadyAdded { + let vTableOff = Int(vTableOffset) + let space = _bb.capacity - vTableOff + _bb.write(value: Int32(offset - vTableOff), index: space, direct: true) + _bb.resize(_bb.capacity - space) + } else { + _bb.write(value: Int32(vt_use) - Int32(vTableOffset), index: Int(vTableOffset)) + _vtables.append(_bb.size) + } + isNested = false + return vTableOffset + } + + // MARK: - Builds Buffer + + /// asserts to see if the object is not nested + fileprivate func notNested() { + assert(!isNested, "Object serialization must not be nested") + } + + /// Changes the minimuim alignment of the buffer + /// - Parameter size: size of the current alignment + fileprivate func minAlignment(size: Int) { + if size > _minAlignment { + _minAlignment = size + } + } + + /// Gets the padding for the current element + /// - Parameters: + /// - bufSize: Current size of the buffer + the offset of the object to be written + /// - elementSize: Element size + fileprivate func padding(bufSize: UInt32, elementSize: UInt32) -> UInt32 { + ((~bufSize) + 1) & (elementSize - 1) + } + + /// Prealigns the buffer before writting a new object into the buffer + /// - Parameters: + /// - len:Length of the object + /// - alignment: Alignment type + fileprivate func preAlign(len: Int, alignment: Int) { + minAlignment(size: alignment) + _bb.fill(padding: padding(bufSize: _bb.size + UOffset(len), elementSize: UOffset(alignment))) + } + + /// Prealigns the buffer before writting a new object into the buffer + /// - Parameters: + /// - len: Length of the object + /// - type: Type of the object to be written + fileprivate func preAlign(len: Int, type: T.Type) { + preAlign(len: len, alignment: MemoryLayout.size) + } + + /// Refers to an object that's written in the buffer + /// - Parameter off: the objects index value + fileprivate func refer(to off: UOffset) -> UOffset { + let size = MemoryLayout.size + preAlign(len: size, alignment: size) + return _bb.size - off + UInt32(size) + } + + /// Tracks the elements written into the buffer + /// - Parameters: + /// - offset: The offset of the element witten + /// - position: The position of the element + fileprivate func track(offset: UOffset, at position: VOffset) { + _vtable[Int(position)] = offset + } + + // MARK: - Vectors + + /// Starts a vector of length and Element size + public func startVector(_ len: Int, elementSize: Int) { + notNested() + isNested = true + preAlign(len: len * elementSize, type: UOffset.self) + preAlign(len: len * elementSize, alignment: elementSize) + } + + /// Ends the vector of at length + /// + /// The current function will fatalError if startVector is called before serializing the vector + /// - Parameter len: Length of the buffer + public func endVector(len: Int) -> UOffset { + assert(isNested, "Calling endVector without calling startVector") + isNested = false + return push(element: Int32(len)) + } + + /// Creates a vector of type Scalar in the buffer + /// - Parameter elements: elements to be written into the buffer + /// - returns: Offset of the vector + public func createVector(_ elements: [T]) -> Offset { + return createVector(elements, size: elements.count) + } + + /// Creates a vector of type Scalar in the buffer + /// - Parameter elements: Elements to be written into the buffer + /// - Parameter size: Count of elements + /// - returns: Offset of the vector + public func createVector(_ elements: [T], size: Int) -> Offset { + let size = size + startVector(size, elementSize: MemoryLayout.size) + _bb.push(elements: elements) + return Offset(offset: endVector(len: size)) + } + + /// Creates a vector of type Enums in the buffer + /// - Parameter elements: elements to be written into the buffer + /// - returns: Offset of the vector + public func createVector(_ elements: [T]) -> Offset { + return createVector(elements, size: elements.count) + } + + /// Creates a vector of type Enums in the buffer + /// - Parameter elements: Elements to be written into the buffer + /// - Parameter size: Count of elements + /// - returns: Offset of the vector + public func createVector(_ elements: [T], size: Int) -> Offset { + let size = size + startVector(size, elementSize: T.byteSize) + for e in elements.lazy.reversed() { + _bb.push(value: e.value, len: T.byteSize) + } + return Offset(offset: endVector(len: size)) + } + + /// Creates a vector of type Offsets in the buffer + /// - Parameter offsets:Array of offsets of type T + /// - returns: Offset of the vector + public func createVector(ofOffsets offsets: [Offset]) -> Offset { + createVector(ofOffsets: offsets, len: offsets.count) + } + + /// Creates a vector of type Offsets in the buffer + /// - Parameter elements: Array of offsets of type T + /// - Parameter size: Count of elements + /// - returns: Offset of the vector + public func createVector(ofOffsets offsets: [Offset], len: Int) -> Offset { + startVector(len, elementSize: MemoryLayout>.size) + for o in offsets.lazy.reversed() { + push(element: o) + } + return Offset(offset: endVector(len: len)) + } + + /// Creates a vector of Strings + /// - Parameter str: a vector of strings that will be written into the buffer + /// - returns: Offset of the vector + public func createVector(ofStrings str: [String]) -> Offset { + var offsets: [Offset] = [] + for s in str { + offsets.append(create(string: s)) + } + return createVector(ofOffsets: offsets) + } + + /// Creates a vector of Flatbuffer structs. + /// + /// The function takes a Type to know what size it is, and alignment + /// - Parameters: + /// - structs: An array of UnsafeMutableRawPointer + /// - type: Type of the struct being written + /// - returns: Offset of the vector + public func createVector(structs: [UnsafeMutableRawPointer], + type: T.Type) -> Offset { + startVector(structs.count * T.size, elementSize: T.alignment) + for i in structs.lazy.reversed() { + create(struct: i, type: T.self) + } + return Offset(offset: endVector(len: structs.count)) + } + + // MARK: - Inserting Structs + + /// Writes a Flatbuffer struct into the buffer + /// - Parameters: + /// - s: Flatbuffer struct + /// - type: Type of the element to be serialized + /// - returns: Offset of the Object + @discardableResult + public func create(struct s: UnsafeMutableRawPointer, + type: T.Type) -> Offset { + let size = T.size + preAlign(len: size, alignment: T.alignment) + _bb.push(struct: s, size: size) + return Offset(offset: _bb.size) + } + + /// Adds the offset of a struct into the vTable + /// + /// The function fatalErrors if we pass an offset that is out of range + /// - Parameter o: offset + public func add(structOffset o: UOffset) { + guard Int(o) < _vtable.count else { fatalError("Out of the table range") } + _vtable[Int(o)] = _bb.size + } + + // MARK: - Inserting Strings + + /// Insets a string into the buffer using UTF8 + /// - Parameter str: String to be serialized + /// - returns: The strings offset in the buffer + public func create(string str: String) -> Offset { + let len = str.count + notNested() + preAlign(len: len + 1, type: UOffset.self) + _bb.fill(padding: 1) + _bb.push(string: str, len: len) + push(element: UOffset(len)) + return Offset(offset: _bb.size) + } + + /// Inserts a shared string to the buffer + /// + /// The function checks the stringOffsetmap if it's seen a similar string before + /// - Parameter str: String to be serialized + /// - returns: The strings offset in the buffer + public func createShared(string str: String) -> Offset { + if let offset = stringOffsetMap[str] { + return offset + } + let offset = create(string: str) + stringOffsetMap[str] = offset + return offset + } + + // MARK: - Inseting offsets + + /// Adds the offset of an object into the buffer + /// - Parameters: + /// - offset: Offset of another object to be written + /// - position: The predefined position of the object + public func add(offset: Offset, at position: VOffset) { + if offset.isEmpty { + track(offset: 0, at: position) + return + } + add(element: refer(to: offset.o), def: 0, at: position) + } + + /// Pushes a value of type offset into the buffer + /// - Parameter o: Offset + /// - returns: Position of the offset + @discardableResult + public func push(element o: Offset) -> UOffset { + push(element: refer(to: o.o)) + } + + // MARK: - Inserting Scalars to Buffer + + /// Adds a value into the buffer of type Scalar + /// + /// - Parameters: + /// - element: Element to insert + /// - def: Default value for that element + /// - position: The predefined position of the element + public func add(element: T, def: T, at position: VOffset) { + if (element == def && !serializeDefaults) { + track(offset: 0, at: position) + return + } + let off = push(element: element) + track(offset: off, at: position) + } + + /// Adds Boolean values into the buffer + /// - Parameters: + /// - condition: Condition to insert + /// - def: Default condition + /// - position: The predefined position of the element + public func add(condition: Bool, def: Bool, at position: VOffset) { + if (condition == def && !serializeDefaults) { + track(offset: 0, at: position) + return + } + let off = push(element: Byte(condition ? 1 : 0)) + track(offset: off, at: position) + } + + /// Pushes the values into the buffer + /// - Parameter element: Element to insert + /// - returns: Postion of the Element + @discardableResult + public func push(element: T) -> UOffset { + preAlign(len: MemoryLayout.size, + alignment: MemoryLayout.size) + _bb.push(value: element, len: MemoryLayout.size) + return _bb.size + } + + #if DEBUG + /// Used to debug the buffer and the implementation + public func debug(str: String = "normal memory: ") { + _bb.debugMemory(str: str) + } + #endif +} diff --git a/swift/Sources/FlatBuffers/FlatBufferObject.swift b/swift/Sources/FlatBuffers/FlatBufferObject.swift new file mode 100644 index 000000000..6e405f5b2 --- /dev/null +++ b/swift/Sources/FlatBuffers/FlatBufferObject.swift @@ -0,0 +1,87 @@ +import Foundation + +/// FlatbufferObject structures all the Flatbuffers objects +public protocol FlatBufferObject { + init(_ bb: ByteBuffer, o: Int32) +} + +/// Readable is structures all the Flatbuffers structs +/// +/// Readable is a procotol that each Flatbuffer struct should confirm to since +/// FlatBufferBuilder would require a Type to both create(struct:) and createVector(structs:) functions +public protocol Readable: FlatBufferObject { + static var size: Int { get } + static var alignment: Int { get } +} + +public protocol Enum { + associatedtype T: Scalar + static var byteSize: Int { get } + var value: T { get } +} + +/// Mutable is a protocol that allows us to mutate Scalar values within the buffer +public protocol Mutable { + /// makes Flatbuffer accessed within the Protocol + var bb: ByteBuffer { get } + /// makes position of the table/struct accessed within the Protocol + var postion: Int32 { get } +} + +extension Mutable { + + /// Mutates the memory in the buffer, this is only called from the access function of table and structs + /// - Parameters: + /// - value: New value to be inserted to the buffer + /// - index: index of the Element + func mutate(value: T, o: Int32) -> Bool { + guard o != 0 else { return false } + bb.write(value: value, index: Int(o), direct: true) + return true + } +} + +extension Mutable where Self == Table { + + /// Mutates a value by calling mutate with respect to the position in the table + /// - Parameters: + /// - value: New value to be inserted to the buffer + /// - index: index of the Element + public func mutate(_ value: T, index: Int32) -> Bool { + guard index != 0 else { return false } + return mutate(value: value, o: index + postion) + } + + /// Directly mutates the element by calling mutate + /// + /// Mutates the Element at index ignoring the current position by calling mutate + /// - Parameters: + /// - value: New value to be inserted to the buffer + /// - index: index of the Element + public func directMutate(_ value: T, index: Int32) -> Bool { + return mutate(value: value, o: index) + } +} + +extension Mutable where Self == Struct { + + /// Mutates a value by calling mutate with respect to the position in the struct + /// - Parameters: + /// - value: New value to be inserted to the buffer + /// - index: index of the Element + public func mutate(_ value: T, index: Int32) -> Bool { + return mutate(value: value, o: index + postion) + } + + /// Directly mutates the element by calling mutate + /// + /// Mutates the Element at index ignoring the current position by calling mutate + /// - Parameters: + /// - value: New value to be inserted to the buffer + /// - index: index of the Element + public func directMutate(_ value: T, index: Int32) -> Bool { + return mutate(value: value, o: index) + } +} +extension Struct: Mutable {} +extension Table: Mutable {} diff --git a/swift/Sources/FlatBuffers/FlatBuffersUtils.swift b/swift/Sources/FlatBuffers/FlatBuffersUtils.swift new file mode 100644 index 000000000..6838f8623 --- /dev/null +++ b/swift/Sources/FlatBuffers/FlatBuffersUtils.swift @@ -0,0 +1,16 @@ +import Foundation + +public final class FlatBuffersUtils { + + /// Gets the size of the prefix + /// - Parameter bb: Flatbuffer object + public static func getSizePrefix(bb: ByteBuffer) -> Int32 { + return bb.read(def: Int32.self, position: bb.reader) + } + + /// Removes the prefix by duplicating the Flatbuffer + /// - Parameter bb: Flatbuffer object + public static func removeSizePrefix(bb: ByteBuffer) -> ByteBuffer { + return bb.duplicate(removing: MemoryLayout.size) + } +} diff --git a/swift/Sources/FlatBuffers/Int+extension.swift b/swift/Sources/FlatBuffers/Int+extension.swift new file mode 100644 index 000000000..e52bdab62 --- /dev/null +++ b/swift/Sources/FlatBuffers/Int+extension.swift @@ -0,0 +1,31 @@ +import Foundation + +extension Int { + + /// Moves the current int into the nearest power of two + /// + /// This is used since the UnsafeMutableRawPointer will face issues when writing/reading + /// if the buffer alignment exceeds that actual size of the buffer + var convertToPowerofTwo: Int { + guard self > 0 else { return 1 } + var n = UOffset(self) + + #if arch(arm) || arch(i386) + let max = UInt32(Int.max) + #else + let max = UInt32.max + #endif + + n -= 1 + n |= n >> 1 + n |= n >> 2 + n |= n >> 4 + n |= n >> 8 + n |= n >> 16 + if n != max { + n += 1 + } + + return Int(n) + } +} diff --git a/swift/Sources/FlatBuffers/Offset.swift b/swift/Sources/FlatBuffers/Offset.swift new file mode 100644 index 000000000..cdb02278b --- /dev/null +++ b/swift/Sources/FlatBuffers/Offset.swift @@ -0,0 +1,12 @@ +import Foundation + +/// Offset object for all the Objects that are written into the buffer +public struct Offset { + /// Offset of the object in the buffer + public var o: UOffset + /// Returns false if the offset is equal to zero + public var isEmpty: Bool { return o == 0 } + + public init(offset: UOffset) { o = offset } + public init() { o = 0 } +} diff --git a/swift/Sources/FlatBuffers/Struct.swift b/swift/Sources/FlatBuffers/Struct.swift new file mode 100644 index 000000000..88e3a41a2 --- /dev/null +++ b/swift/Sources/FlatBuffers/Struct.swift @@ -0,0 +1,16 @@ +import Foundation + +public struct Struct { + public private(set) var bb: ByteBuffer + public private(set) var postion: Int32 + + public init(bb: ByteBuffer, position: Int32 = 0) { + self.bb = bb + self.postion = position + } + + public func readBuffer(of type: T.Type, at o: Int32) -> T { + let r = bb.read(def: T.self, position: Int(o + postion)) + return r + } +} diff --git a/swift/Sources/FlatBuffers/Table.swift b/swift/Sources/FlatBuffers/Table.swift new file mode 100644 index 000000000..0f783bfeb --- /dev/null +++ b/swift/Sources/FlatBuffers/Table.swift @@ -0,0 +1,144 @@ +import Foundation + +public struct Table { + public private(set) var bb: ByteBuffer + public private(set) var postion: Int32 + + public init(bb: ByteBuffer, position: Int32 = 0) { + guard isLitteEndian else { + fatalError("Reading/Writing a buffer in big endian machine is not supported on swift") + } + self.bb = bb + self.postion = position + } + + public func offset(_ o: Int32) -> Int32 { + let vtable = postion - bb.read(def: Int32.self, position: Int(postion)) + return o < bb.read(def: VOffset.self, position: Int(vtable)) ? Int32(bb.read(def: Int16.self, position: Int(vtable + o))) : 0 + } + + public func indirect(_ o: Int32) -> Int32 { return o + bb.read(def: Int32.self, position: Int(o)) } + + /// String reads from the buffer with respect to position of the current table. + /// - Parameter offset: Offset of the string + public func string(at offset: Int32) -> String? { + return directString(at: offset + postion) + } + + /// Direct string reads from the buffer disregarding the position of the table. + /// It would be preferable to use string unless the current position of the table is not needed + /// - Parameter offset: Offset of the string + public func directString(at offset: Int32) -> String? { + var offset = offset + offset += bb.read(def: Int32.self, position: Int(offset)) + let count = bb.read(def: Int32.self, position: Int(offset)) + let position = offset + Int32(MemoryLayout.size) + return bb.readString(at: position, count: count) + } + + /// Reads from the buffer with respect to the position in the table. + /// - Parameters: + /// - type: Type of Scalar that needs to be read from the buffer + /// - o: Offset of the Element + public func readBuffer(of type: T.Type, at o: Int32) -> T { + return directRead(of: T.self, offset: o + postion) + } + + /// Reads from the buffer disregarding the position of the table. + /// It would be used when reading from an + /// ``` + /// let offset = __t.offset(10) + /// //Only used when the we already know what is the + /// // position in the table since __t.vector(at:) + /// // returns the index with respect to the position + /// __t.directRead(of: Byte.self, + /// offset: __t.vector(at: offset) + index * 1) + /// ``` + /// - Parameters: + /// - type: Type of Scalar that needs to be read from the buffer + /// - o: Offset of the Element + public func directRead(of type: T.Type, offset o: Int32) -> T { + let r = bb.read(def: T.self, position: Int(o)) + return r + } + + public func union(_ o: Int32) -> T { + let o = o + postion + return directUnion(o) + } + + public func directUnion(_ o: Int32) -> T { + return T.init(bb, o: o + bb.read(def: Int32.self, position: Int(o))) + } + + public func getVector(at off: Int32) -> [T]? { + let o = offset(off) + guard o != 0 else { return nil } + return bb.readSlice(index: vector(at: o), count: vector(count: o)) + } + + /// Vector count gets the count of Elements within the array + /// - Parameter o: start offset of the vector + /// - returns: Count of elements + public func vector(count o: Int32) -> Int32 { + var o = o + o += postion + o += bb.read(def: Int32.self, position: Int(o)) + return bb.read(def: Int32.self, position: Int(o)) + } + + /// Vector start index in the buffer + /// - Parameter o:start offset of the vector + /// - returns: the start index of the vector + public func vector(at o: Int32) -> Int32 { + var o = o + o += postion + return o + bb.read(def: Int32.self, position: Int(o)) + 4 + } +} + +extension Table { + + static public func indirect(_ o: Int32, _ fbb: ByteBuffer) -> Int32 { return o + fbb.read(def: Int32.self, position: Int(o)) } + + static public func offset(_ o: Int32, vOffset: Int32, fbb: ByteBuffer) -> Int32 { + let vTable = Int32(fbb.capacity) - o + return vTable + Int32(fbb.read(def: Int16.self, position: Int(vTable + vOffset - fbb.read(def: Int32.self, position: Int(vTable))))) + } + + static public func compare(_ off1: Int32, _ off2: Int32, fbb: ByteBuffer) -> Int32 { + let memorySize = Int32(MemoryLayout.size) + let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1)) + let _off2 = off2 + fbb.read(def: Int32.self, position: Int(off2)) + let len1 = fbb.read(def: Int32.self, position: Int(_off1)) + let len2 = fbb.read(def: Int32.self, position: Int(_off2)) + let startPos1 = _off1 + memorySize + let startPos2 = _off2 + memorySize + let minValue = min(len1, len2) + for i in 0...minValue { + let b1 = fbb.read(def: Int8.self, position: Int(i + startPos1)) + let b2 = fbb.read(def: Int8.self, position: Int(i + startPos2)) + if b1 != b2 { + return Int32(b2 - b1) + } + } + return len1 - len2 + } + + static public func compare(_ off1: Int32, _ key: [Byte], fbb: ByteBuffer) -> Int32 { + let memorySize = Int32(MemoryLayout.size) + let _off1 = off1 + fbb.read(def: Int32.self, position: Int(off1)) + let len1 = fbb.read(def: Int32.self, position: Int(_off1)) + let len2 = Int32(key.count) + let startPos1 = _off1 + memorySize + let minValue = min(len1, len2) + for i in 0.. Void) -> Benchmark { + action() + let start = CFAbsoluteTimeGetCurrent() + for _ in 0.. String { + let separator = "-------------------------------------" + var document = "\(separator)\n" + document += "\(String(format: "|\t%@\t\t|\t\t%@\t\t|", "Name", "Scores"))\n" + document += "\(separator)\n" + for i in Benchmarks { + document += "\(i.description) \n" + document += "\(separator)\n" + } + return document +} + +@inlinable func create10Strings() { + let fb = FlatBufferBuilder(initialSize: 1<<20) + for _ in 0..<10_000 { + _ = fb.create(string: "foobarbaz") + } +} + +@inlinable func create100Strings(str: String) { + let fb = FlatBufferBuilder(initialSize: 1<<20) + for _ in 0..<10_000 { + _ = fb.create(string: str) + } +} + +@inlinable func benchmarkFiveHundredAdds() { + let fb = FlatBufferBuilder(initialSize: 1024 * 1024 * 32) + for _ in 0..<500_000 { + let off = fb.create(string: "T") + let s = fb.startTable(with: 4) + fb.add(element: 3.2, def: 0, at: 0) + fb.add(element: 4.2, def: 0, at: 1) + fb.add(element: 5.2, def: 0, at: 2) + fb.add(offset: off, at: 3) + _ = fb.endTable(at: s) + } +} + +func benchmark(numberOfRuns runs: Int) { + var benchmarks: [Benchmark] = [] + let str = (0...99).map { _ -> String in return "x" }.joined() + benchmarks.append(run(name: "500_000", runs: runs, action: benchmarkFiveHundredAdds)) + benchmarks.append(run(name: "10 str", runs: runs, action: create10Strings)) + let hundredStr = run(name: "100 str", runs: runs) { + create100Strings(str: str) + } + benchmarks.append(hundredStr) + print(createDocument(Benchmarks: benchmarks)) +} + +benchmark(numberOfRuns: 20) diff --git a/tests/FlatBuffers.Test.Swift/Package.swift b/tests/FlatBuffers.Test.Swift/Package.swift new file mode 100644 index 000000000..bbe5a6ab6 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Package.swift @@ -0,0 +1,20 @@ +// swift-tools-version:5.1 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "FlatBuffers.Test.Swift", + platforms: [ + .iOS(.v11), + .macOS(.v10_14), + ], + dependencies: [ + .package(path: "../../swift/") + ], + targets: [ + .testTarget( + name: "FlatBuffers.Test.SwiftTests", + dependencies: ["FlatBuffers"]), + ] +) diff --git a/tests/FlatBuffers.Test.Swift/SwiftTest.sh b/tests/FlatBuffers.Test.Swift/SwiftTest.sh new file mode 100644 index 000000000..aa8b5aca7 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/SwiftTest.sh @@ -0,0 +1,10 @@ +swift_dir=`pwd` +cd .. +test_dir=`pwd` + +${test_dir}/../flatc --swift --gen-mutable -I ${test_dir}/include_test ${test_dir}/monster_test.fbs ${test_dir}/union_vector/union_vector.fbs +cd ${test_dir} +mv *_generated.swift ${swift_dir}/Tests/FlatBuffers.Test.SwiftTests +cd ${swift_dir} +swift build --build-tests +swift test \ No newline at end of file diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersMonsterWriterTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersMonsterWriterTests.swift new file mode 100644 index 000000000..486cd1a4b --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersMonsterWriterTests.swift @@ -0,0 +1,190 @@ +import XCTest +import Foundation +@testable import FlatBuffers + +typealias Test1 = MyGame.Example.Test +typealias Monster1 = MyGame.Example.Monster +typealias Vec3 = MyGame.Example.Vec3 + +class FlatBuffersMonsterWriterTests: XCTestCase { + + func testData() { + let data = Data([48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + let _data = ByteBuffer(data: data) + readMonster(fb: _data) + } + + func testReadFromOtherLangagues() { + let path = FileManager.default.currentDirectoryPath + let url = URL(fileURLWithPath: path, isDirectory: true).appendingPathComponent("monsterdata_test").appendingPathExtension("mon") + guard let data = try? Data(contentsOf: url) else { return } + let _data = ByteBuffer(data: data) + readMonster(fb: _data) + } + + func testCreateMonster() { + let bytes = createMonster(withPrefix: false) + XCTAssertEqual(bytes.sizedByteArray, [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + readMonster(fb: bytes.buffer) + mutateMonster(fb: bytes.buffer) + readMonster(fb: bytes.buffer) + } + + func testCreateMonsterResizedBuffer() { + let bytes = createMonster(withPrefix: false) + XCTAssertEqual(bytes.sizedByteArray, [48, 0, 0, 0, 77, 79, 78, 83, 0, 0, 0, 0, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + readMonster(fb: ByteBuffer(data: bytes.data)) + } + + func testCreateMonsterPrefixed() { + let bytes = createMonster(withPrefix: true) + XCTAssertEqual(bytes.sizedByteArray, [44, 1, 0, 0, 44, 0, 0, 0, 77, 79, 78, 83, 36, 0, 72, 0, 40, 0, 0, 0, 38, 0, 32, 0, 0, 0, 28, 0, 0, 0, 27, 0, 20, 0, 16, 0, 12, 0, 4, 0, 0, 0, 0, 0, 0, 0, 11, 0, 36, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 68, 0, 0, 0, 76, 0, 0, 0, 0, 0, 0, 1, 88, 0, 0, 0, 120, 0, 0, 0, 0, 0, 80, 0, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 64, 2, 0, 5, 0, 6, 0, 0, 0, 2, 0, 0, 0, 64, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 30, 0, 40, 0, 10, 0, 20, 0, 152, 255, 255, 255, 4, 0, 0, 0, 4, 0, 0, 0, 70, 114, 101, 100, 0, 0, 0, 0, 5, 0, 0, 0, 0, 1, 2, 3, 4, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 50, 0, 0, 0, 5, 0, 0, 0, 116, 101, 115, 116, 49, 0, 0, 0, 9, 0, 0, 0, 77, 121, 77, 111, 110, 115, 116, 101, 114, 0, 0, 0, 3, 0, 0, 0, 20, 0, 0, 0, 36, 0, 0, 0, 4, 0, 0, 0, 240, 255, 255, 255, 32, 0, 0, 0, 248, 255, 255, 255, 36, 0, 0, 0, 12, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4, 0, 12, 0, 0, 0, 28, 0, 0, 0, 5, 0, 0, 0, 87, 105, 108, 109, 97, 0, 0, 0, 6, 0, 0, 0, 66, 97, 114, 110, 101, 121, 0, 0, 5, 0, 0, 0, 70, 114, 111, 100, 111, 0, 0, 0]) + + let newBuf = FlatBuffersUtils.removeSizePrefix(bb: bytes.buffer) + readMonster(fb: newBuf) + } + + func createMonster(withPrefix prefix: Bool) -> FlatBufferBuilder { + let fbb = FlatBufferBuilder(initialSize: 1) + let names = [fbb.create(string: "Frodo"), fbb.create(string: "Barney"), fbb.create(string: "Wilma")] + var offsets: [Offset] = [] + let start1 = Monster1.startMonster(fbb) + Monster1.add(name: names[0], fbb) + offsets.append(Monster1.endMonster(fbb, start: start1)) + let start2 = Monster1.startMonster(fbb) + Monster1.add(name: names[1], fbb) + offsets.append(Monster1.endMonster(fbb, start: start2)) + let start3 = Monster1.startMonster(fbb) + Monster1.add(name: names[2], fbb) + offsets.append(Monster1.endMonster(fbb, start: start3)) + + let sortedArray = Monster1.sortVectorOfMonster(offsets: offsets, fbb) + + let str = fbb.create(string: "MyMonster") + let test1 = fbb.create(string: "test1") + let test2 = fbb.create(string: "test2") + let _inv: [Byte] = [0, 1, 2, 3, 4] + let inv = fbb.createVector(_inv) + + let fred = fbb.create(string: "Fred") + let mon1Start = Monster1.startMonster(fbb) + Monster1.add(name: fred, fbb) + let mon2 = Monster1.endMonster(fbb, start: mon1Start) + let test4 = fbb.createVector(structs: [MyGame.Example.createTest(a: 30, b: 40), + MyGame.Example.createTest(a: 10, b: 20)], + type: Test1.self) + + let stringTestVector = fbb.createVector(ofOffsets: [test1, test2]) + + let mStart = Monster1.startMonster(fbb) + let posOffset = fbb.create(struct: MyGame.Example.createVec3(x: 1, y: 2, z: 3, test1: 3, test2: .green, test3a: 5, test3b: 6), type: Vec3.self) + Monster1.add(pos: posOffset, fbb) + Monster1.add(hp: 80, fbb) + Monster1.add(name: str, fbb) + Monster1.addVectorOf(inventory: inv, fbb) + Monster1.add(testType: .monster, fbb) + Monster1.add(test: mon2, fbb) + Monster1.addVectorOf(test4: test4, fbb) + Monster1.addVectorOf(testarrayofstring: stringTestVector, fbb) + Monster1.add(testbool: true, fbb) + Monster1.addVectorOf(testarrayoftables: sortedArray, fbb) + let end = Monster1.endMonster(fbb, start: mStart) + Monster1.finish(fbb, end: end, prefix: prefix) + return fbb + } + + func mutateMonster(fb: ByteBuffer) { + let monster = Monster1.getRootAsMonster(bb: fb) + XCTAssertFalse(monster.mutate(mana: 10)) + XCTAssertEqual(monster.testarrayoftables(at: 0)?.name, "Barney") + XCTAssertEqual(monster.testarrayoftables(at: 1)?.name, "Frodo") + XCTAssertEqual(monster.testarrayoftables(at: 2)?.name, "Wilma") + + // Example of searching for a table by the key + XCTAssertNotNil(monster.testarrayoftablesBy(key: "Frodo")) + XCTAssertNotNil(monster.testarrayoftablesBy(key: "Barney")) + XCTAssertNotNil(monster.testarrayoftablesBy(key: "Wilma")) + + XCTAssertEqual(monster.testType, .monster) + + XCTAssertEqual(monster.mutate(inventory: 1, at: 0), true) + XCTAssertEqual(monster.mutate(inventory: 2, at: 1), true) + XCTAssertEqual(monster.mutate(inventory: 3, at: 2), true) + XCTAssertEqual(monster.mutate(inventory: 4, at: 3), true) + XCTAssertEqual(monster.mutate(inventory: 5, at: 4), true) + + for i in 0.. UnsafeMutableRawPointer{ + let memory = UnsafeMutableRawPointer.allocate(byteCount: Vec.size, alignment: Vec.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Vec.size) + memory.storeBytes(of: x, toByteOffset: 0, as: Float32.self) + memory.storeBytes(of: y, toByteOffset: 4, as: Float32.self) + memory.storeBytes(of: z, toByteOffset: 8, as: Float32.self) + return memory +} + +struct Vec: Readable { + static var size = 12 + static var alignment = 4 + private var __p: Struct + init(_ fb: ByteBuffer, o: Int32) { __p = Struct(bb: fb, position: o) } + var x: Float32 { return __p.readBuffer(of: Float32.self, at: 0)} + var y: Float32 { return __p.readBuffer(of: Float32.self, at: 4)} + var z: Float32 { return __p.readBuffer(of: Float32.self, at: 8)} +} + +struct VPointerVec { + + private var __t: Table + + private init(_ t: Table) { + __t = t + } + + var vec: Vec? { let o = __t.offset(4); return o == 0 ? nil : Vec(__t.bb, o: o + __t.postion) } + + @inlinable static func getRootAsCountry(_ bb: ByteBuffer) -> VPointerVec { + return VPointerVec(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: 0)))) + } + + static func startVPointer(b: FlatBufferBuilder) -> UOffset { b.startTable(with: 1) } + static func finish(b: FlatBufferBuilder, s: UOffset) -> Offset { return Offset(offset: b.endTable(at: s)) } + + static func createVPointer(b: FlatBufferBuilder, o: Offset) -> Offset { + let s = VPointerVec.startVPointer(b: b) + b.add(structOffset: 0) + return VPointerVec.finish(b: b, s: s) + } +} + +enum Color: UInt32 { case red = 0, green = 1, blue = 2 } + +private let VPointerVectorVecOffsets: (color: VOffset, vector: VOffset) = (0, 1) + +struct VPointerVectorVec { + + static func startVPointer(b: FlatBufferBuilder) -> UOffset { b.startTable(with: 2) } + + static func addVector(b: FlatBufferBuilder, v: Offset) { b.add(offset: v, at: VPointerVectorVecOffsets.vector) } + + static func addColor(b: FlatBufferBuilder, color: Color) { b.add(element: color.rawValue, def: 1, at: VPointerVectorVecOffsets.color) } + + static func finish(b: FlatBufferBuilder, s: UOffset) -> Offset { return Offset(offset: b.endTable(at: s)) } + + static func createVPointer(b: FlatBufferBuilder, color: Color = .green, v: Offset) -> Offset { + let s = VPointerVectorVec.startVPointer(b: b) + VPointerVectorVec.addVector(b: b, v: v) + VPointerVectorVec.addColor(b: b, color: color) + return VPointerVectorVec.finish(b: b, s: s) + } +} + +enum Color2: Int32 { case red = 0, green = 1, blue = 2 } +enum Test: Byte { case none = 0, vec = 1 } + +func createVec2(x: Float32 = 0, y: Float32 = 0, z: Float32 = 0, color: Color2) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: Vec2.size, alignment: Vec2.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Vec2.size) + memory.storeBytes(of: x, toByteOffset: 0, as: Float32.self) + memory.storeBytes(of: y, toByteOffset: 4, as: Float32.self) + memory.storeBytes(of: z, toByteOffset: 8, as: Float32.self) + return memory +} + +struct Vec2: Readable { + static var size = 13 + static var alignment = 4 + private var __p: Struct + + init(_ fb: ByteBuffer, o: Int32) { __p = Struct(bb: fb, position: o) } + var c: Color2 { return Color2(rawValue: __p.readBuffer(of: Int32.self, at: 12)) ?? .red } + var x: Float32 { return __p.readBuffer(of: Float32.self, at: 0)} + var y: Float32 { return __p.readBuffer(of: Float32.self, at: 4)} + var z: Float32 { return __p.readBuffer(of: Float32.self, at: 8)} +} + +struct VPointerVec2 { + + private var __t: Table + + private init(_ t: Table) { + __t = t + } + + var vec: Vec2? { let o = __t.offset(4); return o == 0 ? nil : Vec2( __t.bb, o: o + __t.postion) } + var UType: Test? { let o = __t.offset(6); return o == 0 ? Test.none : Test(rawValue: __t.readBuffer(of: Byte.self, at: o)) } + + @inlinable static func getRootAsCountry(_ bb: ByteBuffer) -> VPointerVec2 { + return VPointerVec2(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: 0)))) + } + + static func startVPointer(b: FlatBufferBuilder) -> UOffset { b.startTable(with: 3) } + static func finish(b: FlatBufferBuilder, s: UOffset) -> Offset { return Offset(offset: b.endTable(at: s)) } + + static func createVPointer(b: FlatBufferBuilder, o: Offset, type: Test) -> Offset { + let s = VPointerVec2.startVPointer(b: b) + b.add(structOffset: 0) + b.add(element: type.rawValue, def: Test.none.rawValue, at: 1) + b.add(offset: o, at: 2) + return VPointerVec2.finish(b: b, s: s) + } +} diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersTests.swift new file mode 100644 index 000000000..e8e737e2f --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersTests.swift @@ -0,0 +1,118 @@ +import XCTest +@testable import FlatBuffers + +final class FlatBuffersTests: XCTestCase { + + let country = "Norway" + + func testEndian() { XCTAssertEqual(isLitteEndian, true) } + + func testOffset() { + let o = Offset() + let b = Offset(offset: 1) + XCTAssertEqual(o.isEmpty, true) + XCTAssertEqual(b.isEmpty, false) + } + + func testCreateString() { + let helloWorld = "Hello, world!" + let b = FlatBufferBuilder(initialSize: 16) + XCTAssertEqual(b.create(string: country).o, 12) + XCTAssertEqual(b.create(string: helloWorld).o, 32) + b.clear() + XCTAssertEqual(b.create(string: helloWorld).o, 20) + XCTAssertEqual(b.create(string: country).o, 32) + } + + func testStartTable() { + let b = FlatBufferBuilder(initialSize: 16) + XCTAssertNoThrow(b.startTable(with: 0)) + b.clear() + XCTAssertEqual(b.create(string: country).o, 12) + XCTAssertEqual(b.startTable(with: 0), 12) + } + + func testCreate() { + var b = FlatBufferBuilder(initialSize: 16) + _ = Country.createCountry(builder: &b, name: country, log: 200, lan: 100) + let v: [UInt8] = [10, 0, 16, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 12, 0, 0, 0, 100, 0, 0, 0, 200, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } + + func testCreateFinish() { + var b = FlatBufferBuilder(initialSize: 16) + let countryOff = Country.createCountry(builder: &b, name: country, log: 200, lan: 100) + b.finish(offset: countryOff) + let v: [UInt8] = [16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 12, 0, 0, 0, 100, 0, 0, 0, 200, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } + + func testCreateFinishWithPrefix() { + var b = FlatBufferBuilder(initialSize: 16) + let countryOff = Country.createCountry(builder: &b, name: country, log: 200, lan: 100) + b.finish(offset: countryOff, addPrefix: true) + let v: [UInt8] = [44, 0, 0, 0, 16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 12, 0, 0, 0, 100, 0, 0, 0, 200, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } + + func testReadCountry() { + let v: [UInt8] = [16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 12, 0, 0, 0, 100, 0, 0, 0, 200, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + let buffer = ByteBuffer(bytes: v) + let c = Country.getRootAsCountry(buffer) + XCTAssertEqual(c.lan, 100) + XCTAssertEqual(c.log, 200) + XCTAssertEqual(c.nameVector, [78, 111, 114, 119, 97, 121]) + XCTAssertEqual(c.name, country) + } +} + +class Country { + + static let offsets: (name: VOffset, lan: VOffset, lng: VOffset) = (0, 1, 2) + private var __t: Table + + private init(_ t: Table) { + __t = t + } + + var lan: Int32 { let o = __t.offset(6); return o == 0 ? 0 : __t.readBuffer(of: Int32.self, at: o) } + var log: Int32 { let o = __t.offset(8); return o == 0 ? 0 : __t.readBuffer(of: Int32.self, at: o) } + var nameVector: [UInt8]? { return __t.getVector(at: 4) } + var name: String? { let o = __t.offset(4); return o == 0 ? nil : __t.string(at: o) } + + @inlinable static func getRootAsCountry(_ bb: ByteBuffer) -> Country { + return Country(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: 0)))) + } + + @inlinable static func createCountry(builder: inout FlatBufferBuilder, name: String, log: Int32, lan: Int32) -> Offset { + return createCountry(builder: &builder, offset: builder.create(string: name), log: log, lan: lan) + } + + @inlinable static func createCountry(builder: inout FlatBufferBuilder, offset: Offset, log: Int32, lan: Int32) -> Offset { + let _start = builder.startTable(with: 3) + Country.add(builder: &builder, lng: log) + Country.add(builder: &builder, lan: lan) + Country.add(builder: &builder, name: offset) + return Country.end(builder: &builder, startOffset: _start) + } + + @inlinable static func end(builder: inout FlatBufferBuilder, startOffset: UOffset) -> Offset { + return Offset(offset: builder.endTable(at: startOffset)) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, name: String) { + add(builder: &builder, name: builder.create(string: name)) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, name: Offset) { + builder.add(offset: name, at: Country.offsets.name) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, lan: Int32) { + builder.add(element: lan, def: 0, at: Country.offsets.lan) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, lng: Int32) { + builder.add(element: lng, def: 0, at: Country.offsets.lng) + } +} diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersUnionTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersUnionTests.swift new file mode 100644 index 000000000..d65242934 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersUnionTests.swift @@ -0,0 +1,229 @@ +import XCTest +@testable import FlatBuffers + +final class FlatBuffersUnionTests: XCTestCase { + + func testCreateMonstor() { + + var b = FlatBufferBuilder(initialSize: 20) + let dmg: Int16 = 5 + let str = "Axe" + let axe = b.create(string: str) + let weapon = Weapon.createWeapon(builder: &b, offset: axe, dmg: dmg) + let weapons = b.createVector(ofOffsets: [weapon]) + let root = Monster.createMonster(builder: &b, + offset: weapons, + equipment: .Weapon, + equippedOffset: weapon.o) + b.finish(offset: root) + let buffer = b.sizedByteArray + XCTAssertEqual(buffer, [16, 0, 0, 0, 0, 0, 10, 0, 16, 0, 8, 0, 7, 0, 12, 0, 10, 0, 0, 0, 0, 0, 0, 1, 8, 0, 0, 0, 20, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 12, 0, 8, 0, 6, 0, 8, 0, 0, 0, 0, 0, 5, 0, 4, 0, 0, 0, 3, 0, 0, 0, 65, 120, 101, 0]) + let monster = Monster.getRootAsMonster(bb: ByteBuffer(bytes: buffer)) + XCTAssertEqual(monster.weapon(at: 0)?.dmg, dmg) + XCTAssertEqual(monster.weapon(at: 0)?.name, str) + XCTAssertEqual(monster.weapon(at: 0)?.nameVector, [65, 120, 101]) + let p: Weapon? = monster.equiped() + XCTAssertEqual(p?.dmg, dmg) + XCTAssertEqual(p?.name, str) + XCTAssertEqual(p?.nameVector, [65, 120, 101]) + } + + func testEndTableFinish() { + var builder = FlatBufferBuilder(initialSize: 20) + let sword = builder.create(string: "Sword") + let axe = builder.create(string: "Axe") + let weaponOne = Weapon.createWeapon(builder: &builder, offset: sword, dmg: 3) + let weaponTwo = Weapon.createWeapon(builder: &builder, offset: axe, dmg: 5) + let name = builder.create(string: "Orc") + let inventory: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + let inv = builder.createVector(inventory, size: 10) + let weapons = builder.createVector(ofOffsets: [weaponOne, weaponTwo]) + var vecArray: [UnsafeMutableRawPointer] = [] + vecArray.append(createVecWrite(x: 4.0, y: 5.0, z: 6.0)) + vecArray.append(createVecWrite(x: 1.0, y: 2.0, z: 3.0)) + let path = builder.createVector(structs: vecArray, type: Vec.self) + let orc = FinalMonster.createMonster(builder: &builder, + position: builder.create(struct: createVecWrite(x: 1.0, y: 2.0, z: 3.0), type: Vec.self), + hp: 300, + name: name, + inventory: inv, + color: .red, + weapons: weapons, + equipment: .Weapon, + equippedOffset: weaponTwo, + path: path) + builder.finish(offset: orc) + XCTAssertEqual(builder.sizedByteArray, [32, 0, 0, 0, 0, 0, 26, 0, 36, 0, 36, 0, 0, 0, 34, 0, 28, 0, 0, 0, 24, 0, 23, 0, 16, 0, 15, 0, 8, 0, 4, 0, 26, 0, 0, 0, 44, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 1, 60, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 76, 0, 0, 0, 0, 0, 44, 1, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 0, 0, 0, 0, 128, 64, 0, 0, 160, 64, 0, 0, 192, 64, 0, 0, 128, 63, 0, 0, 0, 64, 0, 0, 64, 64, 2, 0, 0, 0, 52, 0, 0, 0, 28, 0, 0, 0, 10, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 3, 0, 0, 0, 79, 114, 99, 0, 244, 255, 255, 255, 0, 0, 5, 0, 24, 0, 0, 0, 8, 0, 12, 0, 8, 0, 6, 0, 8, 0, 0, 0, 0, 0, 3, 0, 12, 0, 0, 0, 3, 0, 0, 0, 65, 120, 101, 0, 5, 0, 0, 0, 83, 119, 111, 114, 100, 0, 0, 0]) + } + + func testEnumVector() { + let vectorOfEnums: [ColorsNameSpace.RGB] = [.blue, .green] + + let builder = FlatBufferBuilder(initialSize: 1) + let off = builder.createVector(vectorOfEnums) + let start = ColorsNameSpace.Monster.startMonster(builder) + ColorsNameSpace.Monster.add(colors: off, builder) + let end = ColorsNameSpace.Monster.endMonster(builder, start: start) + builder.finish(offset: end) + XCTAssertEqual(builder.sizedByteArray, [12, 0, 0, 0, 0, 0, 6, 0, 8, 0, 4, 0, 6, 0, 0, 0, 4, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0]) + let monster = ColorsNameSpace.Monster.getRootAsMonster(bb: builder.buffer) + XCTAssertEqual(monster.colorsCount, 2) + XCTAssertEqual(monster.colors(at: 0), .blue) + XCTAssertEqual(monster.colors(at: 1), .green) + } + + func testUnionVector() { + let fb = FlatBufferBuilder() + + let swordDmg: Int32 = 8 + let attackStart = Attacker.startAttacker(fb) + Attacker.add(swordAttackDamage: swordDmg, fb) + let attack = Attacker.endAttacker(fb, start: attackStart) + + let characterType: [Character] = [.belle, .mulan, .bookfan] + let characters = [ + fb.create(struct: createBookReader(booksRead: 7), type: BookReader.self), + attack, + fb.create(struct: createBookReader(booksRead: 2), type: BookReader.self), + ] + let types = fb.createVector(characterType) + let characterVector = fb.createVector(ofOffsets: characters) + + let movieStart = Movie.startMovie(fb) + Movie.addVectorOf(charactersType: types, fb) + Movie.addVectorOf(characters: characterVector, fb) + let end = Movie.endMovie(fb, start: movieStart) + Movie.finish(fb, end: end) + + let movie = Movie.getRootAsMovie(bb: fb.buffer) + XCTAssertEqual(movie.charactersTypeCount, Int32(characterType.count)) + XCTAssertEqual(movie.charactersCount, Int32(characters.count)) + + for i in 0...size } + var value: Int32 { return self.rawValue } + case red = 0, green = 1, blue = 2 +} + +struct Monster: FlatBufferObject { + private var _accessor: Table + static func getRootAsMonster(bb: ByteBuffer) -> Monster { return Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + init(_ t: Table) { _accessor = t } + init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var colorsCount: Int32 { let o = _accessor.offset(4); return o == 0 ? 0 : _accessor.vector(count: o) } + public func colors(at index: Int32) -> ColorsNameSpace.RGB? { let o = _accessor.offset(4); return o == 0 ? ColorsNameSpace.RGB(rawValue: 0)! : ColorsNameSpace.RGB(rawValue: _accessor.directRead(of: Int32.self, offset: _accessor.vector(at: o) + index * 4)) } + static func startMonster(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + static func add(colors: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: colors, at: 0) } + static func endMonster(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} +} + + +enum Equipment: Byte { case none, Weapon } + +enum Color3: Int8 { case red = 0, green, blue } + +struct FinalMonster { + + @inlinable static func createMonster(builder: inout FlatBufferBuilder, + position: Offset, + hp: Int16, + name: Offset, + inventory: Offset, + color: Color3, + weapons: Offset, + equipment: Equipment = .none, + equippedOffset: Offset, + path: Offset) -> Offset { + let start = builder.startTable(with: 11) + builder.add(structOffset: 0) + builder.add(element: hp, def: 100, at: 2) + builder.add(offset: name, at: 3) + builder.add(offset: inventory, at: 5) + builder.add(element: color.rawValue, def: Color3.green.rawValue, at: 6) + builder.add(offset: weapons, at: 7) + builder.add(element: equipment.rawValue, def: Equipment.none.rawValue, at: 8) + builder.add(offset: equippedOffset, at: 9) + builder.add(offset: path, at: 10) + return Offset(offset: builder.endTable(at: start)) + } +} + +struct Monster { + + private var __t: Table + + init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o) } + init(_ t: Table) { __t = t } + + func weapon(at index: Int32) -> Weapon? { let o = __t.offset(4); return o == 0 ? nil : Weapon.assign(__t.indirect(__t.vector(at: o) + (index * 4)), __t.bb) } + + func equiped() -> T? { + let o = __t.offset(8); return o == 0 ? nil : __t.union(o) + } + + static func getRootAsMonster(bb: ByteBuffer) -> Monster { + return Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: 0)))) + } + + @inlinable static func createMonster(builder: inout FlatBufferBuilder, + offset: Offset, + equipment: Equipment = .none, + equippedOffset: UOffset) -> Offset { + let start = builder.startTable(with: 3) + builder.add(element: equippedOffset, def: 0, at: 2) + builder.add(offset: offset, at: 0) + builder.add(element: equipment.rawValue, def: Equipment.none.rawValue, at: 1) + return Offset(offset: builder.endTable(at: start)) + } +} + + +struct Weapon: FlatBufferObject { + + static let offsets: (name: VOffset, dmg: VOffset) = (0, 1) + private var __t: Table + + init(_ t: Table) { __t = t } + init(_ fb: ByteBuffer, o: Int32) { __t = Table(bb: fb, position: o)} + + var dmg: Int16 { let o = __t.offset(6); return o == 0 ? 0 : __t.readBuffer(of: Int16.self, at: o) } + var nameVector: [UInt8]? { return __t.getVector(at: 4) } + var name: String? { let o = __t.offset(4); return o == 0 ? nil : __t.string(at: o) } + + static func assign(_ i: Int32, _ bb: ByteBuffer) -> Weapon { return Weapon(Table(bb: bb, position: i)) } + + @inlinable static func createWeapon(builder: inout FlatBufferBuilder, offset: Offset, dmg: Int16) -> Offset { + let _start = builder.startTable(with: 2) + Weapon.add(builder: &builder, name: offset) + Weapon.add(builder: &builder, dmg: dmg) + return Weapon.end(builder: &builder, startOffset: _start) + } + + @inlinable static func end(builder: inout FlatBufferBuilder, startOffset: UOffset) -> Offset { + return Offset(offset: builder.endTable(at: startOffset)) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, name: Offset) { + builder.add(offset: name, at: Weapon.offsets.name) + } + + @inlinable static func add(builder: inout FlatBufferBuilder, dmg: Int16) { + builder.add(element: dmg, def: 0, at: Weapon.offsets.dmg) + } +} diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift new file mode 100644 index 000000000..5e6301ffe --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatBuffersVectorsTests.swift @@ -0,0 +1,108 @@ +import XCTest +@testable import FlatBuffers + +final class FlatBuffersVectors: XCTestCase { + + func testCreatingTwoCountries() { + let norway = "Norway" + let denmark = "Denmark" + var b = FlatBufferBuilder(initialSize: 20) + let noStr = b.create(string: norway) + let deStr = b.create(string: denmark) + let n = Country.createCountry(builder: &b, offset: noStr, log: 888, lan: 700) + let d = Country.createCountry(builder: &b, offset: deStr, log: 200, lan: 100) + let vector = [n, d] + let vectorOffset = b.createVector(ofOffsets: vector) + b.finish(offset: vectorOffset) + XCTAssertEqual(b.sizedByteArray, [4, 0, 0, 0, 2, 0, 0, 0, 48, 0, 0, 0, 16, 0, 0, 0, 0, 0, 10, 0, 18, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 40, 0, 0, 0, 100, 0, 0, 0, 200, 0, 0, 0, 0, 0, 10, 0, 16, 0, 4, 0, 8, 0, 12, 0, 10, 0, 0, 0, 24, 0, 0, 0, 188, 2, 0, 0, 120, 3, 0, 0, 7, 0, 0, 0, 68, 101, 110, 109, 97, 114, 107, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0]) + } + + func testCreateIntArray() { + let numbers: [Int32] = [1, 2, 3, 4, 5] + let b = FlatBufferBuilder(initialSize: 20) + let o = b.createVector(numbers, size: numbers.count) + b.finish(offset: o) + XCTAssertEqual(b.sizedByteArray, [4, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4, 0, 0, 0, 5, 0, 0, 0]) + } + + func testCreateVectorOfStrings() { + let strs = ["Denmark", "Norway"] + let b = FlatBufferBuilder(initialSize: 20) + let o = b.createVector(ofStrings: strs) + b.finish(offset: o) + XCTAssertEqual(b.sizedByteArray, [4, 0, 0, 0, 2, 0, 0, 0, 20, 0, 0, 0, 4, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0, 7, 0, 0, 0, 68, 101, 110, 109, 97, 114, 107, 0]) + } + func testCreateSharedStringVector() { + let norway = "Norway" + let denmark = "Denmark" + let b = FlatBufferBuilder(initialSize: 20) + let noStr = b.createShared(string: norway) + let deStr = b.createShared(string: denmark) + let _noStr = b.createShared(string: norway) + let _deStr = b.createShared(string: denmark) + let v = [noStr, deStr, _noStr, _deStr] + let end = b.createVector(ofOffsets: v) + b.finish(offset: end) + XCTAssertEqual(b.sizedByteArray, [4, 0, 0, 0, 4, 0, 0, 0, 28, 0, 0, 0, 12, 0, 0, 0, 20, 0, 0, 0, 4, 0, 0, 0, 7, 0, 0, 0, 68, 101, 110, 109, 97, 114, 107, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0]) + } + + func testReadInt32Array() { + let data: [Int32] = [1, 2, 3, 4, 5] + let b = FlatBufferBuilder(initialSize: 20) + let v = Numbers.createNumbersVector(b: b, array: data) + let end = Numbers.createNumbers(b: b, o: v) + b.finish(offset: end) + let number = Numbers.getRootAsNumbers(ByteBuffer(bytes: b.sizedByteArray)) + XCTAssertEqual(number.vArrayInt32, [1, 2, 3, 4, 5]) + } + + func testReadDoubleArray() { + let data: [Double] = [1, 2, 3, 4, 5] + let b = FlatBufferBuilder(initialSize: 20) + let v = Numbers.createNumbersVector(b: b, array: data) + let end = Numbers.createNumbers(b: b, o: v) + b.finish(offset: end) + let number = Numbers.getRootAsNumbers(ByteBuffer(bytes: b.sizedByteArray)) + XCTAssertEqual(number.vArrayDouble, [1, 2, 3, 4, 5]) + } +} + +struct Numbers { + + private var __t: Table + + private init(_ t: Table) { + __t = t + } + + @inlinable static func getRootAsNumbers(_ bb: ByteBuffer) -> Numbers { + return Numbers(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: 0)))) + } + + var vArrayInt: [Int]? { return __t.getVector(at: 4) } + var vArrayInt32: [Int32]? { return __t.getVector(at: 4) } + var vArrayDouble: [Double]? { return __t.getVector(at: 4) } + var vArrayFloat: [Float32]? { return __t.getVector(at: 4) } + + static func createNumbersVector(b: FlatBufferBuilder, array: [Int]) -> Offset { + return b.createVector(array, size: array.count) + } + + static func createNumbersVector(b: FlatBufferBuilder, array: [Int32]) -> Offset { + return b.createVector(array, size: array.count) + } + + static func createNumbersVector(b: FlatBufferBuilder, array: [Double]) -> Offset { + return b.createVector(array, size: array.count) + } + + static func createNumbersVector(b: FlatBufferBuilder, array: [Float32]) -> Offset { + return b.createVector(array, size: array.count) + } + + static func createNumbers(b: FlatBufferBuilder, o: Offset) -> Offset { + let start = b.startTable(with: 1) + b.add(offset: o, at: 0) + return Offset(offset: b.endTable(at: start)) + } +} diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersDoubleTests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersDoubleTests.swift new file mode 100644 index 000000000..c044c5ca8 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/FlatbuffersDoubleTests.swift @@ -0,0 +1,76 @@ +import XCTest +@testable import FlatBuffers + +final class FlatBuffersDoubleTests: XCTestCase { + + let country = "Norway" + + func testCreateCountry() { + var b = FlatBufferBuilder(initialSize: 16) + _ = CountryDouble.createCountry(builder: &b, name: country, log: 200, lan: 100) + let v: [UInt8] = [10, 0, 28, 0, 4, 0, 8, 0, 16, 0, 10, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 64, 0, 0, 0, 0, 0, 0, 105, 64, 0, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } + + func testCreateFinish() { + var b = FlatBufferBuilder(initialSize: 16) + let countryOff = CountryDouble.createCountry(builder: &b, name: country, log: 200, lan: 100) + b.finish(offset: countryOff) + let v: [UInt8] = [16, 0, 0, 0, 0, 0, 10, 0, 28, 0, 4, 0, 8, 0, 16, 0, 10, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 64, 0, 0, 0, 0, 0, 0, 105, 64, 0, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } + + func testCreateFinishWithPrefix() { + var b = FlatBufferBuilder(initialSize: 16) + let countryOff = CountryDouble.createCountry(builder: &b, name: country, log: 200, lan: 100) + b.finish(offset: countryOff, addPrefix: true) + let v: [UInt8] = [60, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 28, 0, 4, 0, 8, 0, 16, 0, 10, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 89, 64, 0, 0, 0, 0, 0, 0, 105, 64, 0, 0, 0, 0, 6, 0, 0, 0, 78, 111, 114, 119, 97, 121, 0, 0] + XCTAssertEqual(b.sizedByteArray, v) + } +} + +class CountryDouble { + + static let offsets: (name: VOffset, lan: VOffset, lng: VOffset) = (4,6,8) + + private var table: Table + + private init(table t: Table) { table = t } + + static func getRootAsCountry(_ bb: ByteBuffer) -> CountryDouble { + let pos = bb.read(def: Int32.self, position: Int(bb.size)) + return CountryDouble(table: Table(bb: bb, position: Int32(pos))) + } + + static func createCountry(builder: inout FlatBufferBuilder, name: String, log: Double, lan: Double) -> Offset { + return createCountry(builder: &builder, offset: builder.create(string: name), log: log, lan: lan) + } + + static func createCountry(builder: inout FlatBufferBuilder, offset: Offset, log: Double, lan: Double) -> Offset { + let _start = builder.startTable(with: 3) + CountryDouble.add(builder: &builder, lng: log) + CountryDouble.add(builder: &builder, lan: lan) + CountryDouble.add(builder: &builder, name: offset) + return CountryDouble.end(builder: &builder, startOffset: _start) + } + + static func end(builder: inout FlatBufferBuilder, startOffset: UOffset) -> Offset { + return Offset(offset: builder.endTable(at: startOffset)) + } + + static func add(builder: inout FlatBufferBuilder, name: String) { + add(builder: &builder, name: builder.create(string: name)) + } + + static func add(builder: inout FlatBufferBuilder, name: Offset) { + builder.add(offset: name, at: Country.offsets.name) + } + + static func add(builder: inout FlatBufferBuilder, lan: Double) { + builder.add(element: lan, def: 0, at: Country.offsets.lan) + } + + static func add(builder: inout FlatBufferBuilder, lng: Double) { + builder.add(element: lng, def: 0, at: Country.offsets.lng) + } +} diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/XCTestManifests.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/XCTestManifests.swift new file mode 100644 index 000000000..d53a7a6c8 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/XCTestManifests.swift @@ -0,0 +1,94 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension FlatBuffersDoubleTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersDoubleTests = [ + ("testCreateCountry", testCreateCountry), + ("testCreateFinish", testCreateFinish), + ("testCreateFinishWithPrefix", testCreateFinishWithPrefix), + ] +} + +extension FlatBuffersMonsterWriterTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersMonsterWriterTests = [ + ("testCreateMonster", testCreateMonster), + ("testCreateMonsterPrefixed", testCreateMonsterPrefixed), + ("testCreateMonsterResizedBuffer", testCreateMonsterResizedBuffer), + ("testData", testData), + ("testReadFromOtherLangagues", testReadFromOtherLangagues), + ] +} + +extension FlatBuffersStructsTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersStructsTests = [ + ("testCreatingEnums", testCreatingEnums), + ("testCreatingStruct", testCreatingStruct), + ("testCreatingVectorStruct", testCreatingVectorStruct), + ("testCreatingVectorStructWithForcedDefaults", testCreatingVectorStructWithForcedDefaults), + ("testReadingStruct", testReadingStruct), + ("testReadingStructWithEnums", testReadingStructWithEnums), + ] +} + +extension FlatBuffersTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersTests = [ + ("testCreate", testCreate), + ("testCreateFinish", testCreateFinish), + ("testCreateFinishWithPrefix", testCreateFinishWithPrefix), + ("testCreateString", testCreateString), + ("testEndian", testEndian), + ("testOffset", testOffset), + ("testReadCountry", testReadCountry), + ("testStartTable", testStartTable), + ] +} + +extension FlatBuffersUnionTests { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersUnionTests = [ + ("testCreateMonstor", testCreateMonstor), + ("testEndTableFinish", testEndTableFinish), + ("testEnumVector", testEnumVector), + ("testUnionVector", testUnionVector), + ] +} + +extension FlatBuffersVectors { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FlatBuffersVectors = [ + ("testCreateIntArray", testCreateIntArray), + ("testCreateSharedStringVector", testCreateSharedStringVector), + ("testCreateVectorOfStrings", testCreateVectorOfStrings), + ("testCreatingTwoCountries", testCreatingTwoCountries), + ("testReadDoubleArray", testReadDoubleArray), + ("testReadInt32Array", testReadInt32Array), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(FlatBuffersDoubleTests.__allTests__FlatBuffersDoubleTests), + testCase(FlatBuffersMonsterWriterTests.__allTests__FlatBuffersMonsterWriterTests), + testCase(FlatBuffersStructsTests.__allTests__FlatBuffersStructsTests), + testCase(FlatBuffersTests.__allTests__FlatBuffersTests), + testCase(FlatBuffersUnionTests.__allTests__FlatBuffersUnionTests), + testCase(FlatBuffersVectors.__allTests__FlatBuffersVectors), + ] +} +#endif diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift new file mode 100644 index 000000000..aa6675d71 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/monster_test_generated.swift @@ -0,0 +1,479 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import FlatBuffers + +public enum MyGame { +public enum Example { + +public enum Color: UInt8, Enum { + public typealias T = UInt8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: UInt8 { return self.rawValue } + case red = 1, green = 2, blue = 8 +} + +public enum Race: Int8, Enum { + public typealias T = Int8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: Int8 { return self.rawValue } + case none = -1, human = 0, dwarf = 1, elf = 2 +} + +public enum Any_: UInt8, Enum { + public typealias T = UInt8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: UInt8 { return self.rawValue } + case none = 0, monster = 1, testsimpletablewithenum = 2, mygame_example2_monster = 3 +} + +public enum AnyUniqueAliases: UInt8, Enum { + public typealias T = UInt8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: UInt8 { return self.rawValue } + case none = 0, m = 1, ts = 2, m2 = 3 +} + +public enum AnyAmbiguousAliases: UInt8, Enum { + public typealias T = UInt8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: UInt8 { return self.rawValue } + case none = 0, m1 = 1, m2 = 2, m3 = 3 +} + +public struct Test: Readable { + private var _accessor: Struct + public static var size = 4 + public static var alignment = 2 + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Struct(bb: bb, position: o) } + + public var a: Int16 { return _accessor.readBuffer(of: Int16.self, at: 0) } + public func mutate(a: Int16) -> Bool { return _accessor.mutate(a, index: 0) } + public var b: Int8 { return _accessor.readBuffer(of: Int8.self, at: 2) } + public func mutate(b: Int8) -> Bool { return _accessor.mutate(b, index: 2) } +} + +public struct Vec3: Readable { + private var _accessor: Struct + public static var size = 32 + public static var alignment = 8 + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Struct(bb: bb, position: o) } + + public var x: Float32 { return _accessor.readBuffer(of: Float32.self, at: 0) } + public func mutate(x: Float32) -> Bool { return _accessor.mutate(x, index: 0) } + public var y: Float32 { return _accessor.readBuffer(of: Float32.self, at: 4) } + public func mutate(y: Float32) -> Bool { return _accessor.mutate(y, index: 4) } + public var z: Float32 { return _accessor.readBuffer(of: Float32.self, at: 8) } + public func mutate(z: Float32) -> Bool { return _accessor.mutate(z, index: 8) } + public var test1: Double { return _accessor.readBuffer(of: Double.self, at: 16) } + public func mutate(test1: Double) -> Bool { return _accessor.mutate(test1, index: 16) } + public var test2: MyGame.Example.Color { return MyGame.Example.Color(rawValue: _accessor.readBuffer(of: UInt8.self, at: 24)) ?? MyGame.Example.Color.red } + public var test3: MyGame.Example.Test { return MyGame.Example.Test(_accessor.bb, o: _accessor.postion + 26) } +} + +public struct Ability: Readable { + private var _accessor: Struct + public static var size = 8 + public static var alignment = 4 + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Struct(bb: bb, position: o) } + + public var id: UInt32 { return _accessor.readBuffer(of: UInt32.self, at: 0) } + public func mutate(id: UInt32) -> Bool { return _accessor.mutate(id, index: 0) } + public var distance: UInt32 { return _accessor.readBuffer(of: UInt32.self, at: 4) } + public func mutate(distance: UInt32) -> Bool { return _accessor.mutate(distance, index: 4) } +} + +public static func createTest(a: Int16, b: Int8) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: Test.size, alignment: Test.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Test.size) + memory.storeBytes(of: a, toByteOffset: 0, as: Int16.self) + memory.storeBytes(of: b, toByteOffset: 2, as: Int8.self) + return memory +} + +public static func createVec3(x: Float32, y: Float32, z: Float32, test1: Double, test2: MyGame.Example.Color, test3a: Int16, test3b: Int8) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: Vec3.size, alignment: Vec3.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Vec3.size) + memory.storeBytes(of: x, toByteOffset: 0, as: Float32.self) + memory.storeBytes(of: y, toByteOffset: 4, as: Float32.self) + memory.storeBytes(of: z, toByteOffset: 8, as: Float32.self) + memory.storeBytes(of: test1, toByteOffset: 16, as: Double.self) + memory.storeBytes(of: test2.rawValue, toByteOffset: 24, as: UInt8.self) + memory.storeBytes(of: test3a, toByteOffset: 26, as: Int16.self) + memory.storeBytes(of: test3b, toByteOffset: 28, as: Int8.self) + return memory +} + +public static func createAbility(id: UInt32, distance: UInt32) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: Ability.size, alignment: Ability.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Ability.size) + memory.storeBytes(of: id, toByteOffset: 0, as: UInt32.self) + memory.storeBytes(of: distance, toByteOffset: 4, as: UInt32.self) + return memory +} + +} + +// MARK: - Example + + +public struct InParentNamespace: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsInParentNamespace(bb: ByteBuffer) -> InParentNamespace { return InParentNamespace(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public static func startInParentNamespace(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 0) } + public static func endInParentNamespace(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +public enum Example2 { + +public struct Monster: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsMonster(bb: ByteBuffer) -> Monster { return Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public static func startMonster(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 0) } + public static func endMonster(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +} + +// MARK: - Example2 + + +} +extension MyGame.Example { + +public struct TestSimpleTableWithEnum: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsTestSimpleTableWithEnum(bb: ByteBuffer) -> TestSimpleTableWithEnum { return TestSimpleTableWithEnum(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var color: MyGame.Example.Color { let o = _accessor.offset(4); return o == 0 ? MyGame.Example.Color.green : MyGame.Example.Color(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? MyGame.Example.Color.green } + public func mutate(color: MyGame.Example.Color) -> Bool {let o = _accessor.offset(4); return _accessor.mutate(color.rawValue, index: o) } + public static func startTestSimpleTableWithEnum(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + public static func add(color: MyGame.Example.Color, _ fbb: FlatBufferBuilder) { fbb.add(element: color.rawValue, def: 2, at: 0) } + public static func endTestSimpleTableWithEnum(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +public struct Stat: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsStat(bb: ByteBuffer) -> Stat { return Stat(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var id: String? { let o = _accessor.offset(4); return o == 0 ? nil : _accessor.string(at: o) } + public var idSegmentArray: [UInt8]? { return _accessor.getVector(at: 4) } + public var val: Int64 { let o = _accessor.offset(6); return o == 0 ? 0 : _accessor.readBuffer(of: Int64.self, at: o) } + public func mutate(val: Int64) -> Bool {let o = _accessor.offset(6); return _accessor.mutate(val, index: o) } + public var count: UInt16 { let o = _accessor.offset(8); return o == 0 ? 0 : _accessor.readBuffer(of: UInt16.self, at: o) } + public func mutate(count: UInt16) -> Bool {let o = _accessor.offset(8); return _accessor.mutate(count, index: o) } + public static func startStat(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 3) } + public static func add(id: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: id, at: 0) } + public static func add(val: Int64, _ fbb: FlatBufferBuilder) { fbb.add(element: val, def: 0, at: 1) } + public static func add(count: UInt16, _ fbb: FlatBufferBuilder) { fbb.add(element: count, def: 0, at: 2) } + public static func endStat(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +public struct Referrable: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsReferrable(bb: ByteBuffer) -> Referrable { return Referrable(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var id: UInt64 { let o = _accessor.offset(4); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(id: UInt64) -> Bool {let o = _accessor.offset(4); return _accessor.mutate(id, index: o) } + public static func startReferrable(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + public static func add(id: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: id, def: 0, at: 0) } + public static func endReferrable(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } + public static func sortVectorOfReferrable(offsets:[Offset], _ fbb: FlatBufferBuilder) -> Offset { + var off = offsets + off.sort { Table.compare(Table.offset(Int32($1.o), vOffset: 4, fbb: fbb.buffer), Table.offset(Int32($0.o), vOffset: 4, fbb: fbb.buffer), fbb: fbb.buffer) < 0 } + return fbb.createVector(ofOffsets: off) + } + fileprivate static func lookupByKey(vector: Int32, key: UInt64, fbb: ByteBuffer) -> Referrable? { + var span = fbb.read(def: Int32.self, position: Int(vector - 4)) + var start: Int32 = 0 + while span != 0 { + var middle = span / 2 + let tableOffset = Table.indirect(vector + 4 * (start + middle), fbb) + let comp = fbb.read(def: UInt64.self, position: Int(Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: 4, fbb: fbb))) + if comp > 0 { + span = middle + } else if comp < 0 { + middle += 1 + start += middle + span -= middle + } else { + return Referrable(fbb, o: tableOffset) + } + } + return nil + } +} + +public struct Monster: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsMonster(bb: ByteBuffer) -> Monster { return Monster(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var pos: MyGame.Example.Vec3? { let o = _accessor.offset(4); return o == 0 ? nil : MyGame.Example.Vec3(_accessor.bb, o: o + _accessor.postion) } + public var mana: Int16 { let o = _accessor.offset(6); return o == 0 ? 150 : _accessor.readBuffer(of: Int16.self, at: o) } + public func mutate(mana: Int16) -> Bool {let o = _accessor.offset(6); return _accessor.mutate(mana, index: o) } + public var hp: Int16 { let o = _accessor.offset(8); return o == 0 ? 100 : _accessor.readBuffer(of: Int16.self, at: o) } + public func mutate(hp: Int16) -> Bool {let o = _accessor.offset(8); return _accessor.mutate(hp, index: o) } + public var name: String? { let o = _accessor.offset(10); return o == 0 ? nil : _accessor.string(at: o) } + public var nameSegmentArray: [UInt8]? { return _accessor.getVector(at: 10) } + public var inventoryCount: Int32 { let o = _accessor.offset(14); return o == 0 ? 0 : _accessor.vector(count: o) } + public func inventory(at index: Int32) -> UInt8 { let o = _accessor.offset(14); return o == 0 ? 0 : _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1) } + public var inventory: [UInt8] { return _accessor.getVector(at: 14) ?? [] } + public func mutate(inventory: UInt8, at index: Int32) -> Bool { let o = _accessor.offset(14); return _accessor.directMutate(inventory, index: _accessor.vector(at: o) + index * 1) } + public var color: MyGame.Example.Color { let o = _accessor.offset(16); return o == 0 ? MyGame.Example.Color.blue : MyGame.Example.Color(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? MyGame.Example.Color.blue } + public func mutate(color: MyGame.Example.Color) -> Bool {let o = _accessor.offset(16); return _accessor.mutate(color.rawValue, index: o) } + public var testType: MyGame.Example.Any_ { let o = _accessor.offset(18); return o == 0 ? MyGame.Example.Any_.none : MyGame.Example.Any_(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? MyGame.Example.Any_.none } + public func test(type: T.Type) -> T? { let o = _accessor.offset(20); return o == 0 ? nil : _accessor.union(o) } + public var test4Count: Int32 { let o = _accessor.offset(22); return o == 0 ? 0 : _accessor.vector(count: o) } + public func test4(at index: Int32) -> MyGame.Example.Test? { let o = _accessor.offset(22); return o == 0 ? nil : MyGame.Example.Test(_accessor.bb, o: _accessor.vector(at: o) + index * 4) } + public var testarrayofstringCount: Int32 { let o = _accessor.offset(24); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testarrayofstring(at index: Int32) -> String? { let o = _accessor.offset(24); return o == 0 ? nil : _accessor.directString(at: _accessor.vector(at: o) + index * 4) } + public var testarrayoftablesCount: Int32 { let o = _accessor.offset(26); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testarrayoftables(at index: Int32) -> MyGame.Example.Monster? { let o = _accessor.offset(26); return o == 0 ? nil : MyGame.Example.Monster(_accessor.bb, o: _accessor.indirect(_accessor.vector(at: o) + index * 4)) } + public func testarrayoftablesBy(key: String) -> MyGame.Example.Monster? { let o = _accessor.offset(26); return o == 0 ? nil : MyGame.Example.Monster.lookupByKey(vector: _accessor.vector(at: o), key: key, fbb: _accessor.bb) } + public var enemy: MyGame.Example.Monster? { let o = _accessor.offset(28); return o == 0 ? nil : MyGame.Example.Monster(_accessor.bb, o: _accessor.indirect(o + _accessor.postion)) } + public var testnestedflatbufferCount: Int32 { let o = _accessor.offset(30); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testnestedflatbuffer(at index: Int32) -> UInt8 { let o = _accessor.offset(30); return o == 0 ? 0 : _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1) } + public var testnestedflatbuffer: [UInt8] { return _accessor.getVector(at: 30) ?? [] } + public func mutate(testnestedflatbuffer: UInt8, at index: Int32) -> Bool { let o = _accessor.offset(30); return _accessor.directMutate(testnestedflatbuffer, index: _accessor.vector(at: o) + index * 1) } + public var testempty: MyGame.Example.Stat? { let o = _accessor.offset(32); return o == 0 ? nil : MyGame.Example.Stat(_accessor.bb, o: _accessor.indirect(o + _accessor.postion)) } + public var testbool: Bool { let o = _accessor.offset(34); return o == 0 ? false : 0 != _accessor.readBuffer(of: Byte.self, at: o) } + public func mutate(testbool: Byte) -> Bool {let o = _accessor.offset(34); return _accessor.mutate(testbool, index: o) } + public var testhashs32Fnv1: Int32 { let o = _accessor.offset(36); return o == 0 ? 0 : _accessor.readBuffer(of: Int32.self, at: o) } + public func mutate(testhashs32Fnv1: Int32) -> Bool {let o = _accessor.offset(36); return _accessor.mutate(testhashs32Fnv1, index: o) } + public var testhashu32Fnv1: UInt32 { let o = _accessor.offset(38); return o == 0 ? 0 : _accessor.readBuffer(of: UInt32.self, at: o) } + public func mutate(testhashu32Fnv1: UInt32) -> Bool {let o = _accessor.offset(38); return _accessor.mutate(testhashu32Fnv1, index: o) } + public var testhashs64Fnv1: Int64 { let o = _accessor.offset(40); return o == 0 ? 0 : _accessor.readBuffer(of: Int64.self, at: o) } + public func mutate(testhashs64Fnv1: Int64) -> Bool {let o = _accessor.offset(40); return _accessor.mutate(testhashs64Fnv1, index: o) } + public var testhashu64Fnv1: UInt64 { let o = _accessor.offset(42); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(testhashu64Fnv1: UInt64) -> Bool {let o = _accessor.offset(42); return _accessor.mutate(testhashu64Fnv1, index: o) } + public var testhashs32Fnv1a: Int32 { let o = _accessor.offset(44); return o == 0 ? 0 : _accessor.readBuffer(of: Int32.self, at: o) } + public func mutate(testhashs32Fnv1a: Int32) -> Bool {let o = _accessor.offset(44); return _accessor.mutate(testhashs32Fnv1a, index: o) } + public var testhashu32Fnv1a: UInt32 { let o = _accessor.offset(46); return o == 0 ? 0 : _accessor.readBuffer(of: UInt32.self, at: o) } + public func mutate(testhashu32Fnv1a: UInt32) -> Bool {let o = _accessor.offset(46); return _accessor.mutate(testhashu32Fnv1a, index: o) } + public var testhashs64Fnv1a: Int64 { let o = _accessor.offset(48); return o == 0 ? 0 : _accessor.readBuffer(of: Int64.self, at: o) } + public func mutate(testhashs64Fnv1a: Int64) -> Bool {let o = _accessor.offset(48); return _accessor.mutate(testhashs64Fnv1a, index: o) } + public var testhashu64Fnv1a: UInt64 { let o = _accessor.offset(50); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(testhashu64Fnv1a: UInt64) -> Bool {let o = _accessor.offset(50); return _accessor.mutate(testhashu64Fnv1a, index: o) } + public var testarrayofboolsCount: Int32 { let o = _accessor.offset(52); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testarrayofbools(at index: Int32) -> Bool { let o = _accessor.offset(52); return o == 0 ? true : 0 != _accessor.directRead(of: Byte.self, offset: _accessor.vector(at: o) + index * 1) } + public var testarrayofbools: [Byte] { return _accessor.getVector(at: 52) ?? [] } + public func mutate(testarrayofbools: Byte, at index: Int32) -> Bool { let o = _accessor.offset(52); return _accessor.directMutate(testarrayofbools, index: _accessor.vector(at: o) + index * 1) } + public var testf: Float32 { let o = _accessor.offset(54); return o == 0 ? 3.14159 : _accessor.readBuffer(of: Float32.self, at: o) } + public func mutate(testf: Float32) -> Bool {let o = _accessor.offset(54); return _accessor.mutate(testf, index: o) } + public var testf2: Float32 { let o = _accessor.offset(56); return o == 0 ? 3.0 : _accessor.readBuffer(of: Float32.self, at: o) } + public func mutate(testf2: Float32) -> Bool {let o = _accessor.offset(56); return _accessor.mutate(testf2, index: o) } + public var testf3: Float32 { let o = _accessor.offset(58); return o == 0 ? 0.0 : _accessor.readBuffer(of: Float32.self, at: o) } + public func mutate(testf3: Float32) -> Bool {let o = _accessor.offset(58); return _accessor.mutate(testf3, index: o) } + public var testarrayofstring2Count: Int32 { let o = _accessor.offset(60); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testarrayofstring2(at index: Int32) -> String? { let o = _accessor.offset(60); return o == 0 ? nil : _accessor.directString(at: _accessor.vector(at: o) + index * 4) } + public var testarrayofsortedstructCount: Int32 { let o = _accessor.offset(62); return o == 0 ? 0 : _accessor.vector(count: o) } + public func testarrayofsortedstruct(at index: Int32) -> MyGame.Example.Ability? { let o = _accessor.offset(62); return o == 0 ? nil : MyGame.Example.Ability(_accessor.bb, o: _accessor.vector(at: o) + index * 8) } + public var flexCount: Int32 { let o = _accessor.offset(64); return o == 0 ? 0 : _accessor.vector(count: o) } + public func flex(at index: Int32) -> UInt8 { let o = _accessor.offset(64); return o == 0 ? 0 : _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1) } + public var flex: [UInt8] { return _accessor.getVector(at: 64) ?? [] } + public func mutate(flex: UInt8, at index: Int32) -> Bool { let o = _accessor.offset(64); return _accessor.directMutate(flex, index: _accessor.vector(at: o) + index * 1) } + public var test5Count: Int32 { let o = _accessor.offset(66); return o == 0 ? 0 : _accessor.vector(count: o) } + public func test5(at index: Int32) -> MyGame.Example.Test? { let o = _accessor.offset(66); return o == 0 ? nil : MyGame.Example.Test(_accessor.bb, o: _accessor.vector(at: o) + index * 4) } + public var vectorOfLongsCount: Int32 { let o = _accessor.offset(68); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfLongs(at index: Int32) -> Int64 { let o = _accessor.offset(68); return o == 0 ? 0 : _accessor.directRead(of: Int64.self, offset: _accessor.vector(at: o) + index * 8) } + public var vectorOfLongs: [Int64] { return _accessor.getVector(at: 68) ?? [] } + public func mutate(vectorOfLongs: Int64, at index: Int32) -> Bool { let o = _accessor.offset(68); return _accessor.directMutate(vectorOfLongs, index: _accessor.vector(at: o) + index * 8) } + public var vectorOfDoublesCount: Int32 { let o = _accessor.offset(70); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfDoubles(at index: Int32) -> Double { let o = _accessor.offset(70); return o == 0 ? 0 : _accessor.directRead(of: Double.self, offset: _accessor.vector(at: o) + index * 8) } + public var vectorOfDoubles: [Double] { return _accessor.getVector(at: 70) ?? [] } + public func mutate(vectorOfDoubles: Double, at index: Int32) -> Bool { let o = _accessor.offset(70); return _accessor.directMutate(vectorOfDoubles, index: _accessor.vector(at: o) + index * 8) } + public var parentNamespaceTest: MyGame.InParentNamespace? { let o = _accessor.offset(72); return o == 0 ? nil : MyGame.InParentNamespace(_accessor.bb, o: _accessor.indirect(o + _accessor.postion)) } + public var vectorOfReferrablesCount: Int32 { let o = _accessor.offset(74); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfReferrables(at index: Int32) -> MyGame.Example.Referrable? { let o = _accessor.offset(74); return o == 0 ? nil : MyGame.Example.Referrable(_accessor.bb, o: _accessor.indirect(_accessor.vector(at: o) + index * 4)) } + public func vectorOfReferrablesBy(key: UInt64) -> MyGame.Example.Referrable? { let o = _accessor.offset(74); return o == 0 ? nil : MyGame.Example.Referrable.lookupByKey(vector: _accessor.vector(at: o), key: key, fbb: _accessor.bb) } + public var singleWeakReference: UInt64 { let o = _accessor.offset(76); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(singleWeakReference: UInt64) -> Bool {let o = _accessor.offset(76); return _accessor.mutate(singleWeakReference, index: o) } + public var vectorOfWeakReferencesCount: Int32 { let o = _accessor.offset(78); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfWeakReferences(at index: Int32) -> UInt64 { let o = _accessor.offset(78); return o == 0 ? 0 : _accessor.directRead(of: UInt64.self, offset: _accessor.vector(at: o) + index * 8) } + public var vectorOfWeakReferences: [UInt64] { return _accessor.getVector(at: 78) ?? [] } + public func mutate(vectorOfWeakReferences: UInt64, at index: Int32) -> Bool { let o = _accessor.offset(78); return _accessor.directMutate(vectorOfWeakReferences, index: _accessor.vector(at: o) + index * 8) } + public var vectorOfStrongReferrablesCount: Int32 { let o = _accessor.offset(80); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfStrongReferrables(at index: Int32) -> MyGame.Example.Referrable? { let o = _accessor.offset(80); return o == 0 ? nil : MyGame.Example.Referrable(_accessor.bb, o: _accessor.indirect(_accessor.vector(at: o) + index * 4)) } + public func vectorOfStrongReferrablesBy(key: UInt64) -> MyGame.Example.Referrable? { let o = _accessor.offset(80); return o == 0 ? nil : MyGame.Example.Referrable.lookupByKey(vector: _accessor.vector(at: o), key: key, fbb: _accessor.bb) } + public var coOwningReference: UInt64 { let o = _accessor.offset(82); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(coOwningReference: UInt64) -> Bool {let o = _accessor.offset(82); return _accessor.mutate(coOwningReference, index: o) } + public var vectorOfCoOwningReferencesCount: Int32 { let o = _accessor.offset(84); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfCoOwningReferences(at index: Int32) -> UInt64 { let o = _accessor.offset(84); return o == 0 ? 0 : _accessor.directRead(of: UInt64.self, offset: _accessor.vector(at: o) + index * 8) } + public var vectorOfCoOwningReferences: [UInt64] { return _accessor.getVector(at: 84) ?? [] } + public func mutate(vectorOfCoOwningReferences: UInt64, at index: Int32) -> Bool { let o = _accessor.offset(84); return _accessor.directMutate(vectorOfCoOwningReferences, index: _accessor.vector(at: o) + index * 8) } + public var nonOwningReference: UInt64 { let o = _accessor.offset(86); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(nonOwningReference: UInt64) -> Bool {let o = _accessor.offset(86); return _accessor.mutate(nonOwningReference, index: o) } + public var vectorOfNonOwningReferencesCount: Int32 { let o = _accessor.offset(88); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfNonOwningReferences(at index: Int32) -> UInt64 { let o = _accessor.offset(88); return o == 0 ? 0 : _accessor.directRead(of: UInt64.self, offset: _accessor.vector(at: o) + index * 8) } + public var vectorOfNonOwningReferences: [UInt64] { return _accessor.getVector(at: 88) ?? [] } + public func mutate(vectorOfNonOwningReferences: UInt64, at index: Int32) -> Bool { let o = _accessor.offset(88); return _accessor.directMutate(vectorOfNonOwningReferences, index: _accessor.vector(at: o) + index * 8) } + public var anyUniqueType: MyGame.Example.AnyUniqueAliases { let o = _accessor.offset(90); return o == 0 ? MyGame.Example.AnyUniqueAliases.none : MyGame.Example.AnyUniqueAliases(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? MyGame.Example.AnyUniqueAliases.none } + public func anyUnique(type: T.Type) -> T? { let o = _accessor.offset(92); return o == 0 ? nil : _accessor.union(o) } + public var anyAmbiguousType: MyGame.Example.AnyAmbiguousAliases { let o = _accessor.offset(94); return o == 0 ? MyGame.Example.AnyAmbiguousAliases.none : MyGame.Example.AnyAmbiguousAliases(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? MyGame.Example.AnyAmbiguousAliases.none } + public func anyAmbiguous(type: T.Type) -> T? { let o = _accessor.offset(96); return o == 0 ? nil : _accessor.union(o) } + public var vectorOfEnumsCount: Int32 { let o = _accessor.offset(98); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vectorOfEnums(at index: Int32) -> MyGame.Example.Color? { let o = _accessor.offset(98); return o == 0 ? MyGame.Example.Color.red : MyGame.Example.Color(rawValue: _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1)) } + public var signedEnum: MyGame.Example.Race { let o = _accessor.offset(100); return o == 0 ? MyGame.Example.Race.none : MyGame.Example.Race(rawValue: _accessor.readBuffer(of: Int8.self, at: o)) ?? MyGame.Example.Race.none } + public func mutate(signedEnum: MyGame.Example.Race) -> Bool {let o = _accessor.offset(100); return _accessor.mutate(signedEnum.rawValue, index: o) } + public static func startMonster(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 49) } + public static func add(pos: Offset, _ fbb: FlatBufferBuilder) { fbb.add(structOffset: 0) } + public static func add(mana: Int16, _ fbb: FlatBufferBuilder) { fbb.add(element: mana, def: 150, at: 1) } + public static func add(hp: Int16, _ fbb: FlatBufferBuilder) { fbb.add(element: hp, def: 100, at: 2) } + public static func add(name: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: name, at: 3) } + public static func addVectorOf(inventory: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: inventory, at: 5) } + public static func add(color: MyGame.Example.Color, _ fbb: FlatBufferBuilder) { fbb.add(element: color.rawValue, def: 8, at: 6) } + public static func add(testType: MyGame.Example.Any_, _ fbb: FlatBufferBuilder) { fbb.add(element: testType.rawValue, def: 0, at: 7) } + public static func add(test: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: test, at: 8) } + public static func addVectorOf(test4: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: test4, at: 9) } + public static func addVectorOf(testarrayofstring: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testarrayofstring, at: 10) } + public static func addVectorOf(testarrayoftables: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testarrayoftables, at: 11) } + public static func add(enemy: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: enemy, at: 12) } + public static func addVectorOf(testnestedflatbuffer: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testnestedflatbuffer, at: 13) } + public static func add(testempty: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testempty, at: 14) } + public static func add(testbool: Bool, _ fbb: FlatBufferBuilder) { fbb.add(condition: testbool, def: false, at: 15) } + public static func add(testhashs32Fnv1: Int32, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashs32Fnv1, def: 0, at: 16) } + public static func add(testhashu32Fnv1: UInt32, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashu32Fnv1, def: 0, at: 17) } + public static func add(testhashs64Fnv1: Int64, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashs64Fnv1, def: 0, at: 18) } + public static func add(testhashu64Fnv1: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashu64Fnv1, def: 0, at: 19) } + public static func add(testhashs32Fnv1a: Int32, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashs32Fnv1a, def: 0, at: 20) } + public static func add(testhashu32Fnv1a: UInt32, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashu32Fnv1a, def: 0, at: 21) } + public static func add(testhashs64Fnv1a: Int64, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashs64Fnv1a, def: 0, at: 22) } + public static func add(testhashu64Fnv1a: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: testhashu64Fnv1a, def: 0, at: 23) } + public static func addVectorOf(testarrayofbools: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testarrayofbools, at: 24) } + public static func add(testf: Float32, _ fbb: FlatBufferBuilder) { fbb.add(element: testf, def: 3.14159, at: 25) } + public static func add(testf2: Float32, _ fbb: FlatBufferBuilder) { fbb.add(element: testf2, def: 3.0, at: 26) } + public static func add(testf3: Float32, _ fbb: FlatBufferBuilder) { fbb.add(element: testf3, def: 0.0, at: 27) } + public static func addVectorOf(testarrayofstring2: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testarrayofstring2, at: 28) } + public static func addVectorOf(testarrayofsortedstruct: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: testarrayofsortedstruct, at: 29) } + public static func addVectorOf(flex: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: flex, at: 30) } + public static func addVectorOf(test5: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: test5, at: 31) } + public static func addVectorOf(vectorOfLongs: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfLongs, at: 32) } + public static func addVectorOf(vectorOfDoubles: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfDoubles, at: 33) } + public static func add(parentNamespaceTest: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: parentNamespaceTest, at: 34) } + public static func addVectorOf(vectorOfReferrables: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfReferrables, at: 35) } + public static func add(singleWeakReference: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: singleWeakReference, def: 0, at: 36) } + public static func addVectorOf(vectorOfWeakReferences: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfWeakReferences, at: 37) } + public static func addVectorOf(vectorOfStrongReferrables: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfStrongReferrables, at: 38) } + public static func add(coOwningReference: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: coOwningReference, def: 0, at: 39) } + public static func addVectorOf(vectorOfCoOwningReferences: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfCoOwningReferences, at: 40) } + public static func add(nonOwningReference: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: nonOwningReference, def: 0, at: 41) } + public static func addVectorOf(vectorOfNonOwningReferences: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfNonOwningReferences, at: 42) } + public static func add(anyUniqueType: MyGame.Example.AnyUniqueAliases, _ fbb: FlatBufferBuilder) { fbb.add(element: anyUniqueType.rawValue, def: 0, at: 43) } + public static func add(anyUnique: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: anyUnique, at: 44) } + public static func add(anyAmbiguousType: MyGame.Example.AnyAmbiguousAliases, _ fbb: FlatBufferBuilder) { fbb.add(element: anyAmbiguousType.rawValue, def: 0, at: 45) } + public static func add(anyAmbiguous: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: anyAmbiguous, at: 46) } + public static func addVectorOf(vectorOfEnums: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vectorOfEnums, at: 47) } + public static func add(signedEnum: MyGame.Example.Race, _ fbb: FlatBufferBuilder) { fbb.add(element: signedEnum.rawValue, def: -1, at: 48) } + public static func endMonster(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); fbb.require(table: end, fields: [10]); return end } + public static func sortVectorOfMonster(offsets:[Offset], _ fbb: FlatBufferBuilder) -> Offset { + var off = offsets + off.sort { Table.compare(Table.offset(Int32($1.o), vOffset: 10, fbb: fbb.buffer), Table.offset(Int32($0.o), vOffset: 10, fbb: fbb.buffer), fbb: fbb.buffer) < 0 } + return fbb.createVector(ofOffsets: off) + } + fileprivate static func lookupByKey(vector: Int32, key: String, fbb: ByteBuffer) -> Monster? { + let key = key.utf8.map { $0 } + var span = fbb.read(def: Int32.self, position: Int(vector - 4)) + var start: Int32 = 0 + while span != 0 { + var middle = span / 2 + let tableOffset = Table.indirect(vector + 4 * (start + middle), fbb) + let comp = Table.compare(Table.offset(Int32(fbb.capacity) - tableOffset, vOffset: 10, fbb: fbb), key, fbb: fbb) + if comp > 0 { + span = middle + } else if comp < 0 { + middle += 1 + start += middle + span -= middle + } else { + return Monster(fbb, o: tableOffset) + } + } + return nil + } +} + +public struct TypeAliases: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MONS", addPrefix: prefix) } + public static func getRootAsTypeAliases(bb: ByteBuffer) -> TypeAliases { return TypeAliases(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var i8: Int8 { let o = _accessor.offset(4); return o == 0 ? 0 : _accessor.readBuffer(of: Int8.self, at: o) } + public func mutate(i8: Int8) -> Bool {let o = _accessor.offset(4); return _accessor.mutate(i8, index: o) } + public var u8: UInt8 { let o = _accessor.offset(6); return o == 0 ? 0 : _accessor.readBuffer(of: UInt8.self, at: o) } + public func mutate(u8: UInt8) -> Bool {let o = _accessor.offset(6); return _accessor.mutate(u8, index: o) } + public var i16: Int16 { let o = _accessor.offset(8); return o == 0 ? 0 : _accessor.readBuffer(of: Int16.self, at: o) } + public func mutate(i16: Int16) -> Bool {let o = _accessor.offset(8); return _accessor.mutate(i16, index: o) } + public var u16: UInt16 { let o = _accessor.offset(10); return o == 0 ? 0 : _accessor.readBuffer(of: UInt16.self, at: o) } + public func mutate(u16: UInt16) -> Bool {let o = _accessor.offset(10); return _accessor.mutate(u16, index: o) } + public var i32: Int32 { let o = _accessor.offset(12); return o == 0 ? 0 : _accessor.readBuffer(of: Int32.self, at: o) } + public func mutate(i32: Int32) -> Bool {let o = _accessor.offset(12); return _accessor.mutate(i32, index: o) } + public var u32: UInt32 { let o = _accessor.offset(14); return o == 0 ? 0 : _accessor.readBuffer(of: UInt32.self, at: o) } + public func mutate(u32: UInt32) -> Bool {let o = _accessor.offset(14); return _accessor.mutate(u32, index: o) } + public var i64: Int64 { let o = _accessor.offset(16); return o == 0 ? 0 : _accessor.readBuffer(of: Int64.self, at: o) } + public func mutate(i64: Int64) -> Bool {let o = _accessor.offset(16); return _accessor.mutate(i64, index: o) } + public var u64: UInt64 { let o = _accessor.offset(18); return o == 0 ? 0 : _accessor.readBuffer(of: UInt64.self, at: o) } + public func mutate(u64: UInt64) -> Bool {let o = _accessor.offset(18); return _accessor.mutate(u64, index: o) } + public var f32: Float32 { let o = _accessor.offset(20); return o == 0 ? 0.0 : _accessor.readBuffer(of: Float32.self, at: o) } + public func mutate(f32: Float32) -> Bool {let o = _accessor.offset(20); return _accessor.mutate(f32, index: o) } + public var f64: Double { let o = _accessor.offset(22); return o == 0 ? 0.0 : _accessor.readBuffer(of: Double.self, at: o) } + public func mutate(f64: Double) -> Bool {let o = _accessor.offset(22); return _accessor.mutate(f64, index: o) } + public var v8Count: Int32 { let o = _accessor.offset(24); return o == 0 ? 0 : _accessor.vector(count: o) } + public func v8(at index: Int32) -> Int8 { let o = _accessor.offset(24); return o == 0 ? 0 : _accessor.directRead(of: Int8.self, offset: _accessor.vector(at: o) + index * 1) } + public var v8: [Int8] { return _accessor.getVector(at: 24) ?? [] } + public func mutate(v8: Int8, at index: Int32) -> Bool { let o = _accessor.offset(24); return _accessor.directMutate(v8, index: _accessor.vector(at: o) + index * 1) } + public var vf64Count: Int32 { let o = _accessor.offset(26); return o == 0 ? 0 : _accessor.vector(count: o) } + public func vf64(at index: Int32) -> Double { let o = _accessor.offset(26); return o == 0 ? 0 : _accessor.directRead(of: Double.self, offset: _accessor.vector(at: o) + index * 8) } + public var vf64: [Double] { return _accessor.getVector(at: 26) ?? [] } + public func mutate(vf64: Double, at index: Int32) -> Bool { let o = _accessor.offset(26); return _accessor.directMutate(vf64, index: _accessor.vector(at: o) + index * 8) } + public static func startTypeAliases(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 12) } + public static func add(i8: Int8, _ fbb: FlatBufferBuilder) { fbb.add(element: i8, def: 0, at: 0) } + public static func add(u8: UInt8, _ fbb: FlatBufferBuilder) { fbb.add(element: u8, def: 0, at: 1) } + public static func add(i16: Int16, _ fbb: FlatBufferBuilder) { fbb.add(element: i16, def: 0, at: 2) } + public static func add(u16: UInt16, _ fbb: FlatBufferBuilder) { fbb.add(element: u16, def: 0, at: 3) } + public static func add(i32: Int32, _ fbb: FlatBufferBuilder) { fbb.add(element: i32, def: 0, at: 4) } + public static func add(u32: UInt32, _ fbb: FlatBufferBuilder) { fbb.add(element: u32, def: 0, at: 5) } + public static func add(i64: Int64, _ fbb: FlatBufferBuilder) { fbb.add(element: i64, def: 0, at: 6) } + public static func add(u64: UInt64, _ fbb: FlatBufferBuilder) { fbb.add(element: u64, def: 0, at: 7) } + public static func add(f32: Float32, _ fbb: FlatBufferBuilder) { fbb.add(element: f32, def: 0.0, at: 8) } + public static func add(f64: Double, _ fbb: FlatBufferBuilder) { fbb.add(element: f64, def: 0.0, at: 9) } + public static func addVectorOf(v8: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: v8, at: 10) } + public static func addVectorOf(vf64: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: vf64, at: 11) } + public static func endTypeAliases(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +} + +// MARK: - Example + + +// MARK: - MyGame + + diff --git a/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift new file mode 100644 index 000000000..c1c6aca98 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/FlatBuffers.Test.SwiftTests/union_vector_generated.swift @@ -0,0 +1,82 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +import FlatBuffers + +public enum Character: UInt8, Enum { + public typealias T = UInt8 + public static var byteSize: Int { return MemoryLayout.size } + public var value: UInt8 { return self.rawValue } + case none = 0, mulan = 1, rapunzel = 2, belle = 3, bookfan = 4, other = 5, unused = 6 +} + +public struct Rapunzel: Readable { + private var _accessor: Struct + public static var size = 4 + public static var alignment = 4 + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Struct(bb: bb, position: o) } + + public var hairLength: Int32 { return _accessor.readBuffer(of: Int32.self, at: 0) } + public func mutate(hairLength: Int32) -> Bool { return _accessor.mutate(hairLength, index: 0) } +} + +public struct BookReader: Readable { + private var _accessor: Struct + public static var size = 4 + public static var alignment = 4 + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Struct(bb: bb, position: o) } + + public var booksRead: Int32 { return _accessor.readBuffer(of: Int32.self, at: 0) } + public func mutate(booksRead: Int32) -> Bool { return _accessor.mutate(booksRead, index: 0) } +} + +public func createRapunzel(hairLength: Int32) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: Rapunzel.size, alignment: Rapunzel.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: Rapunzel.size) + memory.storeBytes(of: hairLength, toByteOffset: 0, as: Int32.self) + return memory +} + +public func createBookReader(booksRead: Int32) -> UnsafeMutableRawPointer { + let memory = UnsafeMutableRawPointer.allocate(byteCount: BookReader.size, alignment: BookReader.alignment) + memory.initializeMemory(as: UInt8.self, repeating: 0, count: BookReader.size) + memory.storeBytes(of: booksRead, toByteOffset: 0, as: Int32.self) + return memory +} + +public struct Attacker: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MOVI", addPrefix: prefix) } + public static func getRootAsAttacker(bb: ByteBuffer) -> Attacker { return Attacker(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var swordAttackDamage: Int32 { let o = _accessor.offset(4); return o == 0 ? 0 : _accessor.readBuffer(of: Int32.self, at: o) } + public func mutate(swordAttackDamage: Int32) -> Bool {let o = _accessor.offset(4); return _accessor.mutate(swordAttackDamage, index: o) } + public static func startAttacker(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 1) } + public static func add(swordAttackDamage: Int32, _ fbb: FlatBufferBuilder) { fbb.add(element: swordAttackDamage, def: 0, at: 0) } + public static func endAttacker(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + +public struct Movie: FlatBufferObject { + private var _accessor: Table + public static func finish(_ fbb: FlatBufferBuilder, end: Offset, prefix: Bool = false) { fbb.finish(offset: end, fileId: "MOVI", addPrefix: prefix) } + public static func getRootAsMovie(bb: ByteBuffer) -> Movie { return Movie(Table(bb: bb, position: Int32(bb.read(def: UOffset.self, position: bb.reader)) + Int32(bb.reader))) } + + private init(_ t: Table) { _accessor = t } + public init(_ bb: ByteBuffer, o: Int32) { _accessor = Table(bb: bb, position: o) } + + public var mainCharacterType: Character { let o = _accessor.offset(4); return o == 0 ? Character.none : Character(rawValue: _accessor.readBuffer(of: UInt8.self, at: o)) ?? Character.none } + public func mainCharacter(type: T.Type) -> T? { let o = _accessor.offset(6); return o == 0 ? nil : _accessor.union(o) } + public var charactersTypeCount: Int32 { let o = _accessor.offset(8); return o == 0 ? 0 : _accessor.vector(count: o) } + public func charactersType(at index: Int32) -> Character? { let o = _accessor.offset(8); return o == 0 ? Character.none : Character(rawValue: _accessor.directRead(of: UInt8.self, offset: _accessor.vector(at: o) + index * 1)) } + public var charactersCount: Int32 { let o = _accessor.offset(10); return o == 0 ? 0 : _accessor.vector(count: o) } + public func characters(at index: Int32, type: T.Type) -> T? { let o = _accessor.offset(10); return o == 0 ? nil : _accessor.directUnion(_accessor.vector(at: o) + index * 4) } + public static func startMovie(_ fbb: FlatBufferBuilder) -> UOffset { fbb.startTable(with: 4) } + public static func add(mainCharacterType: Character, _ fbb: FlatBufferBuilder) { fbb.add(element: mainCharacterType.rawValue, def: 0, at: 0) } + public static func add(mainCharacter: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: mainCharacter, at: 1) } + public static func addVectorOf(charactersType: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: charactersType, at: 2) } + public static func addVectorOf(characters: Offset, _ fbb: FlatBufferBuilder) { fbb.add(offset: characters, at: 3) } + public static func endMovie(_ fbb: FlatBufferBuilder, start: UOffset) -> Offset { let end = Offset(offset: fbb.endTable(at: start)); return end } +} + diff --git a/tests/FlatBuffers.Test.Swift/Tests/LinuxMain.swift b/tests/FlatBuffers.Test.Swift/Tests/LinuxMain.swift new file mode 100644 index 000000000..1b16a78e7 --- /dev/null +++ b/tests/FlatBuffers.Test.Swift/Tests/LinuxMain.swift @@ -0,0 +1,8 @@ +import XCTest + +import FlatBuffers_Test_SwiftTests + +var tests = [XCTestCaseEntry]() +tests += FlatBuffers_Test_SwiftTests.__allTests() + +XCTMain(tests) diff --git a/tests/FlatBuffers.Test.Swift/monsterdata_test.mon b/tests/FlatBuffers.Test.Swift/monsterdata_test.mon new file mode 100644 index 000000000..ba6cf2786 Binary files /dev/null and b/tests/FlatBuffers.Test.Swift/monsterdata_test.mon differ diff --git a/tests/TestAll.sh b/tests/TestAll.sh index 0fc0acdbc..e20431c3b 100644 --- a/tests/TestAll.sh +++ b/tests/TestAll.sh @@ -60,4 +60,6 @@ echo "(in a different repo)" echo "************************ Swift:" -echo "(in a different repo)" +cd FlatBuffers.Test.Swift +sh SwiftTest.sh +cd .. \ No newline at end of file diff --git a/tests/docker/languages/Dockerfile.testing.swift_5_1 b/tests/docker/languages/Dockerfile.testing.swift_5_1 new file mode 100644 index 000000000..e98e4be1f --- /dev/null +++ b/tests/docker/languages/Dockerfile.testing.swift_5_1 @@ -0,0 +1,8 @@ +FROM swift:5.1 +WORKDIR /code +ADD . . +RUN cp flatc_debian_stretch flatc +WORKDIR /code/tests +RUN swift --version +WORKDIR /code/tests/FlatBuffers.Test.Swift +RUN sh SwiftTest.sh