예제 #1
0
        public static ReadOnlySpan <byte> Deserialize(ReadOnlySpan <byte> input, Type type, out object value)
        {
            object result           = Activator.CreateInstance(type) !;
            ReadOnlySpan <byte> itr = input;

            // Get the object's serialization settings.
            BitStructAttribute structAttribute = type.GetCustomAttribute <BitStructAttribute>() ?? BitStructAttribute.Default;

            if (type.StructLayoutAttribute == null || type.StructLayoutAttribute.Value != LayoutKind.Sequential)
            {
                throw new Exception($"Type {type.Name} must a LayoutKind.Sequential struct layout.");
            }

            // Gets the type's fields in the order in which they are declared.
            // Note: Sorting by MetadataToken sorts the fields by declaration order when StructLayout is set to LayoutKind.Sequential.
            IEnumerable <FieldInfo> fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                             .OrderBy((field) => field.MetadataToken);

            // Iterate through the object's fields.
            foreach (FieldInfo field in fields)
            {
                Type   fieldType = field.FieldType;
                object fieldValue;

                if (fieldType.IsArray)
                {
                    itr = DeserializeArray(structAttribute.Endianess, field, type.Name, itr, out fieldValue);
                }
                else
                {
                    itr = DeserializeValue(structAttribute.Endianess, fieldType, itr, field.Name, out fieldValue);
                }

                field.SetValue(result, fieldValue);
            }

            value = result;
            return(itr);
        }
예제 #2
0
        private static Span <byte> Serialize(Span <byte> output, object value)
        {
            Span <byte> itr  = output;
            Type        type = value.GetType();

            // Get the object's serialization settings.
            BitStructAttribute structAttribute = type.GetCustomAttribute <BitStructAttribute>() ?? BitStructAttribute.Default;

            if (type.StructLayoutAttribute == null || type.StructLayoutAttribute.Value != LayoutKind.Sequential)
            {
                throw new Exception($"Type {type.Name} must have a LayoutKind.Sequential struct layout.");
            }

            // Gets the type's fields in the order in which they are declared.
            // Note: Sorting by MetadataToken sorts the fields by declaration order when StructLayout is set to LayoutKind.Sequential.
            IEnumerable <FieldInfo> fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                             .OrderBy((field) => field.MetadataToken);

            // Iterate through the object's fields.
            foreach (FieldInfo field in fields)
            {
                Type   fieldType          = field.FieldType;
                object fieldValueAsObject = field.GetValue(value) !;

                if (fieldType.IsArray)
                {
                    itr = SerializeArray(structAttribute.Endianess, fieldValueAsObject, field, type.Name, itr);
                }
                else
                {
                    itr = SerializeValue(structAttribute.Endianess, fieldValueAsObject, fieldType, field.Name, itr);
                }
            }

            return(itr);
        }
예제 #3
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));
            }
        }
예제 #4
0
        // Creates a playbook for serializing and deserializing the type T.
        static BitSerializer()
        {
            Type type = typeof(T);

            if (!type.IsLayoutSequential)
            {
                throw new Exception($"Type {type.Name} must have a LayoutKind.Sequential struct layout.");
            }

            BitStructAttribute structAttribute = type.GetCustomAttribute <BitStructAttribute>() ?? BitStructAttribute.Default;

            // Gets the type's fields in the order in which they are declared.
            // Note: Sorting by MetadataToken sorts the fields by declaration order when StructLayout is set to LayoutKind.Sequential.
            IEnumerable <FieldInfo> fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                                             .OrderBy((field) => field.MetadataToken);

            List <FieldSerializationData> playbook = new List <FieldSerializationData>();

            foreach (FieldInfo fieldInfo in fields)
            {
                DeserializeFieldHandler?deserializeFunc = null;
                SerializeFieldHandler?  serializeFunc   = null;
                bool handled = false;

                if (fieldInfo.FieldType.IsArray)
                {
                    // Get the array's serialization settings.
                    BitArrayAttribute?arrayAttribute = fieldInfo.GetCustomAttribute <BitArrayAttribute>();
                    if (arrayAttribute == null)
                    {
                        throw new Exception($"Field ${fieldInfo.Name} from type ${type.Name} must be annotated with BitArrayAttribute.");
                    }

                    switch (arrayAttribute.SizeType)
                    {
                    case BitArraySizeType.Const:
                    case BitArraySizeType.EndFill:
                        break;

                    default:
                        throw new Exception($"Unknown BitArraySizeType value of {arrayAttribute.SizeType}");
                    }

                    Type elementType = fieldInfo.FieldType.GetElementType() !;

                    if (elementType.IsArray)
                    {
                        // Can't handle array of arrays directly as serialization settings are required for the nested arrays.
                        throw new Exception($"Cannot serialize a pure array of array for field {fieldInfo.Name} of type {type.Name}. Use a wrapper struct instead.");
                    }
                    else if (elementType.IsStruct() ||
                             elementType.IsClass ||
                             elementType.IsPrimitive ||
                             elementType.IsEnum)
                    {
                        Type?arraySerializerType = null;

                        if (elementType.IsStruct() || elementType.IsClass)
                        {
                            // Create an array serializer type for this object type.
                            arraySerializerType = typeof(BitSerializerStructArray <>).MakeGenericType(elementType);
                        }
                        else
                        {
                            // If element type is an enum, get the underlying integer type to use for serialization.
                            Type elementUnderlyingType = elementType.IsEnum ?
                                                         elementType.GetEnumUnderlyingType() :
                                                         elementType;

                            Dictionary <Type, BitSerializerPrimitives.TypeData> types = BitSerializerPrimitives.GetTypeData(structAttribute.Endianess);

                            // Get the array serializer class type for the integer type.
                            if (types.TryGetValue(elementUnderlyingType, out var typeData))
                            {
                                arraySerializerType = typeData.ArraySerializerType;

                                if (elementType.IsEnum)
                                {
                                    // Change the array serializer class's generic type parameter to the enum type.
                                    arraySerializerType = arraySerializerType.GetGenericTypeDefinition().MakeGenericType(elementType);
                                }
                            }
                        }

                        if (arraySerializerType != null)
                        {
                            // Create an instance of the array serialization type.
                            object arraySerializer = Activator.CreateInstance(arraySerializerType, arrayAttribute) !;

                            // Get references to the serialization and deserialization member functions.
                            deserializeFunc = (DeserializeFieldHandler)arraySerializerType.GetMethod(nameof(BitSerializerArray <int> .DeserializeField), BindingFlags.Instance | BindingFlags.Public) !.CreateDelegate(typeof(DeserializeFieldHandler), arraySerializer);
                            serializeFunc   = (SerializeFieldHandler)arraySerializerType.GetMethod(nameof(BitSerializerArray <int> .SerializeField), BindingFlags.Instance | BindingFlags.Public) !.CreateDelegate(typeof(SerializeFieldHandler), arraySerializer);
                            handled         = true;
                        }
                    }
                }
                else if (fieldInfo.FieldType.IsEnum ||
                         fieldInfo.FieldType.IsPrimitive)
                {
                    // If element type is an enum, get the underlying integer type to use for serialization.
                    Type fieldUnderlyingType = fieldInfo.FieldType.IsEnum ?
                                               fieldInfo.FieldType.GetEnumUnderlyingType() :
                                               fieldInfo.FieldType;

                    Dictionary <Type, BitSerializerPrimitives.TypeData> types = BitSerializerPrimitives.GetTypeData(structAttribute.Endianess);

                    // Get the serialization methods for the integer type.
                    if (types.TryGetValue(fieldUnderlyingType, out var typeData))
                    {
                        deserializeFunc = typeData.DeserializeFunc;
                        serializeFunc   = typeData.SerializeFunc;

                        if (fieldInfo.FieldType.IsEnum)
                        {
                            // Change deserialization function's generic type parameter to the enum type.
                            // Note: This isn't required for the serialization function since an enum wrapped in an object box can be directly
                            // cast to the integer type.
                            MethodInfo deserializeFuncInfo = deserializeFunc.GetMethodInfo() !;
                            deserializeFunc = (DeserializeFieldHandler)deserializeFuncInfo.GetGenericMethodDefinition().MakeGenericMethod(fieldInfo.FieldType).CreateDelegate(typeof(DeserializeFieldHandler));
                        }

                        handled = true;
                    }
                }
                else if (fieldInfo.FieldType.IsStruct() ||
                         fieldInfo.FieldType.IsClass)
                {
                    // Get the BitSerializer type for this object.
                    Type bitSerializerStructType = typeof(BitSerializer <>).MakeGenericType(fieldInfo.FieldType);

                    // Get references to the serialization and deserialization static functions.
                    deserializeFunc = (DeserializeFieldHandler)bitSerializerStructType.GetMethod(nameof(DeserializeField), BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate(typeof(DeserializeFieldHandler));
                    serializeFunc   = (SerializeFieldHandler)bitSerializerStructType.GetMethod(nameof(SerializeField), BindingFlags.NonPublic | BindingFlags.Static).CreateDelegate(typeof(SerializeFieldHandler));
                    handled         = true;
                }

                if (!handled)
                {
                    throw new Exception($"Can't serialize type of {fieldInfo.FieldType.Name} from field {fieldInfo.Name}.");
                }

                playbook.Add(new FieldSerializationData(fieldInfo, deserializeFunc !, serializeFunc !));
            }

            _Playbook = playbook.ToArray();
        }