diff --git a/src/FlatSharp.Compiler/CompilerOptions.cs b/src/FlatSharp.Compiler/CompilerOptions.cs index e5f8840d..93fe52ef 100644 --- a/src/FlatSharp.Compiler/CompilerOptions.cs +++ b/src/FlatSharp.Compiler/CompilerOptions.cs @@ -28,5 +28,14 @@ public record CompilerOptions [Option("nullable-warnings", Default = false)] public bool? NullableWarnings { get; set; } + + [Option('s', "offset-size")] + public int? OffsetSize { get; set; } + + [Option( "file-identifier-size")] + public int? FileIdentifierSize { get; set; } + + [Option("strict-file-identifier-size")] + public bool? StrictFileIdentifierSize { get; set; } } } diff --git a/src/FlatSharp.Compiler/FlatSharp.Compiler.targets b/src/FlatSharp.Compiler/FlatSharp.Compiler.targets index 0c2bd3a2..25446c39 100644 --- a/src/FlatSharp.Compiler/FlatSharp.Compiler.targets +++ b/src/FlatSharp.Compiler/FlatSharp.Compiler.targets @@ -33,7 +33,7 @@ diff --git a/src/FlatSharp.Compiler/FlatSharpCompiler.cs b/src/FlatSharp.Compiler/FlatSharpCompiler.cs index a1d0ca09..f8a22a1f 100644 --- a/src/FlatSharp.Compiler/FlatSharpCompiler.cs +++ b/src/FlatSharp.Compiler/FlatSharpCompiler.cs @@ -371,15 +371,32 @@ private static void CreateCSharp( writer = new CodeWriter(); + TypeModelContainer container; + + if (localOptions.OffsetSize == null + && localOptions.FileIdentifierSize == null + && localOptions.StrictFileIdentifierSize == null) + { + container = TypeModelContainer.CreateDefault(); + } + else + { + container = TypeModelContainer.CreateDefault(new( + localOptions.OffsetSize, + localOptions.FileIdentifierSize, + localOptions.StrictFileIdentifierSize + )); + } + rootNode.WriteCode( writer, new CompileContext { CompilePass = step, Options = localOptions, - RootFile = rootNode.DeclaringFile, + RootFile = rootNode.DeclaringFile ?? string.Empty, PreviousAssembly = assembly, - TypeModelContainer = TypeModelContainer.CreateDefault(), + TypeModelContainer = container, }); ErrorContext.Current.ThrowIfHasErrors(); diff --git a/src/FlatSharp.Compiler/TypeDefinitions/TableOrStructDefinition.cs b/src/FlatSharp.Compiler/TypeDefinitions/TableOrStructDefinition.cs index 50491dae..1d41cc21 100755 --- a/src/FlatSharp.Compiler/TypeDefinitions/TableOrStructDefinition.cs +++ b/src/FlatSharp.Compiler/TypeDefinitions/TableOrStructDefinition.cs @@ -50,7 +50,7 @@ public TableOrStructDefinition( public DefaultConstructorKind? DefaultConstructorKind { get; set; } - public string? FileIdentifier { get; set; } + public string? FileIdentifier; public FlatBufferDeserializationOption? RequestedSerializer { get; set; } @@ -158,6 +158,7 @@ protected override void OnWriteCode(CodeWriter writer, CompileContext context) if (this.IsTable && !string.IsNullOrEmpty(this.FileIdentifier)) { + context.TypeModelContainer.OffsetModel.ValidateFileIdentifier(ref this.FileIdentifier); attributeParts.Add($"{nameof(FlatBufferTableAttribute.FileIdentifier)} = \"{this.FileIdentifier}\""); } diff --git a/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs b/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs index c9940c30..12e1bf4b 100644 --- a/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs +++ b/src/FlatSharp.Runtime/Attributes/FlatBufferTableAttribute.cs @@ -27,6 +27,6 @@ public class FlatBufferTableAttribute : Attribute /// /// Specifies the file identifier for serialized tables. Must be precisely 4 ASCII characters. /// - public string? FileIdentifier { get; set; } + public string? FileIdentifier; } } diff --git a/src/FlatSharp/FlatBufferSerializer.cs b/src/FlatSharp/FlatBufferSerializer.cs index dbf5116c..f5c84af4 100644 --- a/src/FlatSharp/FlatBufferSerializer.cs +++ b/src/FlatSharp/FlatBufferSerializer.cs @@ -48,7 +48,7 @@ public FlatBufferSerializer() /// Creates a new FlatBufferSerializer using the given options. /// public FlatBufferSerializer(FlatBufferSerializerOptions options) - : this(options, TypeModelContainer.CreateDefault()) + : this(options, TypeModelContainer.CreateDefault(new(options.OffsetSize, options.FileIdentifierSize, options.StrictFileIdentifierSize))) { } diff --git a/src/FlatSharp/FlatBufferSerializerOptions.cs b/src/FlatSharp/FlatBufferSerializerOptions.cs index f0cf118d..10d2eccf 100644 --- a/src/FlatSharp/FlatBufferSerializerOptions.cs +++ b/src/FlatSharp/FlatBufferSerializerOptions.cs @@ -118,5 +118,20 @@ public FlatBufferSerializerOptions( /// Indicates if "protected internal" modifiers should be converted to protected. /// internal bool ConvertProtectedInternalToProtected { get; set; } = true; + + /// + /// Specify the offset size + /// + public int? OffsetSize { get; set; } = null; + + /// + /// Specify the size of the file identifier. + /// + public int? FileIdentifierSize { get; set; } = null; + + /// + /// Specify if the size of the file identifier is strict. + /// + public bool? StrictFileIdentifierSize { get; set; } = null; } } diff --git a/src/FlatSharp/FlatSharp.csproj b/src/FlatSharp/FlatSharp.csproj index c37c9cd4..e7614301 100644 --- a/src/FlatSharp/FlatSharp.csproj +++ b/src/FlatSharp/FlatSharp.csproj @@ -21,8 +21,8 @@ - - + + all runtime diff --git a/src/FlatSharp/OffsetModel.cs b/src/FlatSharp/OffsetModel.cs new file mode 100644 index 00000000..28580c58 --- /dev/null +++ b/src/FlatSharp/OffsetModel.cs @@ -0,0 +1,83 @@ +/* + * Copyright 2020 James Courtney + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace FlatSharp.TypeModel +{ + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Diagnostics.CodeAnalysis; + using System.Reflection; + + /// + /// Common or shared type model properties. + /// + public class OffsetModel + { + public static readonly OffsetModel Default = new(4); + + public int OffsetSize; + public int FileIdentifierSize; + public bool StrictFileIdentifierSize; + + public OffsetModel(int offsetSize, int fileIdentifierSize, bool strictFileIdentifierSize) + { + OffsetSize = offsetSize; + FileIdentifierSize = fileIdentifierSize; + StrictFileIdentifierSize = strictFileIdentifierSize; + } + + public OffsetModel(int offsetSize, int fileIdentifierSize) : this(offsetSize, fileIdentifierSize, fileIdentifierSize == 4) + { + } + + public OffsetModel(int offsetSize) : this(offsetSize, offsetSize, offsetSize == 4) + { + } + + public OffsetModel(int? offsetSize, int? fileIdentifierSize, bool? strictFileIdentifierSize) + { + OffsetSize = offsetSize ?? 4; + FileIdentifierSize = fileIdentifierSize ?? OffsetSize; + StrictFileIdentifierSize = strictFileIdentifierSize ?? FileIdentifierSize == 4; + } + + public void ValidateFileIdentifier(ref string? fileIdentifier) + { + if (string.IsNullOrEmpty(fileIdentifier)) return; + + if (fileIdentifier.Length != FileIdentifierSize) + { + if (StrictFileIdentifierSize) + { + throw new InvalidFlatBufferDefinitionException( + $"File identifier '{fileIdentifier}' is invalid. FileIdentifiers must be exactly {FileIdentifierSize} ASCII characters."); + } + + fileIdentifier = fileIdentifier.Length > FileIdentifierSize + ? fileIdentifier.Substring(0, FileIdentifierSize) + : fileIdentifier.PadRight(FileIdentifierSize, '\0'); + } + + foreach (var c in fileIdentifier) + { + if (c < 128) continue; + + throw new InvalidFlatBufferDefinitionException($"File identifier '{fileIdentifier}' contains non-ASCII characters. Character '{c}' is invalid."); + } + } + } +} diff --git a/src/FlatSharp/TypeModel/EnumTypeModel.cs b/src/FlatSharp/TypeModel/EnumTypeModel.cs index d071d342..23ea7ab3 100644 --- a/src/FlatSharp/TypeModel/EnumTypeModel.cs +++ b/src/FlatSharp/TypeModel/EnumTypeModel.cs @@ -30,7 +30,7 @@ public class EnumTypeModel : RuntimeTypeModel { private ITypeModel underlyingTypeModel; - internal EnumTypeModel(Type type, TypeModelContainer typeModelContainer) : base(type, typeModelContainer) + internal EnumTypeModel(Type type, TypeModelContainer typeModelContainer) : base(type, typeModelContainer, typeModelContainer.OffsetModel) { this.underlyingTypeModel = null!; } diff --git a/src/FlatSharp/TypeModel/ITypeModel.cs b/src/FlatSharp/TypeModel/ITypeModel.cs index 5c06287c..2061ee78 100644 --- a/src/FlatSharp/TypeModel/ITypeModel.cs +++ b/src/FlatSharp/TypeModel/ITypeModel.cs @@ -27,6 +27,8 @@ namespace FlatSharp.TypeModel /// public interface ITypeModel { + OffsetModel OffsetModel { get; } + /// /// Gets the schema element type that this type model represents. Note that this is not a 1:1 relationship with the type of class. There can /// be multiple implementations of ITypeModel that satisfy a particular schema type. diff --git a/src/FlatSharp/TypeModel/ItemMemberModel.cs b/src/FlatSharp/TypeModel/ItemMemberModel.cs index 55c4ad37..9f8c4379 100644 --- a/src/FlatSharp/TypeModel/ItemMemberModel.cs +++ b/src/FlatSharp/TypeModel/ItemMemberModel.cs @@ -43,6 +43,7 @@ protected ItemMemberModel( var setMethod = propertyInfo.SetMethod; this.ItemTypeModel = propertyModel; + this.OffsetModel = propertyModel.OffsetModel; this.PropertyInfo = propertyInfo; this.Index = attribute.Index; this.CustomAccessor = propertyInfo.GetFlatBufferMetadataOrNull(FlatBufferMetadataKind.Accessor); @@ -102,6 +103,8 @@ protected ItemMemberModel( } } } + + public OffsetModel OffsetModel { get; } private static bool CanBeOverridden(MethodInfo method) { diff --git a/src/FlatSharp/TypeModel/NullableTypeModel.cs b/src/FlatSharp/TypeModel/NullableTypeModel.cs index 7d14a5ce..5295d0df 100644 --- a/src/FlatSharp/TypeModel/NullableTypeModel.cs +++ b/src/FlatSharp/TypeModel/NullableTypeModel.cs @@ -29,7 +29,7 @@ public class NullableTypeModel : RuntimeTypeModel private Type underlyingType; private ITypeModel underlyingTypeModel; - internal NullableTypeModel(TypeModelContainer container, Type type) : base(type, container) + internal NullableTypeModel(TypeModelContainer container, Type type) : base(type, container, container.OffsetModel) { this.underlyingType = null!; this.underlyingTypeModel = null!; diff --git a/src/FlatSharp/TypeModel/RuntimeTypeModel.cs b/src/FlatSharp/TypeModel/RuntimeTypeModel.cs index eaf9489a..9ed7af46 100644 --- a/src/FlatSharp/TypeModel/RuntimeTypeModel.cs +++ b/src/FlatSharp/TypeModel/RuntimeTypeModel.cs @@ -31,12 +31,18 @@ public abstract class RuntimeTypeModel : ITypeModel { protected readonly TypeModelContainer typeModelContainer; - internal RuntimeTypeModel(Type clrType, TypeModelContainer typeModelContainer) + internal RuntimeTypeModel(Type clrType, TypeModelContainer typeModelContainer, OffsetModel offsetModel) { this.ClrType = clrType; this.typeModelContainer = typeModelContainer; + OffsetModel = offsetModel; } + /// + /// The offset model for serialization. + /// + public OffsetModel OffsetModel { get; } + /// /// Initializes this runtime type model instance. /// @@ -122,7 +128,15 @@ public virtual bool ValidateDefaultValue(object defaultValue) /// internal static ITypeModel CreateFrom(Type type) { - return TypeModelContainer.CreateDefault().CreateTypeModel(type); + return CreateFrom(TypeModelContainer.CreateDefault(), type); + } + + /// + /// Gets or creates a runtime type model from the given type. This is only used in test cases any more. + /// + internal static ITypeModel CreateFrom(TypeModelContainer container, Type type) + { + return container.CreateTypeModel(type); } public abstract CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeGenContext context); diff --git a/src/FlatSharp/TypeModel/ScalarTypeModel.cs b/src/FlatSharp/TypeModel/ScalarTypeModel.cs index 05344a79..ae890269 100644 --- a/src/FlatSharp/TypeModel/ScalarTypeModel.cs +++ b/src/FlatSharp/TypeModel/ScalarTypeModel.cs @@ -31,7 +31,7 @@ public abstract class ScalarTypeModel : RuntimeTypeModel internal ScalarTypeModel( TypeModelContainer container, Type type, - int size) : base(type, container) + int size) : base(type, container, container.OffsetModel) { this.size = size; } diff --git a/src/FlatSharp/TypeModel/SharedStringTypeModel.cs b/src/FlatSharp/TypeModel/SharedStringTypeModel.cs index 9aa0199b..8ed56857 100644 --- a/src/FlatSharp/TypeModel/SharedStringTypeModel.cs +++ b/src/FlatSharp/TypeModel/SharedStringTypeModel.cs @@ -25,7 +25,7 @@ namespace FlatSharp.TypeModel /// public class SharedStringTypeModel : RuntimeTypeModel, ITypeModel { - internal SharedStringTypeModel(TypeModelContainer container) : base(typeof(SharedString), container) + internal SharedStringTypeModel(TypeModelContainer container) : base(typeof(SharedString), container, container.OffsetModel) { } diff --git a/src/FlatSharp/TypeModel/StringTypeModel.cs b/src/FlatSharp/TypeModel/StringTypeModel.cs index a4cba266..d76d3f5c 100644 --- a/src/FlatSharp/TypeModel/StringTypeModel.cs +++ b/src/FlatSharp/TypeModel/StringTypeModel.cs @@ -25,8 +25,7 @@ namespace FlatSharp.TypeModel /// public class StringTypeModel : RuntimeTypeModel { - internal StringTypeModel(TypeModelContainer container) : base(typeof(string), container) - { + internal StringTypeModel(TypeModelContainer container) : base(typeof(string), container, container.OffsetModel) { } /// diff --git a/src/FlatSharp/TypeModel/StructTypeModel.cs b/src/FlatSharp/TypeModel/StructTypeModel.cs index 376a841b..86333053 100644 --- a/src/FlatSharp/TypeModel/StructTypeModel.cs +++ b/src/FlatSharp/TypeModel/StructTypeModel.cs @@ -37,7 +37,7 @@ public class StructTypeModel : RuntimeTypeModel private MethodInfo? onDeserializeMethod; private FlatBufferStructAttribute attribute = null!; - internal StructTypeModel(Type clrType, TypeModelContainer container) : base(clrType, container) + internal StructTypeModel(Type clrType, TypeModelContainer container) : base(clrType, container, container.OffsetModel) { } diff --git a/src/FlatSharp/TypeModel/TableMemberModel.cs b/src/FlatSharp/TypeModel/TableMemberModel.cs index dd5d3952..97b25e75 100644 --- a/src/FlatSharp/TypeModel/TableMemberModel.cs +++ b/src/FlatSharp/TypeModel/TableMemberModel.cs @@ -118,7 +118,7 @@ private string CreateSingleWidthReadItemBody(string parseItemMethodName, string {this.GetNotPresentStatement()} }} - ushort relativeOffset = buffer.ReadUShort({vtableLocationVariableName} + {4 + (2 * this.Index)}); + ushort relativeOffset = buffer.ReadUShort({vtableLocationVariableName} + {OffsetModel.OffsetSize + (2 * this.Index)}); if (relativeOffset == 0) {{ {this.GetNotPresentStatement()} @@ -140,7 +140,7 @@ private string CreateWideReadItemBody(string parseItemMethodName, string bufferV int idx = this.Index + i; relativeOffsets.Add($@" - ushort relativeOffset{i} = buffer.ReadUShort({vtableLocationVariableName} + {4 + (2 * idx)}); + ushort relativeOffset{i} = buffer.ReadUShort({vtableLocationVariableName} + {OffsetModel.OffsetSize + (2 * idx)}); if (relativeOffset{i} == 0) {{ {this.GetNotPresentStatement()} diff --git a/src/FlatSharp/TypeModel/TableTypeModel.cs b/src/FlatSharp/TypeModel/TableTypeModel.cs index 4aa8ffb0..43a072ea 100644 --- a/src/FlatSharp/TypeModel/TableTypeModel.cs +++ b/src/FlatSharp/TypeModel/TableTypeModel.cs @@ -31,7 +31,6 @@ namespace FlatSharp.TypeModel public class TableTypeModel : RuntimeTypeModel { internal const string OnDeserializedMethodName = "OnFlatSharpDeserialized"; - private const int FileIdentifierSize = 4; private readonly string ParseClassName = "tableReader_" + Guid.NewGuid().ToString("n"); @@ -49,7 +48,7 @@ public class TableTypeModel : RuntimeTypeModel private FlatBufferTableAttribute attribute = null!; private readonly string tableReaderClassName = "tableReader_" + Guid.NewGuid().ToString("n"); - internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : base(clrType, typeModelProvider) + internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : base(clrType, typeModelProvider, typeModelProvider.OffsetModel) { } @@ -119,8 +118,8 @@ internal TableTypeModel(Type clrType, TypeModelContainer typeModelProvider) : ba /// internal int NonPaddedMaxTableInlineSize { - // add sizeof(int) for soffset_t to vtable. - get => this.IndexToMemberMap.Values.Sum(x => x.ItemTypeModel.MaxInlineSize) + sizeof(int); + // add TypeModel.OffsetSize for soffset_t to vtable. + get => this.IndexToMemberMap.Values.Sum(x => x.ItemTypeModel.MaxInlineSize) + OffsetModel.OffsetSize; } public override ConstructorInfo? PreferredSubclassConstructor => this.preferredConstructor; @@ -135,7 +134,7 @@ public override void Initialize() this.attribute = attr; } - ValidateFileIdentifier(this.attribute.FileIdentifier); + OffsetModel.ValidateFileIdentifier(ref this.attribute.FileIdentifier); EnsureClassCanBeInheritedByOutsideAssembly(this.ClrType, out this.preferredConstructor); this.onDeserializeMethod = ValidateOnDeserializedMethod(this); @@ -363,26 +362,6 @@ internal static void EnsureClassCanBeInheritedByOutsideAssembly(Type type, out C } } - private static void ValidateFileIdentifier(string? fileIdentifier) - { - if (!string.IsNullOrEmpty(fileIdentifier)) - { - if (fileIdentifier.Length != FileIdentifierSize) - { - throw new InvalidFlatBufferDefinitionException($"File identifier '{fileIdentifier}' is invalid. FileIdentifiers must be exactly {FileIdentifierSize} ASCII characters."); - } - - for (int i = 0; i < fileIdentifier.Length; ++i) - { - char c = fileIdentifier[i]; - if (c >= 128) - { - throw new InvalidFlatBufferDefinitionException($"File identifier '{fileIdentifier}' contains non-ASCII characters. Character '{c}' is invalid."); - } - } - } - } - public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeGenContext context) { var type = this.ClrType; @@ -483,12 +462,12 @@ public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeG // Start by asking for the worst-case number of bytes from the serializationcontext. string methodStart = $@" - int tableStart = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateSpace)}({maxInlineSize}, sizeof(int)); + int tableStart = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateSpace)}({maxInlineSize}, {OffsetModel.OffsetSize}); {context.SpanWriterVariableName}.{nameof(SpanWriterExtensions.WriteUOffset)}({context.SpanVariableName}, {context.OffsetVariableName}, tableStart); - int currentOffset = tableStart + sizeof(int); // skip past vtable soffset_t. + int currentOffset = tableStart + {OffsetModel.OffsetSize}; // skip past vtable soffset_t. int vtableLength = {minVtableLength}; - Span vtable = stackalloc byte[{4 + 2 * (maxIndex + 1)}]; + Span vtable = stackalloc byte[{OffsetModel.OffsetSize + 2 * (maxIndex + 1)}]; "; List body = new(); @@ -745,8 +724,8 @@ public override bool TryGetTableKeyMember([NotNullWhen(true)] out TableMemberMod return tableMember != null; } - private int GetVTableLength(int index) => 4 + (2 * (index + 1)); + private int GetVTableLength(int index) => OffsetModel.OffsetSize + (2 * (index + 1)); - private int GetVTablePosition(int index) => 4 + (2 * index); + private int GetVTablePosition(int index) => OffsetModel.OffsetSize + (2 * index); } } diff --git a/src/FlatSharp/TypeModel/TypeFacadeTypeModelProvider.cs b/src/FlatSharp/TypeModel/TypeFacadeTypeModelProvider.cs index db07426c..958acd11 100644 --- a/src/FlatSharp/TypeModel/TypeFacadeTypeModelProvider.cs +++ b/src/FlatSharp/TypeModel/TypeFacadeTypeModelProvider.cs @@ -32,9 +32,9 @@ internal class TypeFacadeTypeModelProvider : ITy private readonly ITypeModel model; public TypeFacadeTypeModelProvider( - ITypeModel underlyingModel) + ITypeModel underlyingModel, OffsetModel offsetModel) { - this.model = new TypeFacadeTypeModel(underlyingModel); + this.model = new TypeFacadeTypeModel(underlyingModel, offsetModel); } public bool TryCreateTypeModel(TypeModelContainer container, Type type, [NotNullWhen(true)] out ITypeModel? typeModel) @@ -60,12 +60,16 @@ private class TypeFacadeTypeModel : ITypeModel private readonly ITypeModel underlyingModel; public TypeFacadeTypeModel( - ITypeModel underlyingModel) + ITypeModel underlyingModel, + OffsetModel offsetModel) { this.underlyingModel = underlyingModel; + this.OffsetModel = offsetModel; } public FlatBufferSchemaType SchemaType => this.underlyingModel.SchemaType; + + public OffsetModel OffsetModel { get; } public Type ClrType => typeof(TType); diff --git a/src/FlatSharp/TypeModel/TypeModelContainer.cs b/src/FlatSharp/TypeModel/TypeModelContainer.cs index 5f7e742f..faeb880f 100644 --- a/src/FlatSharp/TypeModel/TypeModelContainer.cs +++ b/src/FlatSharp/TypeModel/TypeModelContainer.cs @@ -36,8 +36,9 @@ public sealed class TypeModelContainer private readonly ConcurrentDictionary cache = new ConcurrentDictionary(); private readonly List providers = new List(); - private TypeModelContainer() + private TypeModelContainer(OffsetModel offsetModel) { + OffsetModel = offsetModel; } /// @@ -45,7 +46,7 @@ private TypeModelContainer() /// public static TypeModelContainer CreateEmpty() { - return new TypeModelContainer(); + return new TypeModelContainer(OffsetModel.Default); } /// @@ -53,12 +54,25 @@ public static TypeModelContainer CreateEmpty() /// public static TypeModelContainer CreateDefault() { - var container = new TypeModelContainer(); + var container = new TypeModelContainer(OffsetModel.Default); container.RegisterProvider(new ScalarTypeModelProvider()); container.RegisterProvider(new FlatSharpTypeModelProvider()); return container; } + /// + /// Creates a FlatSharp type model container with default support but with a custom offset model. + /// + public static TypeModelContainer CreateDefault(OffsetModel offsetModel) + { + var container = new TypeModelContainer(offsetModel); + container.RegisterProvider(new ScalarTypeModelProvider()); + container.RegisterProvider(new FlatSharpTypeModelProvider()); + return container; + } + + public OffsetModel OffsetModel { get; } + /// /// Registers a type facade for the given type. Facades are a convenience mechanism to /// expose types to FlatSharp that are based on some well-known underlying type. @@ -94,12 +108,12 @@ public void RegisterTypeFacade(bool th if (typeof(TUnderlyingType).IsValueType && Nullable.GetUnderlyingType(typeof(TUnderlyingType)) == null) { // non-nullable value type: omit the null check - provider = new TypeFacadeTypeModelProvider(model); + provider = new TypeFacadeTypeModelProvider(model, OffsetModel); } else { // reference type or nullable value type. - provider = new TypeFacadeTypeModelProvider, TUnderlyingType, TFacadeType>(model); + provider = new TypeFacadeTypeModelProvider, TUnderlyingType, TFacadeType>(model, OffsetModel); } // add first. diff --git a/src/FlatSharp/TypeModel/UnionTypeModel.cs b/src/FlatSharp/TypeModel/UnionTypeModel.cs index 19a28de9..21204070 100644 --- a/src/FlatSharp/TypeModel/UnionTypeModel.cs +++ b/src/FlatSharp/TypeModel/UnionTypeModel.cs @@ -29,7 +29,7 @@ public class UnionTypeModel : RuntimeTypeModel { private ITypeModel[] memberTypeModels; - internal UnionTypeModel(Type unionType, TypeModelContainer provider) : base(unionType, provider) + internal UnionTypeModel(Type unionType, TypeModelContainer provider) : base(unionType, provider, provider.OffsetModel) { this.memberTypeModels = null!; } diff --git a/src/FlatSharp/TypeModel/ValueStructTypeModel.cs b/src/FlatSharp/TypeModel/ValueStructTypeModel.cs index c5d393c6..c40e5489 100644 --- a/src/FlatSharp/TypeModel/ValueStructTypeModel.cs +++ b/src/FlatSharp/TypeModel/ValueStructTypeModel.cs @@ -36,7 +36,7 @@ public class ValueStructTypeModel : RuntimeTypeModel private int inlineSize; private int maxAlignment = 1; - internal ValueStructTypeModel(Type clrType, TypeModelContainer container) : base(clrType, container) + internal ValueStructTypeModel(Type clrType, TypeModelContainer container) : base(clrType, container, container.OffsetModel) { } diff --git a/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs b/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs index 48d460dd..8b48c98a 100644 --- a/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs +++ b/src/FlatSharp/TypeModel/Vectors/BaseVectorTypeModel.cs @@ -29,7 +29,7 @@ public abstract class BaseVectorTypeModel : RuntimeTypeModel // count of items + padding(uoffset_t); protected static readonly int VectorMinSize = sizeof(uint) + SerializationHelpers.GetMaxPadding(sizeof(uint)); - internal BaseVectorTypeModel(Type vectorType, TypeModelContainer provider) : base(vectorType, provider) + internal BaseVectorTypeModel(Type vectorType, TypeModelContainer provider) : base(vectorType, provider, provider.OffsetModel) { this.ItemTypeModel = null!; } @@ -160,7 +160,7 @@ public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeG int vectorOffset = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateVector)}({itemTypeModel.PhysicalLayout[0].Alignment}, count, {this.PaddedMemberInlineSize}); {context.SpanWriterVariableName}.{nameof(SpanWriterExtensions.WriteUOffset)}({context.SpanVariableName}, {context.OffsetVariableName}, vectorOffset); {context.SpanWriterVariableName}.{nameof(SpanWriter.WriteInt)}({context.SpanVariableName}, count, vectorOffset); - vectorOffset += sizeof(int); + vectorOffset += {OffsetModel.OffsetSize}; {this.CreateLoop(context.Options, context.ValueVariableName, "count", "current", loopBody)}"; diff --git a/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs b/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs index 6942e56d..1aaac8a4 100644 --- a/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs +++ b/src/FlatSharp/TypeModel/VectorsOfUnion/BaseVectorOfUnionTypeModel.cs @@ -26,7 +26,7 @@ namespace FlatSharp.TypeModel /// public abstract class BaseVectorOfUnionTypeModel : RuntimeTypeModel { - internal BaseVectorOfUnionTypeModel(Type vectorType, TypeModelContainer provider) : base(vectorType, provider) + internal BaseVectorOfUnionTypeModel(Type vectorType, TypeModelContainer provider) : base(vectorType, provider, provider.OffsetModel) { this.ItemTypeModel = null!; } @@ -104,12 +104,12 @@ public override bool TryGetUnderlyingVectorType([NotNullWhen(true)] out ITypeMod public override CodeGeneratedMethod CreateGetMaxSizeMethodBody(GetMaxSizeCodeGenContext context) { // 2 vectors. - int baseSize = 2 * (sizeof(int) + SerializationHelpers.GetMaxPadding(sizeof(int))); + int baseSize = 2 * (OffsetModel.OffsetSize + SerializationHelpers.GetMaxPadding(OffsetModel.OffsetSize)); string body = $@" int count = {context.ValueVariableName}.{this.LengthPropertyName}; - int length = {baseSize} + (count * (sizeof(byte) + sizeof(int))); + int length = {baseSize} + (count * (sizeof(byte) + {OffsetModel.OffsetSize})); for (int i = 0; i < count; ++i) {{ @@ -139,12 +139,12 @@ public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeG int discriminatorVectorOffset = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateVector)}(sizeof(byte), count, sizeof(byte)); {context.SpanWriterVariableName}.{nameof(SpanWriterExtensions.WriteUOffset)}({context.SpanVariableName}, {context.OffsetVariableName}.offset0, discriminatorVectorOffset); {context.SpanWriterVariableName}.{nameof(SpanWriter.WriteInt)}({context.SpanVariableName}, count, discriminatorVectorOffset); - discriminatorVectorOffset += sizeof(int); + discriminatorVectorOffset += {OffsetModel.OffsetSize}; - int offsetVectorOffset = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateVector)}(sizeof(int), count, sizeof(int)); + int offsetVectorOffset = {context.SerializationContextVariableName}.{nameof(SerializationContext.AllocateVector)}({OffsetModel.OffsetSize}, count, {OffsetModel.OffsetSize}); {context.SpanWriterVariableName}.{nameof(SpanWriterExtensions.WriteUOffset)}({context.SpanVariableName}, {context.OffsetVariableName}.offset1, offsetVectorOffset); {context.SpanWriterVariableName}.{nameof(SpanWriter.WriteInt)}({context.SpanVariableName}, count, offsetVectorOffset); - offsetVectorOffset += sizeof(int); + offsetVectorOffset += {OffsetModel.OffsetSize}; for (int i = 0; i < count; ++i) {{ @@ -155,7 +155,7 @@ public override CodeGeneratedMethod CreateSerializeMethodBody(SerializationCodeG {innerContext.GetSerializeInvocation(itemTypeModel.ClrType)}; discriminatorVectorOffset++; - offsetVectorOffset += sizeof(int); + offsetVectorOffset += {OffsetModel.OffsetSize}; }}"; return new CodeGeneratedMethod(body); diff --git a/src/Tests/FlatSharpCompilerTests/FileIdentifierTests.cs b/src/Tests/FlatSharpCompilerTests/FileIdentifierTests.cs index b36b24b6..ee48307e 100644 --- a/src/Tests/FlatSharpCompilerTests/FileIdentifierTests.cs +++ b/src/Tests/FlatSharpCompilerTests/FileIdentifierTests.cs @@ -14,6 +14,9 @@ * limitations under the License. */ +using System.Collections.Generic; +using FlatSharp.TypeModel; + namespace FlatSharpTests.Compiler { using System; @@ -27,8 +30,17 @@ namespace FlatSharpTests.Compiler public class FileIdentifierTests { - [Fact] - public void Multiple_Root_Types_Different_Namespaces() + public static readonly IEnumerable OffsetSizes = new[] + { + new object[] { 1 }, + new object[] { 2 }, + new object[] { 4 }, + new object[] { 8 } + }; + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Multiple_Root_Types_Different_Namespaces(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -41,12 +53,13 @@ namespace FileIdTests.B; file_identifier ""food""; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='Duplicate root types: 'Table' and 'TableB'.', Scope=$", ex.Errors[0]); } - [Fact] - public void Multiple_Root_Types_Same_Namespace() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Multiple_Root_Types_Same_Namespace(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -56,12 +69,13 @@ namespace FileIdTests.A; root_type Table; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='Duplicate root types: 'Table' and 'Table'.', Scope=$", ex.Errors[0]); } - [Fact] - public void Multiple_File_Identifiers_Different_Namespaces() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Multiple_File_Identifiers_Different_Namespaces(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -74,12 +88,13 @@ namespace FileIdTests.B; file_identifier ""food""; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize})); Assert.StartsWith("Message='Duplicate file identifiers: 'doof' and 'food'.', Scope=$", ex.Errors[0]); } - [Fact] - public void Multiple_File_Identifiers_Same_Namespace() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Multiple_File_Identifiers_Same_Namespace(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -89,12 +104,13 @@ namespace FileIdTests.A; root_type Table; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='Duplicate file identifiers: 'food' and 'doof'.', Scope=$", ex.Errors[0]); } - [Fact] - public void Unknown_Root_Type_No_File_Identifier() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Unknown_Root_Type_No_File_Identifier(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -102,12 +118,13 @@ namespace FileIdTests.A; root_type Something; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='Unable to resolve root_type 'Something'.', Scope=$", ex.Errors[0]); } - [Fact] - public void Struct_Root_Type() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Struct_Root_Type(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -116,12 +133,13 @@ struct Struct {{ foo:int; }} root_type Struct; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='root_type 'Struct' does not reference a table.', Scope=$", ex.Errors[0]); } - [Fact] - public void Enum_Root_Type() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Enum_Root_Type(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -130,12 +148,13 @@ enum Enum : ubyte {{ Foo, Bar, Baz }} root_type Enum; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='root_type 'Enum' does not reference a table.', Scope=$", ex.Errors[0]); } - [Fact] - public void Unknown_Root_Type_With_File_Identifier() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Unknown_Root_Type_With_File_Identifier(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -144,12 +163,13 @@ namespace FileIdTests.A; file_identifier ""abcd""; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='Unable to resolve root_type 'Something'.', Scope=$", ex.Errors[0]); } - - [Fact] - public void File_Identifier_With_No_Root_Type() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void File_Identifier_With_No_Root_Type(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -157,13 +177,14 @@ namespace FileIdTests.A; file_identifier ""abcd""; "; - Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new()); + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); Assert.Null( asm.GetType("FileIdTests.A.Table").GetCustomAttribute().FileIdentifier); } - - [Fact] - public void Root_Type_With_No_File_Identifier() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Root_Type_With_No_File_Identifier(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -171,13 +192,14 @@ namespace FileIdTests.A; root_type Table; "; - Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new()); + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); Assert.Null( asm.GetType("FileIdTests.A.Table").GetCustomAttribute().FileIdentifier); } - - [Fact] - public void File_Identifier_With_Manually_Specified_Id() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void File_Identifier_With_Manually_Specified_Id(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -186,14 +208,18 @@ namespace FileIdTests.A; root_type Table; "; - Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new()); + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); + + string fileIdentifier = "AbCd"; + if (offsetSize != 4) new OffsetModel(offsetSize).ValidateFileIdentifier(ref fileIdentifier); Assert.Equal( - "AbCd", + fileIdentifier, asm.GetType("FileIdTests.A.Table").GetCustomAttribute().FileIdentifier); } - - [Fact] - public void File_Identifier_With_Manually_Specified_Id_Conflict() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void File_Identifier_With_Manually_Specified_Id_Conflict(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -202,12 +228,13 @@ namespace FileIdTests.A; root_type Table; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); + var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); Assert.StartsWith("Message='root_type 'Table' has conflicting file identifiers: 'AbCd' and 'abcd'.', Scope=$", ex.Errors[0]); } - - [Fact] - public void Manually_Specified_Id_With_No_File_Id() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Manually_Specified_Id_With_No_File_Id(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -215,14 +242,17 @@ namespace FileIdTests.A; root_type Table; "; - Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new()); + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); + string fileIdentifier = "AbCd"; + if (offsetSize != 4) new OffsetModel(offsetSize).ValidateFileIdentifier(ref fileIdentifier); Assert.Equal( - "AbCd", + fileIdentifier, asm.GetType("FileIdTests.A.Table").GetCustomAttribute().FileIdentifier); } - - [Fact] - public void Manually_Specified_Id_With_No_File_Id_Unquoted() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Manually_Specified_Id_With_No_File_Id_Unquoted(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -230,14 +260,17 @@ namespace FileIdTests.A; root_type Table; "; - Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new()); + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); + string fileIdentifier = "AbCd"; + if (offsetSize != 4) new OffsetModel(offsetSize).ValidateFileIdentifier(ref fileIdentifier); Assert.Equal( - "AbCd", + fileIdentifier, asm.GetType("FileIdTests.A.Table").GetCustomAttribute().FileIdentifier); } - - [Fact] - public void Manually_Specified_Id_TooLong() + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void Manually_Specified_Id_TooLong(int offsetSize) { string schema = $@" namespace FileIdTests.A; @@ -245,8 +278,21 @@ namespace FileIdTests.A; root_type Table; "; - var ex = Assert.Throws(() => FlatSharpCompiler.CompileAndLoadAssembly(schema, new())); - Assert.Equal("Message='File identifier 'AbCdefg' is invalid. FileIdentifiers must be exactly 4 ASCII characters.', Scope=$.", ex.Errors[0]); + if (offsetSize == 4) + { + var ex = Assert.Throws(() + => FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize })); + Assert.Equal("Message='File identifier 'AbCdefg' is invalid. FileIdentifiers must be exactly 4 ASCII characters.', Scope=$..FileIdTests.A.Table", + ex.Errors[0]); + } + else + { + Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly(schema, new() { OffsetSize = offsetSize }); + string fileIdentifier = "AbCdefg"; + new OffsetModel(offsetSize).ValidateFileIdentifier(ref fileIdentifier); + Assert.Equal(fileIdentifier, + asm.GetType("FileIdTests.A.Table")!.GetCustomAttribute()!.FileIdentifier); + } } } } diff --git a/src/Tests/FlatSharpCompilerTests/StructVectorTests.cs b/src/Tests/FlatSharpCompilerTests/StructVectorTests.cs index 4db4ac45..936aec2c 100644 --- a/src/Tests/FlatSharpCompilerTests/StructVectorTests.cs +++ b/src/Tests/FlatSharpCompilerTests/StructVectorTests.cs @@ -31,49 +31,71 @@ namespace FlatSharpTests.Compiler public class StructVectorTests { - [Fact] - public void StructVector_Byte_NegativeLength() - => Assert.Throws(() => this.RunTest("ubyte", -3, FlatBufferDeserializationOption.Greedy)); - - [Fact] - public void StructVector_Byte_ZeroLength() - => Assert.Throws(() => this.RunTest("ubyte", 0, FlatBufferDeserializationOption.Greedy)); - - [Fact] - public void StructVector_Byte() => this.RunTest("ubyte", 1, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_SByte() => this.RunTest("byte", 2, FlatBufferDeserializationOption.GreedyMutable); - - [Fact] - public void StructVector_Bool() => this.RunTest("bool", 3, FlatBufferDeserializationOption.VectorCache); - - [Fact] - public void StructVector_UShort() => this.RunTest("ushort", 4, FlatBufferDeserializationOption.VectorCacheMutable); - - [Fact] - public void StructVector_Short() => this.RunTest("short", 5, FlatBufferDeserializationOption.PropertyCache); - - [Fact] - public void StructVector_UInt() => this.RunTest("uint", 6, FlatBufferDeserializationOption.Lazy); - - [Fact] - public void StructVector_Int() => this.RunTest("int", 7, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_ULong() => this.RunTest("ulong", 8, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_Long() => this.RunTest("long", 9, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_Float() => this.RunTest("float", 10, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_Double() => this.RunTest("double", 11, FlatBufferDeserializationOption.Greedy); - - [Fact] - public void StructVector_InvalidType() + public static readonly IEnumerable OffsetSizes = new[] + { + new object[] { 1 }, + new object[] { 2 }, + new object[] { 4 }, + new object[] { 8 } + }; + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Byte_NegativeLength(int offsetSize) + => Assert.Throws(() => this.RunTest("ubyte", -3, FlatBufferDeserializationOption.Greedy, offsetSize)); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Byte_ZeroLength(int offsetSize) + => Assert.Throws(() => this.RunTest("ubyte", 0, FlatBufferDeserializationOption.Greedy, offsetSize)); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Byte(int offsetSize) => this.RunTest("ubyte", 1, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_SByte(int offsetSize) => this.RunTest("byte", 2, FlatBufferDeserializationOption.GreedyMutable, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Bool(int offsetSize) => this.RunTest("bool", 3, FlatBufferDeserializationOption.VectorCache, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_UShort(int offsetSize) => this.RunTest("ushort", 4, FlatBufferDeserializationOption.VectorCacheMutable, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Short(int offsetSize) => this.RunTest("short", 5, FlatBufferDeserializationOption.PropertyCache, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_UInt(int offsetSize) => this.RunTest("uint", 6, FlatBufferDeserializationOption.Lazy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Int(int offsetSize) => this.RunTest("int", 7, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_ULong(int offsetSize) => this.RunTest("ulong", 8, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Long(int offsetSize) => this.RunTest("long", 9, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Float(int offsetSize) => this.RunTest("float", 10, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_Double(int offsetSize) => this.RunTest("double", 11, FlatBufferDeserializationOption.Greedy, offsetSize); + + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_InvalidType(int offsetSize) { string schema = $@" namespace StructVectorTests; @@ -88,13 +110,14 @@ struct Foo {{ var ex = Assert.Throws( () => FlatSharpCompiler.CompileAndLoadAssembly( schema, - new())); + new() { OffsetSize = offsetSize })); Assert.Contains("Unable to resolve struct vector type 'Bar'.", ex.Message); } - [Fact] - public void StructVector_UnsafeVectorOnReferenceType() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_UnsafeVectorOnReferenceType(int offsetSize) { string schema = $@" namespace StructVectorTests; @@ -111,8 +134,9 @@ struct Foo {{ Assert.Contains($"Field '__flatsharp__V_0' declares the '{MetadataKeys.UnsafeValueStructVector}' attribute. Unsafe struct vectors are only supported on value structs.", ex.Message); } - [Fact] - public void StructVector_NestedStruct() + [Theory] + [MemberData(nameof(OffsetSizes))] + public void StructVector_NestedStruct(int offsetSize) { int length = 7; @@ -129,7 +153,7 @@ struct Foo {{ Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly( schema, - new()); + new() { OffsetSize = offsetSize }); Type tableType = asm.GetType("StructVectorTests.Table"); Type fooType = asm.GetType("StructVectorTests.Foo"); @@ -191,7 +215,7 @@ struct Foo {{ } } - private void RunTest(string fbsType, int length, FlatBufferDeserializationOption option) where T : struct + private void RunTest(string fbsType, int length, FlatBufferDeserializationOption option, int offsetSize) where T : struct { string schema = $@" namespace StructVectorTests; @@ -205,7 +229,7 @@ struct Foo {{ Assembly asm = FlatSharpCompiler.CompileAndLoadAssembly( schema, - new()); + new() { OffsetSize = offsetSize }); Type tableType = asm.GetType("StructVectorTests.Table"); Type fooType = asm.GetType("StructVectorTests.Foo"); diff --git a/src/common.props b/src/common.props index 789ed2b8..5f3f36d8 100644 --- a/src/common.props +++ b/src/common.props @@ -8,9 +8,9 @@ true - 5.7.1 + 5.7.2-pre-StirlingLabs $(VersionMajor) - $(Version) + 5.7.2 James Courtney FlatSharp is an idiomatic implementation of the FlatBuffer binary format. 2021