Skip to content

Code Generation

Maxim Zaks edited this page Jan 23, 2016 · 1 revision

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.

FlatBufferSchema Code Generator

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.


The generated Swift API

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 Personclass. 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.

1. Lazy instantiation:

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.

2. Eager Instantiation:

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
Clone this wiki locally