FBS Annotations - jamescourtney/FlatSharp GitHub Wiki

FlatSharp supports the general FBS syntax. It does not implement every feature, but tries to warn you in those cases. It also supports some custom extensions that are specific to FlatSharp schemas. Those are documented here. All flatsharp attributes are prefixed with "fs_" to avoid conflicting with other .FBS compilers.

Standard FBS attributes

Supported

Not Supported

  • force_align
  • flexbuffer
  • hash
  • original_order

FlatSharp FBS Extensions

Common Options

  • fs_forceWrite: Tells FlatSharp to always serialize scalar fields, even if they match the default value.
  • fs_literalName: Tells FlatSharp not to normalize or otherwise adjust field names.
  • fs_serializer: Controls compile-time serializer generation
  • fs_sharedString: Controls Shared Strings.
  • fs_sortedVector: Controls whether table vectors are sorted or not.
  • fs_vector: Controls the type of vectors (lists/memory/indexed vectors).
  • fs_writeThrough: Enables FlatSharp to change the value of certain fields without a full re-serialize.

Generated Code Options

  • fs_defaultCtor: Controls how and if default constructors are generated.
  • fs_setter: Controls how property setters are generated.
  • fs_rpcInterface: For rpc_services in FBS files, instructs FlatSharp to generate a Channel<T> based set of interfaces.

Value Structs

  • fs_valueStruct: Indicates that FlatSharp should generate a value type (C# struct) instead of a reference type (C# class) for a FlatBuffer struct.
  • fs_unsafeExternal: Allows defining value types as "external" to allow reuse from other libraries.
  • fs_unsafeStructVector: Generates fast, unsafe code for accessing indexes of a value struct vector.
  • fs_unsafeUnion: For unions of only value structs, avoids boxing when using Unions.
  • fs_memoryMarshal: Control the behavior of serializing and parsing value structs.

fs_serializer

Description: Instructs the FlatSharp compiler to pre-generate a serializer for this type and all that it encompasses. Precompiled Serializers are required for types exposed in gRPC definitions. Beginning in FlatSharp 7, all types of serializers are generated; the value of this attribute controls only the type of serializer on the .Serializer property of the generated class.

Valid On: Tables only

Value Required: False

Default Value: Default

Valid Values:

  • Default (GreedyMutable)
  • GreedyMutable
  • Greedy
  • Progressive
  • Lazy

Examples:

// Table with a default serializer
table MyTable (fs_serializer) { Value : int; }

// Table with a lazy serializer
table LazyTable (fs_serializer:"Lazy") { Value : int; }

fs_setter

Description: Controls how a property setter is generated for the given field. This, along with the fs_defaultCtor attribute can assist in defining immutable objects.

Valid On: Fields

Value Required: False

Default Value: Public

Valid Values:

  • None
  • ProtectedInternal
  • Protected
  • Public
  • PublicInit
  • ProtectedInit
  • ProtectedInternalInit

Examples:

table MyTable
{ 
   Value  : int; // generates a public setter property.
   Value2 : int (fs_setter:"None"); // does not generate a setter property.
   Value3 : int (fs_setter:"ProtectedInternal"); // generates a setter with the "protected internal" access modifier
   Value4 : int (fs_setter:"Protected"); // generates a setter with the "protected" modifier.
   Value5 : int (fs_setter:"Public"); // generates a setter with the "public" modifier.

   // These will only compile with C# 9 and above.
   Value6 : int (fs_setter:"PublicInit"); // generates an init-only setter with the "public" access modifier
   Value7 : int (fs_setter:"ProtectedInit"); // generates an init-only setter with the "protected" modifier.
   Value8 : int (fs_setter:"ProtectedInternalInit"); // generates an init-only setter with the "protected internal" modifier.
}

fs_vector

Description: Controls the type of vector generated.

Valid On: Vector fields

Value Required: False

Default Value: Memory for ubyte, IList for everything else

Valid Values:

  • IList
  • IReadOnlyList
  • Memory
  • ReadOnlyMemory
  • IIndexedVector

Examples:

table MyTable
{ 
   // Generates an IList<int> vector
   Value : [ int ]; 
   
   // Generates an IList<int> vector.
   Value2 : [ int ] (fs_vector:"IList"); 
   
   // Vector of type IReadOnlyList<int>
   Value3 : [ int ] (fs_vector:"IReadOnlyList"); 
   
   // Vector of type Memory<byte>.
   Value4 : [ ubyte ];

   // Vector of type IList<byte>
   Value5 : [ ubyte ] (fs_vector:"IList");

   // Same as above, but with ReadOnlyMemory<byte>
   Value6 : [ ubyte ] (fs_vector:"ReadOnlyMemory");

   // Generates an IIndexedVector<OtherTable> vector. Indexed vectors are always sorted, can only
   // contain tables, and those tables must have a 'Key' attribute specified.
   Value7 : [ OtherTable ] (fs_vector:"IIndexedVector");
}

fs_sortedVector

Description: Controls whether a given vector is sorted at serialization time. Sorted Vectors must contain tables, and the tables must have a single field with the key attribute.

Valid On: Vector fields

Value Required: False

Default Value: True

Valid Values:

  • True
  • False

Examples:

table MyTable
{ 
   Value : [ ItemTable ] (fs_sortedVector);
}

table ItemTable
{
   Value : string (key);
}

key

Description: Indicates that the field in a given table that should be used as the sort key when the table is used as the item of a sorted vector.

Valid On: Table fields of type string, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, or double.

Examples:

table MyTable
{ 
   Value  : [ItemTable] (fs_sortedVector);
}

table ItemTable
{
   Value : string (key);
}

fs_sharedString

Description: The given string field or string vector should be deduplicated when serializing. The shared string writer used in FlatSharp is configurable -- please refer to the Shared Strings page of this wiki for details.

Valid On: String fields and String Vectors in tables.

Value Required: False

Default Value: True

Valid Values:

  • True
  • False

Examples:

table MyTable
{ 
   Value  : [string] (fs_sharedString);
   Value2 : string (fs_sharedString); 
}

fs_defaultCtor

Description: Controls how FlatSharp generates default (parameterless) constructors for Tables and Structs. This attribute can be used to assist in defining immutable objects. Additional constructors may be added with partial classes.

Valid On: Tables and Structs

Value Required: False

Default Value: "Public"

Valid Values:

  • "Public"
  • "PublicObsolete"
  • "None" (on tables only)

Examples:

// Will not have a default constructor.
table MyTable (fs_defaultCtor:"None") { Value : int; }

// Will have a public default constructor with the [Obsolete] attribute applied.
struct MyStruct (fs_defaultCtor:"PublicObsolete") { Value : int }

id

Description: Controls the ID (index) of fields in a FlatBuffer table. This allows declaring items out of order. The id attribute is an all-or-none proposition; it is not possible to only declare id on some fields.

Valid On: Table fields

Examples:

table MyTable
{
   Value1 : int (id:1);
   Value0 : int (id:0);
}

deprecated

Description: Indicates that a table field is deprecated. Deprecated fields continue to occupy an index, but are not serialized or parsed.

Valid On: Table fields

Examples:

table MyTable
{
   Value : int (deprecated);
   Value1 : int;
}

required

Description: Indicates that a table field is required. If not set, required fields will throw an exception when serializing and parsing (depending on the deserialization mode, FlatSharp may not observe a required field is missing until it is accessed). When a field has the required attribute, FlatSharp will not generate a nullable reference type annotation to convey this intent.

Valid On: Non-scalar Table fields

Examples:

table MyTable
{
   Value : OtherTable (required);
   Value1 : [int] (required);
}

bit_flags

Description: Indicates that an enum should be a set of bit flags. Enums with this attribute carry the [Flags] attribute.

Valid On: Enums

Examples:

enum MyEnum : ubyte (bit_flags)
{
   One,    // value of 1
   Two,    // value of 2 
   Three,  // value of 4
   Four    // value of 8
}

fs_forceWrite

Description: Indicates that FlatSharp should explicitly serialize scalar fields into the buffer, even when they match the default value.

Valid On: Scalar Table Fields

Value Required: False

Default Value: True

Examples:

table MyTable 
{ 
    // this field is not opted into force write.
    Value1:int;                 

    // this field is opted into force write
    Value2:int (fs_forceWrite);
}

table MyTable (fs_forceWrite)
{ 
    // this field inherits the forceWrite setting from the table.
    Value1:int;

    // this field opts out of the table setting.
    Value2:int (fs_forceWrite:"false");
}

fs_writeThrough

Description: Indicates that property setters on deserialized object should write through to the underlying buffer. This allows minor modifications without a full parse/serialize pass.

Valid On:: WriteThrough is only supported when using Progressive or Lazy mode. Greedy/GreedyMutable are unsupported.

WriteThrough is supported in the following contexts:

  • Fields inside reference structs
  • Table fields of value structs
  • Vectors of value structs

Value Required: False

Default Value: True

Examples:

Reference struct:

struct MyReferenceStruct (fs_writeThrough) // all fields are opted in for write through
{ 
    // Write-through is disabled.
    Value1:int (fs_writeThrough:"false");                 

    // Mutations to this property will update the underlying buffer.
    Value2:int;
}

// Same effect as the above
struct MyReferenceStruct2
{
    Value1 : int;
    Value2 : int (fs_writeThrough);
}

Value structs:

table MyTable
{
     // Assignments of this property will update the underlying buffer. Value-struct properties
     // must be marked as 'required'.
     Property : ValueStruct (fs_writeThrough, required);

     // Items in this vector can be saved back to the buffer by assigning to an index.
     // Write through does not allow modifying the number of items in the vector -- only overwriting them.
     Vector : [ ValueStruct ] (fs_writeThrough); 
}

struct ValueStruct (fs_valueStruct)
{
     X : int; Y : int; Z : int;
}

fs_rpcInterface

Description: Indicates that FlatSharp should generate and implement interfaces for gRPC clients based on Channel<T>. This can assist in creating abstracted interfaces that do not directly depend on gRPC. This only impacts client definitions. Server RPC definitions are still tightly coupled to gRPC.

Valid On: gRPC rpc_service elements.

Value Required: False

Examples:

rpc_service MyGrpcService (fs_rpcInterface)
{
    UnaryCall (Message) : Message;
    ClientStreamingCall (Message) : Message (streaming:"client");
    ServerStreamingCall (Message) : Message (streaming:"server");
    DuplexStreamingCall (Message) : Message (streaming:"duplex");
}

Generates:

// The MyGrpcService Client will implement this interface.
// The interface contract uses channels to abstract out the underlying gRPC details. For information about channels, see:
// https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/
public interface IMyGrpcService
{
    // A normal async unary call.
    Task<Message> UnaryCall(Message request, CancellationToken token);

    // Streams items from the requestStream into the underlying gRPC channel. Once the requestStream is completed by the  
    // corresponding ChannelWriter instance, the server streaming call will finish and return the result.
    Task<Message> ClientStreamingCall(ChannelReader<Message> requestStream, CancellationToken token);

    // Streams items from the server into the given responseStream parameter. The caller may read these by using
    // the ChannelReader<Message> instance created with the writer.
    Task ServerStreamingCall(Message request, ChannelWriter<Message> responseStream, CancellationToken token);

    // Streams items bidirectionally from both client and server. Note that requestStream and responseStream should be different channels,
    // otherwise the messages will go in a loop.
    Task DuplexStreamingCall(ChannelReader<Message> requestStream, ChannelWriter<Message> responseStream, CancellationToken token);
}

fs_valueStruct

Description: For a struct, indicates that FlatSharp should generate a C# struct instead of a C# class. Value structs may only contain other value types, including other value structs, scalars, and enums. Reference structs may contain value structs.

Valid On: struct elements

Value Required: False

Default Value: true

Examples:

struct Vec3 (fs_valueStruct)
{
    X : float;
    Y : float;
    Z : float;
}

Generates:

[FlatBufferStruct]
[StructLayout(LayoutKind.Explicit, Size = 12)]
public struct Vec3
{
      [FieldOffset(0)] public float X;
      [FieldOffset(4)] public float Y;
      [FieldOffset(8)] public float Z;
}

fs_unsafeStructVector

Description: Generates unsafe code for accessing struct vector members inside a value struct. The unsafe code is often twice as fast as the equivalent safe code.

Valid On: Struct vector fields in value (fs_valueStruct) structs.

Value Required: False

Default Value: true

Examples:

struct Sha256 (fs_valueStruct)
{
    Value : [ ubyte : 32 ] (fs_unsafeStructVector);
}

Generates:

public static ref byte Value_Item(ref Sha256 item, int index)
{
    if ((uint)index >= 32)
    {
        throw new IndexOutOfRangeException();
    }

    return Unsafe.Add<byte>(ref item.__flatsharp__Value_0, index);
}

fs_memoryMarshal

Description: A hint to FlatSharp about how to use MemoryMarshal.Cast when serializing and deserializing value type structs. Value type structs have the same layout in memory as they do in the FlatBuffer, so FlatSharp can serialize and deserialize these without going field-by-field. This is often a performance win when the number of items in a value struct is large. However, there are large variations in performance of this method between .NET Core 2.1, 3.1, and NET 5.0, not to mention Unity and other runtimes. To allow for users using many different targets that may have their own quirks, fs_memoryMarshal allows tuning the mechanism for value structs for what is most efficient for the given platform. Note that even when Memory Marshalling is enabled, FlatSharp will fall back to field-by-field deserialization in certain situations, such as big-endian architectures.

Valid On: Value type (fs_valueStruct) structs.

Value Required: False

Valid Values:

  • Default - FlatSharp will decide whether to use MemoryMarshal.Cast or not, depending on the complexity of the struct.
  • Never - MemoryMarshal.Cast is completely disabled for the struct.
  • Serialize - MemoryMarshal.Cast is used for serialization, but not for parsing.
  • Parse - MemoryMarshal.Cast is used for parsing, but not serialization.
  • Always - MemoryMarshal.Cast is used for both parsing and serialization.

Default Value: Default

Examples:

struct Vec3 (fs_valueStruct, fs_memoryMarshal:"Always")
{
    X : float;
    Y : float;
    Z : float;
}

Generates (For serialization):

Span<byte> sizedSpan = span.Slice(offset, 12);
if (BitConverter.IsLittleEndian) // Setting the value to "Never" or "Parse" would remove this block from the serialize method.
{
    var tempSpan = System.Runtime.InteropServices.MemoryMarshal.Cast<byte, global::BenchmarkCore.Vec3>(sizedSpan);
    tempSpan[0] = value;
}
else
{
    // Write field by field. 
    WriteFloat(spanWriter, sizedSpan, value.X, 0);
    WriteFloat(spanWriter, sizedSpan, value.Y, 4);
    WriteFloat(spanWriter, sizedSpan, value.Z, 8);
}

fs_unsafeExternal

Description: For some cases, such as Unity, it may be valuable to reuse existing value types from other libraries. A common example would be System.Numerics.Vector3. Defining a value struct as "external" allows FlatSharp to generate code to interoperate with types not defined in a .FBS schema. This feature will only work on little endian architectures, such as x86/x64. Running on a big endian machine will result in a runtime exception.

FlatSharp Versions: 7 and above.

Valid On: Value-type structs (fs_valueStruct).

Example:

struct Vec3 (fs_valueStruct, fs_unsafeExternal:"System.Numerics.Vector3")
{
    X : float32; 
    Y : float32;
    Z : float32;
}

Remarks: This feature is not without risk. FlatSharp can only validate two things:

  • The size of the external struct is equal to the size of the declared schema, and will throw a runtime exception if not.
  • The machine is a little-endian architecture.

FlatSharp will not validate:

  • That the layout of the external struct matches the layout of the schema struct.
  • That the data is properly aligned.
  • That the data is well-formed.

As such, this is a "use at your own risk" feature that should only be considered alongside extensive testing.

fs_literalName

Description: Instructs FlatSharp not to normalize or otherwise adjust a table or struct field name.

FlatSharp Versions: 7 and above.

Valid On: Tables, structs, and fields.

Default Value: true

Value Required: false

Example:

table MyTable (fs_literalName) // all members left alone
{
   normalized_field : int (fs_literalName:"false"); // NormalizedField
   literal_field : int; // literal_field
}

struct MyStruct
{
   normalized_field : int; // NormalizedField
   literal_field : int (fs_literalName); // literal_field
}

fs_unsafeUnion

Description: Generates a union that is sized to the same size as its largest element and contains a fixed byte array. Uses unsafe code but avoids the need for boxing. By default, FlatSharp unions store their value in an internal object, which can cause additional allocations for value structs due to boxing. Unsafe unions provide a way to avoid this penalty so long as all union members are value structs.

FlatSharp Versions: 7 and above.

Valid On: Unions of value structs (fs_valueStruct)

Default Value: true

Value Required: false

Example:

struct Metric (fs_valueStruct) { x : float; } // 4 bytes
struct Imperial (fs_valueStruct) { feet : int; inches : int; } // 8 bytes

union Height (fs_unsafeUnion) { Metric, Imperial }

Generates

public struct Height : IFlatBufferUnion<Metric, Imperial>
{
     // 8 is inferred from the size of the largest member. In this case, Imperial.
     private fixed byte[8] value;

     ...
}
⚠️ **GitHub.com Fallback** ⚠️