internal static string GetDefaultValueToken(TableMemberModel memberModel) { var itemTypeModel = memberModel.ItemTypeModel; var clrType = itemTypeModel.ClrType; string defaultValue = $"default({GetCompilableTypeName(memberModel.ItemTypeModel.ClrType)})"; if (memberModel.HasDefaultValue) { if (BuiltInType.BuiltInScalars.TryGetValue(clrType, out IBuiltInScalarType builtInType)) { return(builtInType.FormatObject(memberModel.DefaultValue)); } else if (clrType.IsEnum) { return($"{CSharpHelpers.GetCompilableTypeName(clrType)}.{memberModel.DefaultValue}"); } else { throw new InvalidOperationException("Unexpected default value type: " + clrType.FullName); } } return(defaultValue); }
/// <summary> /// Generates a special property getter for union types. This stems from /// the fact that unions occupy two spots in the table's vtable to deserialize one /// logical field. This means that the logic to read them must also be special. /// </summary> private GeneratedProperty CreateUnionTableGetter(TableMemberModel memberModel, int index) { Type propertyType = memberModel.ItemTypeModel.ClrType; string defaultValue = CSharpHelpers.GetDefaultValueToken(memberModel); UnionTypeModel unionModel = (UnionTypeModel)memberModel.ItemTypeModel; GeneratedProperty generatedProperty = new GeneratedProperty(this.options, index, memberModel.PropertyInfo); // Start by generating switch cases. The codegen'ed union types have // well-defined constructors for each constituent type, so this .ctor // will always be available. List <string> switchCases = new List <string>(); for (int i = 0; i < unionModel.UnionElementTypeModel.Length; ++i) { var unionMember = unionModel.UnionElementTypeModel[i]; int unionIndex = i + 1; string structOffsetAdjustment = string.Empty; if (unionMember.SchemaType == FlatBufferSchemaType.Struct) { structOffsetAdjustment = $"offsetLocation += buffer.{nameof(InputBuffer.ReadUOffset)}(offsetLocation);"; } string @case = $@" case {unionIndex}: {structOffsetAdjustment} return new {CSharpHelpers.GetCompilableTypeName(unionModel.ClrType)}({this.GetReadInvocation(unionMember.ClrType, "buffer", "offsetLocation")}); "; switchCases.Add(@case); } generatedProperty.ReadValueMethodDefinition = $@" [MethodImpl(MethodImplOptions.AggressiveInlining)] private static {CSharpHelpers.GetCompilableTypeName(propertyType)} {generatedProperty.ReadValueMethodName}(InputBuffer buffer, int offset) {{ int discriminatorLocation = buffer.{nameof(InputBuffer.GetAbsoluteTableFieldLocation)}(offset, {index}); int offsetLocation = buffer.{nameof(InputBuffer.GetAbsoluteTableFieldLocation)}(offset, {index + 1}); if (discriminatorLocation == 0) {{ return {defaultValue}; }} else {{ byte discriminator = buffer.{nameof(InputBuffer.ReadByte)}(discriminatorLocation); if (discriminator == 0 && offsetLocation != 0) throw new System.IO.InvalidDataException(""FlatBuffer union had discriminator set but no offset.""); switch (discriminator) {{ {string.Join("\r\n", switchCases)} default: return {defaultValue}; }} }} }} "; return(generatedProperty); }
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); }
/// <summary> /// Generates a standard getter for a normal vtable entry. /// </summary> private GeneratedProperty CreateStandardTableProperty(TableMemberModel memberModel, int index) { Type propertyType = memberModel.ItemTypeModel.ClrType; string defaultValue = CSharpHelpers.GetDefaultValueToken(memberModel); GeneratedProperty property = new GeneratedProperty(this.options, index, memberModel.PropertyInfo); property.ReadValueMethodDefinition = $@" [MethodImpl(MethodImplOptions.AggressiveInlining)] private static {CSharpHelpers.GetCompilableTypeName(propertyType)} {property.ReadValueMethodName}(InputBuffer buffer, int offset) {{ int absoluteLocation = buffer.{nameof(InputBuffer.GetAbsoluteTableFieldLocation)}(offset, {index}); if (absoluteLocation == 0) {{ return {defaultValue}; }} else {{ return {this.GetReadInvocation(propertyType, "buffer", "absoluteLocation")}; }} }} "; return(property); }
private (string prepareBlock, string serializeBlock) GetStandardSerializeBlocks(int index, TableMemberModel memberModel) { string valueVariableName = $"index{index}Value"; string offsetVariableName = $"index{index}Offset"; string condition = $"if ({valueVariableName} != {CSharpHelpers.GetDefaultValueToken(memberModel)})"; if (memberModel.ItemTypeModel is VectorTypeModel vector && vector.IsMemoryVector) { // Memory is a struct and can't be null, and 0-length vectors are valid. // Therefore, we just need to omit the conditional check entirely. condition = string.Empty; } string prepareBlock = $@" var {valueVariableName} = item.{memberModel.PropertyInfo.Name}; int {offsetVariableName} = 0; {condition} {{ currentOffset += {CSharpHelpers.GetFullMethodName(ReflectedMethods.SerializationHelpers_GetAlignmentErrorMethod)}(currentOffset, {memberModel.ItemTypeModel.Alignment}); {offsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index}, currentOffset - tableStart); currentOffset += {memberModel.ItemTypeModel.InlineSize}; }}"; string serializeBlock = $@" if ({offsetVariableName} != 0) {{ {this.GetSerializeInvocation(memberModel.ItemTypeModel.ClrType, valueVariableName, offsetVariableName)} }}"; return(prepareBlock, serializeBlock); }
private (string prepareBlock, string serializeBlock) GetUnionSerializeBlocks(int index, TableMemberModel memberModel) { UnionTypeModel unionModel = (UnionTypeModel)memberModel.ItemTypeModel; string valueVariableName = $"index{index}Value"; string discriminatorOffsetVariableName = $"index{index}DiscriminatorOffset"; string valueOffsetVariableName = $"index{index}ValueOffset"; string discriminatorValueVariableName = $"index{index}Discriminator"; string prepareBlock = $@" var {valueVariableName} = item.{memberModel.PropertyInfo.Name}; int {discriminatorOffsetVariableName} = 0; int {valueOffsetVariableName} = 0; byte {discriminatorValueVariableName} = 0; if ({valueVariableName} != null && {valueVariableName}.Discriminator != 0) {{ {discriminatorValueVariableName} = {valueVariableName}.Discriminator; {discriminatorOffsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index}, currentOffset - tableStart); currentOffset++; currentOffset += {CSharpHelpers.GetFullMethodName(ReflectedMethods.SerializationHelpers_GetAlignmentErrorMethod)}(currentOffset, sizeof(uint)); {valueOffsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index + 1}, currentOffset - tableStart); currentOffset += sizeof(uint); }}"; List <string> switchCases = new List <string>(); for (int i = 0; i < unionModel.UnionElementTypeModel.Length; ++i) { var elementModel = unionModel.UnionElementTypeModel[i]; var unionIndex = i + 1; string structAdjustment = string.Empty; if (elementModel.SchemaType == FlatBufferSchemaType.Struct) { // Structs are generally written in-line, with the exception of unions. // So, we need to do the normal allocate space dance here, since we're writing // a pointer to a struct. structAdjustment = $@" var writeOffset = context.{nameof(SerializationContext.AllocateSpace)}({elementModel.InlineSize}, {elementModel.Alignment}); writer.{nameof(SpanWriter.WriteUOffset)}(span, {valueOffsetVariableName}, writeOffset, context); {valueOffsetVariableName} = writeOffset;"; } string @case = $@" case {unionIndex}: {{ {structAdjustment} {this.GetSerializeInvocation(elementModel.ClrType, $"{valueVariableName}.Item{unionIndex}", valueOffsetVariableName)} }} break;"; switchCases.Add(@case); } string serializeBlock = $@" if ({discriminatorOffsetVariableName} != 0) {{ {this.GetSerializeInvocation(typeof(byte), discriminatorValueVariableName, discriminatorOffsetVariableName)} switch ({discriminatorValueVariableName}) {{ {string.Join("\r\n", switchCases)} default: throw new InvalidOperationException(""Unexpected""); }} }}"; return(prepareBlock, serializeBlock); }
internal static string GetDefaultValueToken(TableMemberModel memberModel) { var itemTypeModel = memberModel.ItemTypeModel; var clrType = itemTypeModel.ClrType; string defaultValue = $"default({GetCompilableTypeName(memberModel.ItemTypeModel.ClrType)})"; if (memberModel.HasDefaultValue) { string literalSpecifier = string.Empty; string cast = string.Empty; string defaultValueLiteral = memberModel.DefaultValue.ToString(); if (clrType == typeof(bool)) { // Bool.ToString() returns 'True', which is not the right keyword. defaultValueLiteral = defaultValueLiteral.ToLower(); } else if (clrType == typeof(sbyte)) { cast = "(sbyte)"; } else if (clrType == typeof(byte)) { cast = "(byte)"; } else if (clrType == typeof(short)) { cast = "(short)"; } else if (clrType == typeof(ushort)) { cast = "(ushort)"; } else if (clrType == typeof(float)) { literalSpecifier = "f"; } else if (clrType == typeof(uint)) { literalSpecifier = "u"; } else if (clrType == typeof(int)) { // shouldn't need this one, but let's be thorough. cast = "(int)"; } else if (clrType == typeof(double)) { literalSpecifier = "d"; } else if (clrType == typeof(long)) { literalSpecifier = "L"; } else if (clrType == typeof(ulong)) { literalSpecifier = "ul"; } else if (clrType.IsEnum) { defaultValueLiteral = $"{CSharpHelpers.GetCompilableTypeName(clrType)}.{defaultValueLiteral}"; } else { throw new InvalidOperationException("Unexpected default value type: " + clrType.FullName); } defaultValue = $"{cast}{defaultValueLiteral}{literalSpecifier}"; } return(defaultValue); }
public override void AdjustTableMember(TableMemberModel source) { // Force the vector to be sorted. source.IsSortedVector = true; }
internal IndexedVectorTypeModel(Type vectorType, TypeModelContainer provider) : base(vectorType, provider) { this.keyTypeModel = null !; this.valueTypeModel = null !; this.keyMemberModel = null !; }
public void AdjustTableMember(TableMemberModel source) => this.underlyingModel.AdjustTableMember(source);
public virtual void AdjustTableMember(TableMemberModel source) { }
private (string prepareBlock, string serializeBlock) GetStandardSerializeBlocks(int index, TableMemberModel memberModel) { string valueVariableName = $"index{index}Value"; string offsetVariableName = $"index{index}Offset"; string condition = $"if ({valueVariableName} != {CSharpHelpers.GetDefaultValueToken(memberModel)})"; if ((memberModel.ItemTypeModel is VectorTypeModel vector && vector.IsMemoryVector) || memberModel.IsKey) { // 1) Memory is a struct and can't be null, and 0-length vectors are valid. // Therefore, we just need to omit the conditional check entirely. // 2) For sorted vector keys, we must include the value since some other // libraries cannot do binary search with omitted keys. condition = string.Empty; } string keyCheckMethodCall = string.Empty; if (memberModel.IsKey) { keyCheckMethodCall = $"{nameof(SortedVectorHelpers)}.{nameof(SortedVectorHelpers.EnsureKeyNonNull)}({valueVariableName});"; } string prepareBlock = $@" var {valueVariableName} = item.{memberModel.PropertyInfo.Name}; int {offsetVariableName} = 0; {keyCheckMethodCall} {condition} {{ currentOffset += {CSharpHelpers.GetFullMethodName(ReflectedMethods.SerializationHelpers_GetAlignmentErrorMethod)}(currentOffset, {memberModel.ItemTypeModel.Alignment}); {offsetVariableName} = currentOffset; vtable.{nameof(VTableBuilder.SetOffset)}({index}, currentOffset - tableStart); currentOffset += {memberModel.ItemTypeModel.InlineSize}; }}"; string sortInvocation = string.Empty; if (memberModel.IsSortedVector) { VectorTypeModel vectorModel = (VectorTypeModel)memberModel.ItemTypeModel; TableTypeModel tableModel = (TableTypeModel)vectorModel.ItemTypeModel; TableMemberModel keyMember = tableModel.IndexToMemberMap.Single(x => x.Value.PropertyInfo == tableModel.KeyProperty).Value; var builtInType = BuiltInType.BuiltInTypes[keyMember.ItemTypeModel.ClrType]; string inlineSize = builtInType.TypeModel.SchemaType == FlatBufferSchemaType.Scalar ? builtInType.TypeModel.InlineSize.ToString() : "null"; sortInvocation = $"{nameof(SortedVectorHelpers)}.{nameof(SortedVectorHelpers.SortVector)}(" + $"span, {offsetVariableName}, {keyMember.Index}, {inlineSize}, {CSharpHelpers.GetCompilableTypeName(builtInType.SpanComparerType)}.Instance);"; } string serializeBlock = $@" if ({offsetVariableName} != 0) {{ {this.GetSerializeInvocation(memberModel.ItemTypeModel.ClrType, valueVariableName, offsetVariableName)} {sortInvocation} }}"; return(prepareBlock, serializeBlock); }