Architecture - jamescourtney/FlatSharp GitHub Wiki

Scope

This document is intended for those wishing to contribute to FlatSharp. It aims to provide explanation for the "how" of FlatSharp. If you are just looking for documentation on the usage of FlatSharp, this is not the page for you!

Origins and History

FlatSharp was started as a science experiment by the author, who was looking for an excuse to play with Memory<T> and Span<T>. The original version of FlatSharp did not include a .FBS compiler; only C# attributes ([FlatBufferTable], [FlatBufferStruct], etc) were supported. This is an important historical artifact to help understand the present architecture of the project.

Attribute-Based Serialization

The interesting parts of FlatSharp involve taking C# classes and structs annotated with attributes. For an example of attribute-based serialization, refer to this sample.

To do this at runtime, FlatSharp follows roughly the following process:

  • Reflection, to discover attributes on classes and recursively traverse the object graph to create a Type Model
  • The FlatSharp Type Model, which validates the attributes, their usage, and emits C# code to parse and serialize the schema.
  • Roslyn, to compile the generated C# into an in-memory assembly, which is then loaded an instantiated to give an instance of IGeneratedSerializer.

Type Model

The FlatSharp type model is complex and is the core of what FlatSharp does. The FlatSharp type model is responsible for both validation of the declared schema and generation of code. In general, each logical FlatBuffer element has a dedicated ITypeModel implementation, such as StringTypeModel, TableTypeModel, or StructTypeModel.

Code Generation

Code Generation is done with interpolated strings. While this isn't efficient, it's a one-time tax per root type. Of interest, a separate in-memory assembly is loaded per root type. So if tables A and B both contain C, code to serialize and parse C will be emitted twice.

FlatSharp Compiler

The compiler was an addition to FlatSharp after the initial work to support attribute-based serialization. The compiler is "dumb", in the sense that it doesn't deeply understand much about FlatBuffers. The FlatSharp compiler's job is to:

  1. Invoke flatc to parse an FBS file
  2. Generate C# classes with the required attributes and properties
  3. Use Roslyn to compile those classes into a runtime assembly
  4. Invoke the attribute-based workflow from above on the runtime assembly from (3) to generate C# to parse/serialize a given type.
  5. Inject the generated C# serializer into the classes from (2).
  6. Write combined C# to file.

So, the FlatSharp compiler is really just a clever usage of FlatSharp and Roslyn. This process is complex, but allows us to avoid the complexity of maintaining two different codegen implementations, which would be hard to test and maintain.