-
-
Notifications
You must be signed in to change notification settings - Fork 51
FBS Annotations
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.
- force_align
- flexbuffer
- hash
- original_order
- 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.
- fs_defaultCtor: Controls how and if default constructors are generated.
- fs_setter: Controls how property setters are generated.
-
fs_rpcInterface: For
rpc_service
s in FBS files, instructs FlatSharp to generate aChannel<T>
based set of interfaces.
- 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.
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; }
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.
}
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");
}
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);
}
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);
}
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);
}
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 }
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);
}
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;
}
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);
}
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
}
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");
}
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;
}
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);
}
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;
}
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);
}
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);
}
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.
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
}
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;
...
}