Ejemplo n.º 1
0
        private void WriteSerializerClasses(SourceGeneratorContext context, List <INamedTypeSymbol> bitStructClasses, Dictionary <INamedTypeSymbol, ClassSerializeSizeInfo> classesSizeInfo,
                                            INamedTypeSymbol bitStructAttributeSymbol, INamedTypeSymbol bitArrayAttributeSymbol)
        {
            foreach (INamedTypeSymbol classSymbol in bitStructClasses)
            {
                ClassSerializeSizeInfo classSizeInfo = classesSizeInfo[classSymbol];

                string classFullName       = SourceGenUtils.GetTypeFullName(classSymbol);
                string serializerClassName = CreateSerializerName(classSymbol);

                BitStructAttribute bitStructAttribute = SourceGenUtils.GetAttribute <BitStructAttribute>(classSymbol, bitStructAttributeSymbol);

                var sourceBuilder = new StringBuilder();
                sourceBuilder.Append($@"
namespace {classSymbol.ContainingNamespace}
{{
");
                ITypeSymbol[] classContainingTypes = SourceGenUtils.GetContainingTypesList(classSymbol);
                foreach (ITypeSymbol containingType in classContainingTypes)
                {
                    switch (containingType.TypeKind)
                    {
                    case TypeKind.Class:
                        sourceBuilder.Append($@"
    partial class {containingType.Name} {{
");
                        break;

                    case TypeKind.Struct:
                        sourceBuilder.Append($@"
    partial struct {containingType.Name} {{
");
                        break;

                    default:
                        context.ReportDiagnostic(Diagnostic.Create(new DiagnosticDescriptor("TMP", "TMP", $"Only expecting struct or class containing types. Have {containingType.TypeKind}.", "TMP", DiagnosticSeverity.Error, true), Location.Create("TMP", new TextSpan(), new LinePositionSpan())));
                        return;
                    }
                }

                sourceBuilder.Append($@"
    {SourceGenUtils.GetAccessibilityString(classSymbol.DeclaredAccessibility)} static class {serializerClassName}
    {{
");

                var sizeFuncBuilder        = new StringBuilder();
                var serializeFuncBuilder   = new StringBuilder();
                var deserializeFuncBuilder = new StringBuilder();

                if (classSizeInfo.Type == ClassSerializeSizeType.Const)
                {
                    sizeFuncBuilder.Append($@"
        public const int Size = {classSizeInfo.ConstSize};
");
                }

                sizeFuncBuilder.Append($@"
        public static int CalculateSize({classFullName} value)
        {{
            int result = {classSizeInfo.ConstSize};
");

                serializeFuncBuilder.Append($@"
        public static global::System.Span<byte> Serialize(global::System.Span<byte> output, {classFullName} value)
        {{
");

                deserializeFuncBuilder.Append($@"
        public static global::System.ReadOnlySpan<byte> Deserialize(global::System.ReadOnlySpan<byte> input, out {classFullName} value)
        {{
            value = new {classFullName}();
");

                foreach (IFieldSymbol classFieldMember in GetClassFieldMembers(classSymbol))
                {
                    if (classFieldMember.Type.IsIntegerType() ||
                        classFieldMember.Type.TypeKind == TypeKind.Enum)
                    {
                        INamedTypeSymbol      fieldType     = (INamedTypeSymbol)classFieldMember.Type;
                        IntegerOrEnumTypeInfo fieldTypeInfo = GetIntegerOrEnumTypeInfo(fieldType, bitStructAttribute.Endianess);

                        serializeFuncBuilder.Append($@"
            if (!{fieldTypeInfo.SerializeFuncName}(output, {fieldTypeInfo.SerializeTypeCast}value.{classFieldMember.Name}))
            {{
                throw new global::System.Exception(string.Format(""Not enough space to serialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
            }}
            output = output.Slice({fieldTypeInfo.TypeSize});
");

                        deserializeFuncBuilder.Append($@"
            {{
                if (!{fieldTypeInfo.DeserializeFuncName}(input, out var fieldValue))
                {{
                    throw new global::System.Exception(string.Format(""Not enough data to deserialize field {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}
                value.{classFieldMember.Name} = {fieldTypeInfo.DeserializeTypeCast}fieldValue;
                input = input.Slice({fieldTypeInfo.TypeSize});
            }}
");
                    }
                    else if (classFieldMember.Type.TypeKind == TypeKind.Class ||
                             classFieldMember.Type.TypeKind == TypeKind.Struct)
                    {
                        if (!SourceGenUtils.HasAttribute(classFieldMember.Type, bitStructAttributeSymbol))
                        {
                            // Type requires BitStruct attribute.
                            return;
                        }

                        INamedTypeSymbol       fieldType         = (INamedTypeSymbol)classFieldMember.Type;
                        ClassSerializeSizeInfo fieldTypeSizeInfo = classesSizeInfo[fieldType];

                        string serializerClassFullName = CreateSerializerFullName(fieldType);

                        if (fieldTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic)
                        {
                            sizeFuncBuilder.Append($@"
            result += {serializerClassFullName}.CalculateSize(value.{classFieldMember.Name});
");
                        }

                        serializeFuncBuilder.Append($@"
            output = {serializerClassFullName}.Serialize(output, value.{classFieldMember.Name});
");

                        deserializeFuncBuilder.Append($@"
            {{
                input = {serializerClassFullName}.Deserialize(input, out var fieldValue);
                value.{classFieldMember.Name} = fieldValue;
            }}
");
                    }
                    else if (classFieldMember.Type.TypeKind == TypeKind.Array)
                    {
                        BitArrayAttribute bitArrayAttribute = SourceGenUtils.GetAttribute <BitArrayAttribute>(classFieldMember, bitArrayAttributeSymbol);
                        if (bitArrayAttribute == null)
                        {
                            // Type requires BitArray attribute.
                            return;
                        }

                        IArrayTypeSymbol arrayType           = (IArrayTypeSymbol)classFieldMember.Type;
                        string           elementTypeFullName = SourceGenUtils.GetTypeFullName(arrayType.ElementType);

                        ClassSerializeSizeInfo elementTypeSizeInfo;
                        string calculateElementSize;
                        string serializeItem;
                        string deserializeItem;

                        if (arrayType.ElementType.IsIntegerType() ||
                            arrayType.ElementType.TypeKind == TypeKind.Enum)
                        {
                            IntegerOrEnumTypeInfo elementTypeInfo = GetIntegerOrEnumTypeInfo((INamedTypeSymbol)arrayType.ElementType, bitStructAttribute.Endianess);
                            elementTypeSizeInfo = new ClassSerializeSizeInfo()
                            {
                                Type      = ClassSerializeSizeType.Const,
                                ConstSize = elementTypeInfo.TypeSize,
                            };

                            calculateElementSize = $"result += {elementTypeInfo.TypeSize};";

                            serializeItem = $@"
                        if (!{elementTypeInfo.SerializeFuncName}(output, {elementTypeInfo.SerializeTypeCast}item))
                        {{
                            throw new global::System.Exception(string.Format(""Not enough space to serialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                        }}
                        output = output.Slice({elementTypeInfo.TypeSize});
";

                            deserializeItem = $@"
                        if (!{elementTypeInfo.DeserializeFuncName}(input, out var tmp))
                        {{
                            throw new global::System.Exception(string.Format(""Not enough data to deserialize item from list {{0}} from type {{1}}."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                        }}
                        var item = {elementTypeInfo.DeserializeTypeCast}tmp;
                        input = input.Slice({elementTypeInfo.TypeSize});
";
                        }
                        else if (arrayType.ElementType.TypeKind == TypeKind.Class ||
                                 arrayType.ElementType.TypeKind == TypeKind.Struct)
                        {
                            INamedTypeSymbol elementType = (INamedTypeSymbol)arrayType.ElementType;
                            elementTypeSizeInfo = classesSizeInfo[elementType];

                            string elementSerializerClassFullName = CreateSerializerFullName(elementType);

                            if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Const)
                            {
                                calculateElementSize = $@"result += {elementSerializerClassFullName}.Size;";
                            }
                            else
                            {
                                calculateElementSize = $@"result += {elementSerializerClassFullName}.CalculateSize(item);";
                            }

                            serializeItem   = $@"output = {elementSerializerClassFullName}.Serialize(output, item);";
                            deserializeItem = $@"input = {elementSerializerClassFullName}.Deserialize(input, out var item);";
                        }
                        else
                        {
                            // Can't serialize type.
                            return;
                        }

                        switch (bitArrayAttribute.SizeType)
                        {
                        case BitArraySizeType.Const:
                            if (elementTypeSizeInfo.Type == ClassSerializeSizeType.Dynamic)
                            {
                                sizeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                int collectionCount = array?.Length ?? 0;
                if (collectionCount > {bitArrayAttribute.ConstSize})
                {{
                    throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}

                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {calculateElementSize}
                    }}
                }}

                int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount;
                if (backfillCount > 0)
                {{
                    {elementTypeFullName} item = default;
                    for (int i = 0; i != backfillCount; ++i)
                    {{
                        {calculateElementSize}
                    }}
                }}
            }}
");
                            }

                            serializeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                int collectionCount = array?.Length ?? 0;
                if (collectionCount > {bitArrayAttribute.ConstSize})
                {{
                    throw new global::System.Exception(string.Format($""Constant size list {{0}} from type {{1}} has too many items."", ""{classFieldMember.Name}"", ""{classSymbol.Name}""));
                }}

                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {serializeItem}
                    }}
                }}

                int backfillCount = {bitArrayAttribute.ConstSize} - collectionCount;
                if (backfillCount > 0)
                {{
                    {elementTypeFullName} item = default;
                    for (int i = 0; i != backfillCount; ++i)
                    {{
                        {serializeItem}
                    }}
                }}
            }}
");

                            deserializeFuncBuilder.Append($@"
            {{
                var array = new {elementTypeFullName}[{bitArrayAttribute.ConstSize}];

                for (int i = 0; i != {bitArrayAttribute.ConstSize}; ++i)
                {{
                    {deserializeItem}
                    array[i] = item;
                }}

                value.{classFieldMember.Name} = array;
            }}
");

                            break;

                        case BitArraySizeType.EndFill:
                            sizeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {calculateElementSize}
                    }}
                }}
            }}
");

                            serializeFuncBuilder.Append($@"
            {{
                var array = value.{classFieldMember.Name};
                if (array != null)
                {{
                    foreach (var item in array)
                    {{
                        {serializeItem}
                    }}
                }}
            }}
");
                            deserializeFuncBuilder.Append($@"
                var list = new global::System.Collections.Generic.List<{elementTypeFullName}>();

                while (!input.IsEmpty)
                {{
                    {deserializeItem}
                    list.Add(item);
                }}

                value.{classFieldMember.Name} = list.ToArray();
");

                            break;

                        default:
                            // Unknown BitArraySizeType.
                            return;
                        }
                    }
                    else
                    {
                        // Can't serialize type.
                        return;
                    }
                }

                sizeFuncBuilder.Append($@"
            return result;
        }}
");

                serializeFuncBuilder.Append($@"
            return output;
        }}
");

                deserializeFuncBuilder.Append($@"
            return input;
        }}
");

                sourceBuilder.Append(sizeFuncBuilder.ToString());
                sourceBuilder.Append(serializeFuncBuilder.ToString());
                sourceBuilder.Append(deserializeFuncBuilder.ToString());

                for (int i = 0; i != classContainingTypes.Length + 1; ++i)
                {
                    sourceBuilder.Append($@"
    }}
");
                }

                sourceBuilder.Append($@"
}}
");

                string sourceCode = sourceBuilder.ToString();
                context.AddSource($"{serializerClassName}.cs", SourceText.From(sourceCode, Encoding.UTF8));
            }
        }
Ejemplo n.º 2
0
        private static IntegerOrEnumTypeInfo GetIntegerOrEnumTypeInfo(INamedTypeSymbol typeSymbol, BitEndianess endianess)
        {
            IntegerOrEnumTypeInfo result;

            string endianessName = endianess == BitEndianess.BigEndian ?
                                   "BigEndian" :
                                   "LittleEndian";

            ITypeSymbol fieldUnderlyingType = typeSymbol;

            result.SerializeTypeCast   = string.Empty;
            result.DeserializeTypeCast = string.Empty;

            if (typeSymbol.TypeKind == TypeKind.Enum)
            {
                fieldUnderlyingType        = typeSymbol.EnumUnderlyingType;
                result.SerializeTypeCast   = $"({SourceGenUtils.GetTypeFullName(fieldUnderlyingType)})";
                result.DeserializeTypeCast = $"({SourceGenUtils.GetTypeFullName(typeSymbol)})";
            }

            switch (fieldUnderlyingType.SpecialType)
            {
            case SpecialType.System_Byte:
            case SpecialType.System_SByte:
                result.SerializeFuncName   = $"global::BitSerialization.Generated.BitPrimitivesSerializer.TryWrite{fieldUnderlyingType.Name}";
                result.DeserializeFuncName = $"global::BitSerialization.Generated.BitPrimitivesSerializer.TryRead{fieldUnderlyingType.Name}";
                break;

            case SpecialType.System_UInt16:
            case SpecialType.System_Int16:
            case SpecialType.System_UInt32:
            case SpecialType.System_Int32:
            case SpecialType.System_UInt64:
            case SpecialType.System_Int64:
                result.SerializeFuncName   = $"global::System.Buffers.Binary.BinaryPrimitives.TryWrite{fieldUnderlyingType.Name}{endianessName}";
                result.DeserializeFuncName = $"global::System.Buffers.Binary.BinaryPrimitives.TryRead{fieldUnderlyingType.Name}{endianessName}";
                break;

            default:
                throw new Exception();
            }

            switch (fieldUnderlyingType.SpecialType)
            {
            case SpecialType.System_Byte:
            case SpecialType.System_SByte:
                result.TypeSize = 1;
                break;

            case SpecialType.System_UInt16:
            case SpecialType.System_Int16:
                result.TypeSize = 2;
                break;

            case SpecialType.System_UInt32:
            case SpecialType.System_Int32:
                result.TypeSize = 4;
                break;

            case SpecialType.System_UInt64:
            case SpecialType.System_Int64:
                result.TypeSize = 8;
                break;

            default:
                throw new Exception();
            }

            return(result);
        }