-
Notifications
You must be signed in to change notification settings - Fork 40
Code Generation
In order to have a great coding experience I decided to write a code generator which will leverage the OOP concepts baked in Swift language. I paid attention to performance but can not claim being as efficient as the C or C++ implementation of FlatBuffers. However even with the nicer API Swift implementation is completely compatible with other implementations, as it generates and reads compatible byte arrays.
In the root folder of FlatBuffersSwift you can find fbsCG.jar
file. Which is the FlatBufferSchema code generator based on the FlatBuffersSchemaEditor.
Example folder contains an example fbs file and a generated swift file. If you want to generate it again please execute following command in the root folder of the project.
java -jar fbsCG.jar -fbs Example/contacts.fbs -out Example/contactList.swift -lang swift
This implies that you have Java 8 installed.
For the sake of simplicity I would like to introduce following FlatBuffers schema:
table List {
people : [Person];
}
table Person {
firstName : string;
lastName : string;
}
root_type List;
which results in following Public API:
final public class List {
public var people: [Person?]
public init()
public init(people: [Person?])
}
extension List {
public class func fromByteArray(data: UnsafePointer<UInt8>) -> List
}
extension List {
public var toByteArray: [UInt8] { get }
}
extension List {
final public class LazyAccess {
public init(data: UnsafePointer<UInt8>)
lazy public var people: LazyVector<Person.LazyAccess>
lazy public var createEagerVersion: List?
}
}
final public class Person {
public var firstName: String?
public var lastName: String?
public init()
public init(firstName: String?, lastName: String?)
}
extension Person {
final public class LazyAccess {
lazy public var firstName: String?
lazy public var lastName: String?
lazy public var createEagerVersion: Person?
}
}
Lets start with the Person
class.
The main class declaration reflects the fields of the corresponding Table and provide us with two initialisers.
In extension we define another class Person.LazyAccess
which also reflects the fields but define them as lazy getters. It doesn't provide us with an initialiser, however it has another lazy getter which returns an instance of Person
class, not the Person.LazyAccess
class.
The List
class has the same API accept it's lazy accessor class List.LazyAccess
has a public initialiser. And there are two additional extensions which define toByteArray
and fromByteArray
methods.
Those differences are due to the fact that List is marked as root_type
in the FlatBuffersSchema.
This convenient API allows me to construct FlatBuffers in most natural way for a OO language
let p1 = Person(firstName: "Maxim", lastName: "Zaks")
let p2 = Person(firstName: "Alex", lastName: "Zaks")
let list = List(people: [p1, p2])
let fbData = list.toByteArray
And there are two ways how I can read the values out of the byte array.
let lazyList = List.LazyAccess(data: UnsafePointer(fbData))
let name = lazyList.people[0]?.firstName
here we use List.LazyAccess
class to lazily access the data. We read and create objects only when we access the value. This way a cost for accessing the data inside of a FlatBuffers is almost zero. However LazyAccess classes are intended for read access only.
let eagerList = List.fromByteArray(UnsafePointer(fbData))
let name2 = eagerList.people[0]?.firstName
The API look almost the same, however this time on the first line we created all the objects at the spot. Same as calling NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)
, however profiling my small performance test shows that eager instantiation of FlattBuffer objects is around two times faster than parsing a JSON of equivalent data.
Why would I introduce eager instantiation in the first place?
It is a nice to have, if you want to change values and build a binary again.
eagerList.people[0]?.lastName = "Mustermann"
eagerList.toByteArray