private void ImplementMemoryVectorReadMethod(VectorTypeModel typeModel) { string invocation; if (typeModel.ItemTypeModel.ClrType == typeof(byte)) { invocation = nameof(InputBuffer.ReadByteMemoryBlock); if (typeModel.IsReadOnly) { invocation = nameof(InputBuffer.ReadByteReadOnlyMemoryBlock); } } else { string methodName = nameof(InputBuffer.ReadMemoryBlock); if (typeModel.IsReadOnly) { methodName = nameof(InputBuffer.ReadReadOnlyMemoryBlock); } invocation = $"{methodName}<{CSharpHelpers.GetCompilableTypeName(typeModel.ItemTypeModel.ClrType)}>"; } string body = $"memory.{invocation}(offset, {typeModel.ItemTypeModel.InlineSize})"; // Greedy deserialize has the invariant that we no longer touch the // original buffer. This means a memory copy here. if (this.options.GreedyDeserialize) { body = $"{body}.ToArray().AsMemory()"; } this.GenerateMethodDefinition(typeModel.ClrType, $"return {body};"); }
public override string ToString() { List <string> lines = new List <string>(); if (this.MemberModel.IsVirtual) { if (!string.IsNullOrEmpty(this.BackingFieldName)) { lines.Add($"private {CSharpHelpers.GetCompilableTypeName(this.propertyInfo.PropertyType)} {this.BackingFieldName};"); } if (!string.IsNullOrEmpty(this.HasValueFieldName)) { lines.Add($"private bool {this.HasValueFieldName};"); } var accessModifiers = CSharpHelpers.GetPropertyAccessModifiers(this.propertyInfo); lines.Add($@"{accessModifiers.propertyModifier} override {CSharpHelpers.GetCompilableTypeName(this.propertyInfo.PropertyType)} {this.propertyInfo.Name} {{"); lines.Add($"{accessModifiers.getModifer} get {{ {this.GetterBody} }}"); MethodInfo methodInfo = this.propertyInfo.SetMethod; if (methodInfo != null) { lines.Add($"{accessModifiers.setModifier} set {{ {this.SetterBody} }}"); } lines.Add("}"); } lines.Add(this.ReadValueMethodDefinition); return(string.Join("\r\n", lines)); }
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); }
private void ImplementEnumInlineWriteMethod(EnumTypeModel enumModel) { var type = enumModel.ClrType; var underlyingType = Enum.GetUnderlyingType(type); string body = this.GetSerializeInvocation(underlyingType, $"({CSharpHelpers.GetCompilableTypeName(underlyingType)})item", "originalOffset"); this.GenerateSerializeMethod(type, body); }
private void ImplementArrayVectorReadMethod(VectorTypeModel typeModel) { var itemTypeModel = typeModel.ItemTypeModel; string statement; if (itemTypeModel is ScalarTypeModel scalarModel && scalarModel.NativelyReadableFromMemory) { // Memory is faster in situations where we can get away with it. statement = $"memory.{nameof(InputBuffer.ReadMemoryBlock)}<{CSharpHelpers.GetCompilableTypeName(itemTypeModel.ClrType)}>(offset, {itemTypeModel.InlineSize}).ToArray()"; }
private void ImplementStructReadMethod(StructTypeModel typeModel) { // We have to implement two items: The table class and the overall "read" method. // Let's start with the read method. string className = "structReader_" + Guid.NewGuid().ToString("n"); // Static factory method. this.GenerateMethodDefinition(typeModel.ClrType, $"return new {className}(memory, offset);"); // Implement the class { // Build up a list of property overrides. var propertyOverrides = new List <GeneratedProperty>(); for (int index = 0; index < typeModel.Members.Count; ++index) { var value = typeModel.Members[index]; PropertyInfo propertyInfo = value.PropertyInfo; Type propertyType = propertyInfo.PropertyType; string compilableTypeName = CSharpHelpers.GetCompilableTypeName(propertyType); GeneratedProperty generatedProperty = new GeneratedProperty(this.options, index, propertyInfo); generatedProperty.ReadValueMethodDefinition = $@" [MethodImpl(MethodImplOptions.AggressiveInlining)] private static {CSharpHelpers.GetCompilableTypeName(propertyType)} {generatedProperty.ReadValueMethodName}(InputBuffer buffer, int offset) {{ return {this.GetReadInvocation(propertyType, "buffer", $"offset + {value.Offset}")}; }} "; propertyOverrides.Add(generatedProperty); } string classDefinition = this.CreateClass( className, typeModel.ClrType, typeModel.Members.Select(x => x.PropertyInfo.Name), propertyOverrides); var node = CSharpSyntaxTree.ParseText(classDefinition, ParseOptions); this.methodDeclarations.Add(node.GetRoot()); } }
/// <summary> /// Gets a method to serialize the given type with the given body. /// </summary> private void GenerateGetMaxSizeMethod(Type type, string body, bool inline = true) { string inlineDeclaration = "[MethodImpl(MethodImplOptions.AggressiveInlining)]"; if (!inline) { inlineDeclaration = string.Empty; } string declaration = $@" {inlineDeclaration} private static int {this.MethodNames[type]}({CSharpHelpers.GetCompilableTypeName(type)} item) {{ {body} }}"; var node = CSharpSyntaxTree.ParseText(declaration, ParseOptions); this.methodDeclarations.Add(node.GetRoot()); }
private string CreateClass( string className, Type baseType, IEnumerable <string> propertyNames, IEnumerable <GeneratedProperty> propertyOverrides) { string inputBufferFieldDef = "private readonly InputBuffer buffer;"; string offsetFieldDef = "private readonly int offset;"; string ctorBody = $@" this.buffer = buffer; this.offset = offset; "; if (this.options.GreedyDeserialize) { inputBufferFieldDef = string.Empty; offsetFieldDef = string.Empty; ctorBody = string.Join("\r\n", propertyOverrides.Select(x => $"this.{x.BackingFieldName} = {x.ReadValueMethodName}(buffer, offset);")); } return ($@" private sealed class {className} : {CSharpHelpers.GetCompilableTypeName(baseType)} {{ {inputBufferFieldDef} {offsetFieldDef} public {className}(InputBuffer buffer, int offset) {{ {ctorBody} }} {string.Join("\r\n", propertyOverrides)} }} "); }
/// <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 void ImplementStructInlineWriteMethod(StructTypeModel structModel) { var type = structModel.ClrType; List <string> body = new List <string>(); for (int i = 0; i < structModel.Members.Count; ++i) { var memberInfo = structModel.Members[i]; string propertyAccessor = $"item.{memberInfo.PropertyInfo.Name}"; if (memberInfo.ItemTypeModel.SchemaType == FlatBufferSchemaType.Struct) { // Force structs to be non-null. FlatSharp doesn't declare structs as structs, // so we need to be careful that structs-within-structs are not null. propertyAccessor += $" ?? new {CSharpHelpers.GetCompilableTypeName(memberInfo.ItemTypeModel.ClrType)}()"; } string invocation = this.GetSerializeInvocation(memberInfo.ItemTypeModel.ClrType, propertyAccessor, $"{memberInfo.Offset} + originalOffset"); body.Add(invocation); } this.GenerateSerializeMethod(type, string.Join("\r\n", body)); }
/// <summary> /// Gets a method to serialize the given type with the given body. /// </summary> private void GenerateSerializeMethod(Type type, string body, bool inline = true) { string inlineDeclaration = inline ? string.Empty : "[MethodImpl(MethodImplOptions.AggressiveInlining)]"; string declaration = $@" {inlineDeclaration} private static void {this.MethodNames[type]} (SpanWriter writer, Span<byte> span, {CSharpHelpers.GetCompilableTypeName(type)} item, int originalOffset, SerializationContext context) {{ {body} }}"; var node = CSharpSyntaxTree.ParseText(declaration, ParseOptions); this.methodDeclarations.Add(node.GetRoot()); }
private void ImplementMemoryVectorInlineWriteMethod(VectorTypeModel vectorModel) { var type = vectorModel.ClrType; var itemTypeModel = vectorModel.ItemTypeModel; string writerMethodName = $"{nameof(SpanWriter.WriteReadOnlyMemoryBlock)}<{CSharpHelpers.GetCompilableTypeName(itemTypeModel.ClrType)}>"; if (itemTypeModel.ClrType == typeof(byte)) { // Optimization: when we're writing bytes we don't have to change types. writerMethodName = nameof(SpanWriter.WriteReadOnlyByteMemoryBlock); } string body = $"writer.{writerMethodName}(span, item, originalOffset, {itemTypeModel.Alignment}, {itemTypeModel.InlineSize}, context);"; this.GenerateSerializeMethod(type, body); }
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); }
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); }