Beispiel #1
0
    public static int GetScalarSize(this BaseType type)
    {
        FlatSharpInternal.Assert(type.IsScalar(), "Type " + type + " was not a scalar");

        switch (type)
        {
        case BaseType.Bool:
        case BaseType.Byte:
        case BaseType.UByte:
            return(1);

        case BaseType.Short:
        case BaseType.UShort:
            return(2);

        case BaseType.Int:
        case BaseType.UInt:
        case BaseType.Float:
            return(4);

        case BaseType.Long:
        case BaseType.ULong:
        case BaseType.Double:
            return(8);

        default:
            throw new InvalidOperationException("impossible");
        }
    }
    internal static string GetCompilableTypeName(this Type t)
    {
        FlatSharpInternal.Assert(!string.IsNullOrEmpty(t.FullName), $"{t} has null/empty full name.");

        string name;

        if (t.IsGenericType)
        {
            List <string> parameters = new List <string>();
            foreach (var generic in t.GetGenericArguments())
            {
                parameters.Add(GetCompilableTypeName(generic));
            }

            name = $"{t.FullName.Split('`')[0]}<{string.Join(", ", parameters)}>";
        }
        else if (t.IsArray)
        {
            name = $"{GetCompilableTypeName(t.GetElementType()!)}[]";
        }
        else
        {
            name = t.FullName;
        }

        name = name.Replace('+', '.');
        return(name);
    }
Beispiel #3
0
    public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext context)
    {
        if (!context.Options.GreedyDeserialize)
        {
            throw new InvalidFlatBufferDefinitionException("Array vectors may only be used with Greedy serializers.");
        }

        var(classDef, className) = FlatBufferVectorHelpers.CreateFlatBufferVectorOfUnionSubclass(
            this.ItemTypeModel,
            context);

        FlatSharpInternal.Assert(!string.IsNullOrEmpty(context.TableFieldContextVariableName), "expecting table field context");

        string createFlatBufferVector =
            $@"new {className}<{context.InputBufferTypeName}>(
            {context.InputBufferVariableName}, 
            {context.OffsetVariableName}.offset0 + {context.InputBufferVariableName}.{nameof(InputBufferExtensions.ReadUOffset)}({context.OffsetVariableName}.offset0), 
            {context.OffsetVariableName}.offset1 + {context.InputBufferVariableName}.{nameof(InputBufferExtensions.ReadUOffset)}({context.OffsetVariableName}.offset1),
            {context.TableFieldContextVariableName})";

        string body = $"return ({createFlatBufferVector}).ToArray();";

        return(new CodeGeneratedMethod(body)
        {
            ClassDefinition = classDef
        });
    }
Beispiel #4
0
    public override Type OnInitialize()
    {
        FlatSharpInternal.Assert(this.ClrType.IsArray, $"Array vectors must be arrays. Type = {this.ClrType.FullName}.");
        FlatSharpInternal.Assert(this.ClrType.GetArrayRank() == 1, "Array vectors may only be single-dimension.");

        return(this.ClrType.GetElementType() !);
    }
    internal static string CreateParseBody(
        ITypeModel itemTypeModel,
        string createFlatBufferVector,
        ParserCodeGenContext context)
    {
        FlatSharpInternal.Assert(!string.IsNullOrEmpty(context.TableFieldContextVariableName), "expecting table field context");

        if (context.Options.GreedyDeserialize)
        {
            string body = $"({createFlatBufferVector}).FlatBufferVectorToList()";
            if (!context.Options.GenerateMutableObjects)
            {
                body += ".AsReadOnly()";
            }

            return($"return {body};");
        }
        else if (context.Options.Lazy)
        {
            return($"return {createFlatBufferVector};");
        }
        else
        {
            FlatSharpInternal.Assert(context.Options.Progressive, "expecting progressive");
            return($"return new FlatBufferProgressiveVector<{itemTypeModel.GetGlobalCompilableTypeName()}, {context.InputBufferTypeName}>({createFlatBufferVector});");
        }
    }
    public sealed override void Initialize()
    {
        base.Initialize();

        this.ItemTypeModel = this.typeModelContainer.CreateTypeModel(this.OnInitialize());

        FlatSharpInternal.Assert(this.ItemTypeModel.SchemaType == FlatBufferSchemaType.Union, "Union vectors can't contain non-union elements.");
    }
    private TableSchemaModel(Schema.Schema schema, FlatBufferObject table) : base(schema, table)
    {
        FlatSharpInternal.Assert(table.IsStruct == false, "Not expecting struct");

        this.AttributeValidator.DeserializationOptionValidator = _ => AttributeValidationResult.Valid;
        this.AttributeValidator.DefaultConstructorValidator    = _ => AttributeValidationResult.Valid;
        this.AttributeValidator.ForceWriteValidator            = _ => AttributeValidationResult.Valid;
    }
    private void ValidateHasSerializer(FlatBufferObject obj)
    {
        FlatSharpInternal.Assert(!obj.IsStruct, "expecting only tables");
        FlatSharpAttributes attrs = new FlatSharpAttributes(obj.Attributes);

        if (attrs.DeserializationOption is null)
        {
            ErrorContext.Current.RegisterError($"RPC call '{this.fullName}' uses table '{obj.Name}', which does not specify the '{MetadataKeys.SerializerKind}' attribute.");
        }
    }
    public Memory <byte> GetByteMemory(int start, int length)
    {
        checked
        {
            FlatSharpInternal.Assert(start >= 0, "GetByteMemory.Start was negative.");

            var seg = this.pointer.segment;
            return(new Memory <byte>(seg.Array, seg.Offset + start, length));
        }
    }
    public override Type OnInitialize()
    {
        var genericDef = this.ClrType.GetGenericTypeDefinition();

        FlatSharpInternal.Assert(
            genericDef == typeof(IList <>) || genericDef == typeof(IReadOnlyList <>),
            "List vector of union must be IList or IReadOnlyList.");

        return(this.ClrType.GetGenericArguments()[0]);
    }
    private EnumSchemaModel(Schema.Schema schema, FlatBufferEnum @enum) : base(schema, @enum.Name, new FlatSharpAttributes(@enum.Attributes))
    {
        FlatSharpInternal.Assert([email protected], "Not expecting union");
        FlatSharpInternal.Assert(@enum.UnderlyingType.BaseType.IsInteger(), "Expected scalar base type");
        FlatSharpInternal.Assert(@enum.UnderlyingType.BaseType.TryGetBuiltInTypeName(out this.underlyingType !), "Couldn't get type name string");

        this.isFlags       = @enum.Attributes?.ContainsKey(MetadataKeys.BitFlags) == true;
        this.nameValueMap  = @enum.Values.ToDictionary(x => x.Value.Key, x => x.Value);
        this.DeclaringFile = @enum.DeclarationFile;
        this.documentation = @enum.Documentation;
    }
    public override Type OnInitialize()
    {
        var genericDef = this.ClrType.GetGenericTypeDefinition();

        FlatSharpInternal.Assert(
            genericDef == typeof(IList <>) || genericDef == typeof(IReadOnlyList <>),
            $"Cannot build a vector from type: {this.ClrType}. Only List, ReadOnlyList, Memory, ReadOnlyMemory, and Arrays are supported.");

        this.isReadOnly = genericDef == typeof(IReadOnlyList <>);
        return(this.ClrType.GetGenericArguments()[0]);
    }
    private ReferenceStructSchemaModel(Schema.Schema schema, FlatBufferObject @struct) : base(schema, @struct)
    {
        FlatSharpInternal.Assert(@struct.IsStruct, "Expecting struct");

        this.AttributeValidator.DefaultConstructorValidator = kind => kind switch
        {
            DefaultConstructorKind.Public or DefaultConstructorKind.PublicObsolete => AttributeValidationResult.Valid,
                                                                    _ => AttributeValidationResult.ValueInvalid,
        };

        this.AttributeValidator.WriteThroughValidator = _ => AttributeValidationResult.Valid;
    }
    public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext context)
    {
        string body;

        FlatSharpInternal.Assert(this.ItemTypeModel is not null, "Flatsharp internal error: ItemTypeModel null");

        if (!context.Options.GreedyDeserialize)
        {
            throw new InvalidFlatBufferDefinitionException("Array vectors may only be used with Greedy serializers.");
        }

        ValidateWriteThrough(
            writeThroughSupported: false,
            this,
            context.AllFieldContexts,
            context.Options);

        (string vectorClassDef, string vectorClassName) = (string.Empty, string.Empty);

        if (this.ItemTypeModel.ClrType == typeof(byte))
        {
            // can handle this as memory.
            string method           = nameof(InputBufferExtensions.ReadByteReadOnlyMemoryBlock);
            string memoryVectorRead = $"{context.InputBufferVariableName}.{method}({context.OffsetVariableName})";
            body = $"return {memoryVectorRead}.ToArray();";
        }
        else
        {
            (vectorClassDef, vectorClassName) = FlatBufferVectorHelpers.CreateFlatBufferVectorSubclass(
                this.ItemTypeModel,
                context);

            FlatSharpInternal.Assert(!string.IsNullOrEmpty(context.TableFieldContextVariableName), "expecting table field context");

            string createFlatBufferVector =
                $@"new {vectorClassName}<{context.InputBufferTypeName}>(
                    {context.InputBufferVariableName}, 
                    {context.OffsetVariableName} + {context.InputBufferVariableName}.{nameof(InputBufferExtensions.ReadUOffset)}({context.OffsetVariableName}), 
                    {this.PaddedMemberInlineSize},
                    {context.TableFieldContextVariableName})";

            body = $"return ({createFlatBufferVector}).ToArray();";
        }

        return(new CodeGeneratedMethod(body)
        {
            ClassDefinition = vectorClassDef
        });
    }
    internal static (AccessModifier propertyModifier, AccessModifier?getModifer, AccessModifier?setModifier) GetPropertyAccessModifiers(
        AccessModifier getModifier,
        AccessModifier?setModifier)
    {
        if (setModifier is null || getModifier == setModifier.Value)
        {
            return(getModifier, null, null);
        }

        FlatSharpInternal.Assert(
            getModifier < setModifier.Value,
            "Getter expected to be more visible than setter");

        return(getModifier, null, setModifier);
    }
Beispiel #16
0
    public override CodeGeneratedMethod CreateParseMethodBody(ParserCodeGenContext context)
    {
        ValidateWriteThrough(
            writeThroughSupported: false,
            this,
            context.AllFieldContexts,
            context.Options);

        string body;
        string keyTypeName   = CSharpHelpers.GetGlobalCompilableTypeName(this.keyTypeModel.ClrType);
        string valueTypeName = CSharpHelpers.GetGlobalCompilableTypeName(this.valueTypeModel.ClrType);

        (string vectorClassDef, string vectorClassName) = FlatBufferVectorHelpers.CreateFlatBufferVectorSubclass(
            this.valueTypeModel,
            context);

        FlatSharpInternal.Assert(!string.IsNullOrEmpty(context.TableFieldContextVariableName), "field context was null/empty");

        string createFlatBufferVector =
            $@"new {vectorClassName}<{context.InputBufferTypeName}>(
            {context.InputBufferVariableName}, 
            {context.OffsetVariableName} + {context.InputBufferVariableName}.{nameof(InputBufferExtensions.ReadUOffset)}({context.OffsetVariableName}), 
            {this.PaddedMemberInlineSize},
            {context.TableFieldContextVariableName})";

        string mutable = context.Options.GenerateMutableObjects.ToString().ToLowerInvariant();

        if (context.Options.GreedyDeserialize)
        {
            // Eager indexed vector.
            body = $@"return new {nameof(IndexedVector<string, string>)}<{keyTypeName}, {valueTypeName}>({createFlatBufferVector}, {mutable});";
        }
        else if (context.Options.Lazy)
        {
            // Lazy indexed vector.
            body = $@"return new {nameof(FlatBufferIndexedVector<string, string, IInputBuffer>)}<{keyTypeName}, {valueTypeName}, {context.InputBufferTypeName}>({createFlatBufferVector});";
        }
        else
        {
            FlatSharpInternal.Assert(context.Options.Progressive, "expecting progressive");
            body = $@"return new {nameof(FlatBufferProgressiveIndexedVector<string, string, IInputBuffer>)}<{keyTypeName}, {valueTypeName}, {context.InputBufferTypeName}>({createFlatBufferVector});";
        }

        return(new CodeGeneratedMethod(body)
        {
            IsMethodInline = true, ClassDefinition = vectorClassDef
        });
    }
    public override void Initialize()
    {
        {
            FlatBufferStructAttribute?attribute = this.ClrType.GetCustomAttribute <FlatBufferStructAttribute>();

            FlatSharpInternal.Assert(attribute != null, "Missing attribute.");
            this.attribute = attribute !;
        }

        TableTypeModel.EnsureClassCanBeInheritedByOutsideAssembly(this.ClrType, out this.preferredConstructor);
        this.onDeserializeMethod = TableTypeModel.ValidateOnDeserializedMethod(this);

        var properties = this.ClrType
                         .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
                         .Select(x => new
        {
            Property  = x,
            Attribute = x.GetCustomAttribute <FlatBufferItemAttribute>() !   // suppress check here; we filter on the next line.
        })
    private ValueStructSchemaModel(Schema.Schema schema, FlatBufferObject @struct) : base(schema, @struct.Name, new FlatSharpAttributes(@struct.Attributes))
    {
        FlatSharpInternal.Assert(@struct.IsStruct, "Expecting struct");
        FlatSharpInternal.Assert(this.Attributes.ValueStruct == true, "Expecting value struct");

        this.@struct       = @struct;
        this.fields        = new();
        this.structVectors = new();

        foreach (Field field in [email protected](x => x.Id))
        {
            IFlatSharpAttributes attrs = new FlatSharpAttributes(field.Attributes);

            string fieldType = field.Type.ResolveTypeOrElementTypeName(schema, this.Attributes);
            if (field.Type.BaseType == BaseType.Array)
            {
                // struct vector
                int size = field.Type.ElementType.GetScalarSize();

                List <string> vectorFields = new();
                for (int i = 0; i < field.Type.FixedLength; ++i)
                {
                    string name = $"__flatsharp__{field.Name}_{i}";

                    MutableFlatSharpAttributes tempAttrs = new MutableFlatSharpAttributes(attrs)
                    {
                        UnsafeStructVector = null,
                    };

                    vectorFields.Add(name);
                    this.fields.Add(new(field.Offset + (i * size), name, "private", fieldType, $"{field.Name}({i})", null, this, tempAttrs));
                }

                this.structVectors.Add(new(fieldType, field.Name, vectorFields, this, field.Documentation, attrs));
            }
            else
            {
                this.fields.Add(new(field.Offset, field.Name, "public", fieldType, field.Name, field.Documentation, this, attrs));
            }
        }

        this.AttributeValidator.MemoryMarshalValidator = _ => AttributeValidationResult.Valid;
    }
Beispiel #19
0
    public override Type OnInitialize()
    {
        FlatSharpInternal.Assert(
            this.ClrType.IsGenericType && this.ClrType.GetGenericTypeDefinition() == typeof(IIndexedVector <,>),
            $"Indexed vectors must be of type IIndexedVector. Type = {this.GetCompilableTypeName()}.");

        Type keyType   = this.ClrType.GetGenericArguments()[0];
        Type valueType = this.ClrType.GetGenericArguments()[1];

        this.keyTypeModel   = this.typeModelContainer.CreateTypeModel(keyType);
        this.valueTypeModel = this.typeModelContainer.CreateTypeModel(valueType);

        if (this.valueTypeModel.SchemaType != FlatBufferSchemaType.Table)
        {
            throw new InvalidFlatBufferDefinitionException(
                      $"Indexed vector values must be flatbuffer tables. Type = '{this.valueTypeModel.GetCompilableTypeName()}'");
        }

        if (!this.valueTypeModel.TryGetTableKeyMember(out TableMemberModel? tempKeyMemberModel))
        {
            throw new InvalidFlatBufferDefinitionException(
                      $"Indexed vector values must have a property with the key attribute defined. Table = '{this.valueTypeModel.GetCompilableTypeName()}'");
        }
        else
        {
            this.keyMemberModel = tempKeyMemberModel;
        }

        if (!this.keyMemberModel.ItemTypeModel.TryGetSpanComparerType(out _))
        {
            throw new InvalidFlatBufferDefinitionException(
                      $"FlatSharp indexed vector keys must supply a span comparer. KeyType = '{this.keyMemberModel.ItemTypeModel.GetCompilableTypeName()}'.");
        }

        if (keyMemberModel.ItemTypeModel.ClrType != this.keyTypeModel.ClrType)
        {
            throw new InvalidFlatBufferDefinitionException(
                      $"FlatSharp indexed vector keys must have the same type as the key of the value. KeyType = {this.keyTypeModel.GetCompilableTypeName()}, Value Key Type = '{this.valueTypeModel.GetCompilableTypeName()}'.");
        }

        return(valueType);
    }
    public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeGenContext context)
    {
        List <string> body = new List <string>();

        body.Add($"Span<byte> scopedSpan = {context.SpanVariableName}.Slice({context.OffsetVariableName}, {this.PhysicalLayout[0].InlineSize});");

        FlatSharpInternal.Assert(!this.ClrType.IsValueType, "Value-type struct is unexpected");

        body.Add(
            $@"
                if ({context.ValueVariableName} is null)
                {{
                    scopedSpan.Clear();
                    return;
                }}
            ");

        for (int i = 0; i < this.Members.Count; ++i)
        {
            var memberInfo = this.Members[i];

            string propertyAccessor = $"{context.ValueVariableName}.{memberInfo.PropertyInfo.Name}";
            if (memberInfo.CustomAccessor is not null)
            {
                propertyAccessor = $"{context.ValueVariableName}.{memberInfo.CustomAccessor}";
            }

            var propContext = context with
            {
                SpanVariableName   = "scopedSpan",
                OffsetVariableName = $"{memberInfo.Offset}",
                ValueVariableName  = $"{propertyAccessor}"
            };

            string invocation = propContext.GetSerializeInvocation(memberInfo.ItemTypeModel.ClrType) + ";";
            body.Add(invocation);
        }

        return(new CodeGeneratedMethod(string.Join("\r\n", body)));
    }
    private UnionSchemaModel(Schema.Schema schema, FlatBufferEnum union) : base(schema, union.Name, new FlatSharpAttributes(union.Attributes))
    {
        FlatSharpInternal.Assert(union.UnderlyingType.BaseType == BaseType.UType, "Expecting utype");

        this.union = union;
    }
    protected override void OnWriteCode(CodeWriter writer, CompileContext context)
    {
        List <(string resolvedType, EnumVal value)> innerTypes = new List <(string, EnumVal)>();

        foreach (var inner in this.union.Values.Select(x => x.Value))
        {
            // Skip "none".
            if (inner.Value == 0)
            {
                FlatSharpInternal.Assert(inner.Key == "NONE", "Expecting discriminator 0 to be 'None'");
                continue;
            }

            FlatSharpInternal.Assert(inner.UnionType is not null, "Union type was null");

            long   discriminator = inner.Value;
            string typeName      = inner.UnionType.ResolveTypeOrElementTypeName(this.Schema, this.Attributes);
            innerTypes.Add((typeName, inner));
        }

        string interfaceName = $"IFlatBufferUnion<{string.Join(", ", innerTypes.Select(x => x.resolvedType))}>";

        writer.AppendSummaryComment(this.union.Documentation);
        writer.AppendLine("[System.Runtime.CompilerServices.CompilerGenerated]");
        writer.AppendLine($"public partial struct {this.Name} : {interfaceName}");
        using (writer.WithBlock())
        {
            // Generate an internal type enum.
            writer.AppendLine("public enum ItemKind : byte");
            using (writer.WithBlock())
            {
                foreach (var item in this.union.Values)
                {
                    writer.AppendLine($"{item.Value.Key} = {item.Value.Value},");
                }
            }

            writer.AppendLine();
            writer.AppendLine("private readonly object value;");

            writer.AppendLine();
            writer.AppendLine("public ItemKind Kind => (ItemKind)this.Discriminator;");

            writer.AppendLine();
            writer.AppendLine("public byte Discriminator { get; }");

            foreach (var item in innerTypes)
            {
                Type?propertyClrType = null;
                if (context.CompilePass > CodeWritingPass.Initialization)
                {
                    Type?previousType = context.PreviousAssembly?.GetType(this.FullName);
                    FlatSharpInternal.Assert(previousType is not null, "PreviousType was null");

                    propertyClrType = previousType
                                      .GetProperty($"Item{item.value.Value}", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)?
                                      .PropertyType;

                    FlatSharpInternal.Assert(propertyClrType is not null, "Couldn't find property");
                }

                writer.AppendLine();
                writer.AppendLine($"public {this.Name}({item.resolvedType} value)");
                using (writer.WithBlock())
                {
                    if (propertyClrType?.IsValueType == false)
                    {
                        writer.AppendLine("if (value is null)");
                        using (writer.WithBlock())
                        {
                            writer.AppendLine("throw new ArgumentNullException(nameof(value));");
                        }
                    }

                    writer.AppendLine($"this.value = value;");
                    writer.AppendLine($"this.Discriminator = {item.value.Value};");
                }

                writer.AppendLine();
                writer.AppendLine($"public {item.resolvedType} {item.value.Key} => this.Item{item.value.Value};");

                writer.AppendLine();
                writer.AppendLine($"public {item.resolvedType} Item{item.value.Value}");
                using (writer.WithBlock())
                {
                    writer.AppendLine("get");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine($"if (this.Discriminator != {item.value.Value})");
                        using (writer.WithBlock())
                        {
                            writer.AppendLine("throw new InvalidOperationException();");
                        }

                        writer.AppendLine($"return ({item.resolvedType})this.value;");
                    }
                }

                string notNullWhen       = string.Empty;
                string nullableReference = string.Empty;

                if (propertyClrType is not null)
                {
                    if (!propertyClrType.IsValueType)
                    {
                        nullableReference = "?";

                        if (context.Options.NullableWarnings == true)
                        {
                            notNullWhen = $"[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] ";
                        }
                    }
                }

                writer.AppendLine();
                writer.AppendLine($"public bool TryGet({notNullWhen}out {item.resolvedType}{nullableReference} value)");
                using (writer.WithBlock())
                {
                    writer.AppendLine($"if (this.Discriminator != {item.value.Value})");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine("value = default;");
                        writer.AppendLine("return false;");
                    }

                    writer.AppendLine($"value = ({item.resolvedType})this.value;");
                    writer.AppendLine("return true;");
                }
            }

            // Switch methods.
            this.WriteSwitchMethod(writer, true, true, innerTypes);
            this.WriteSwitchMethod(writer, true, false, innerTypes);
            this.WriteSwitchMethod(writer, false, true, innerTypes);
            this.WriteSwitchMethod(writer, false, false, innerTypes);
        }
    }
Beispiel #23
0
 protected override string CreateLoop(FlatBufferSerializerOptions options, string vectorVariableName, string numberOfItemsVariableName, string expectedVariableName, string body)
 {
     FlatSharpInternal.Assert(false, "Not expecting to do loop get max size for memory vector");
     throw new Exception();
 }
    protected override void OnWriteCode(CodeWriter writer, CompileContext context)
    {
        writer.AppendSummaryComment([email protected]);

        string memMarshalBehavior = string.Empty;

        if (this.Attributes.MemoryMarshalBehavior is not null)
        {
            memMarshalBehavior = $"{nameof(FlatBufferStructAttribute.MemoryMarshalBehavior)} = {nameof(MemoryMarshalBehavior)}.{this.Attributes.MemoryMarshalBehavior}";
        }

        writer.AppendLine($"[FlatBufferStruct({memMarshalBehavior})]");
        string size = string.Empty;

        if (context.CompilePass > CodeWritingPass.Initialization)
        {
            // load size from type.
            Type?previousType = context.PreviousAssembly?.GetType(this.FullName);
            FlatSharpInternal.Assert(previousType is not null, "Previous type was null");

            ITypeModel model = context.TypeModelContainer.CreateTypeModel(previousType);
            FlatSharpInternal.Assert(model.PhysicalLayout.Length == 1, "Expected physical layout length of 1.");

            size = $", Size = {model.PhysicalLayout[0].InlineSize}";
        }

        writer.AppendLine($"[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit{size})]");
        writer.AppendLine($"public partial struct {this.Name}");
        using (writer.WithBlock())
        {
            foreach (var field in this.fields)
            {
                writer.AppendSummaryComment(field.Documentation);
                writer.AppendLine($"[System.Runtime.InteropServices.FieldOffset({field.Offset})]");
                writer.AppendLine($"[FlatBufferMetadataAttribute(FlatBufferMetadataKind.Accessor, \"{field.Accessor}\")]");
                writer.AppendLine($"{field.Visibility} {field.TypeName} {field.Name};");
                writer.AppendLine();
            }

            foreach (var sv in this.structVectors)
            {
                writer.AppendSummaryComment($"Gets the number of items in the {sv.Name} vector.");
                writer.AppendLine($"public int {sv.Name}_Length => {sv.Properties.Count};");

                writer.AppendLine();

                writer.AppendSummaryComment(sv.Documentation);
                writer.AppendLine($"public static ref {sv.TypeName} {sv.Name}_Item(ref {this.Name} item, int index)");
                using (writer.WithBlock())
                {
                    if (sv.Attributes.UnsafeStructVector == true)
                    {
                        writer.AppendLine($"if (unchecked((uint)index) >= {sv.Properties.Count})");
                        using (writer.WithBlock())
                        {
                            writer.AppendLine("throw new IndexOutOfRangeException();");
                        }

                        writer.AppendLine($"return ref System.Runtime.CompilerServices.Unsafe.Add(ref item.{sv.Properties[0]}, index);");
                    }
                    else
                    {
                        writer.AppendLine("switch (index)");
                        using (writer.WithBlock())
                        {
                            for (int i = 0; i < sv.Properties.Count; ++i)
                            {
                                var item = sv.Properties[i];
                                writer.AppendLine($"case {i}: return ref item.{item};");
                            }

                            writer.AppendLine("default: throw new IndexOutOfRangeException();");
                        }
                    }
                }
            }
        }

        // extensions class.
        if (this.structVectors.Any())
        {
            writer.AppendLine($"public static class {this.Name}__FlatSharpExtensions");
            using (writer.WithBlock())
            {
                foreach (var sv in this.structVectors)
                {
                    writer.AppendSummaryComment(sv.Documentation);
                    writer.AppendLine($"public static ref {sv.TypeName} {sv.Name}(this ref {this.Name} item, int index)");
                    using (writer.WithBlock())
                    {
                        writer.AppendLine($"return ref {this.Name}.{sv.Name}_Item(ref item, index);");
                    }
                }
            }
        }
    }