Exemple #1
0
 public KeyValuePairFormatterResolver(CerasSerializer serializer)
 {
     _serializer = serializer;
 }
        public ReferenceFormatter(CerasSerializer serializer)
        {
            _serializer = serializer;

            _typeFormatter = (TypeFormatter)serializer.GetSpecificFormatter(typeof(Type));
        }
Exemple #3
0
 static UlidFormatter()
 {
     CerasSerializer.AddFormatterConstructedType(typeof(Ulid));
 }
        //called when data for any output pin is requested
        public void Evaluate(int SpreadMax)
        {
            if (!_pgready)
            {
                return;
            }
            if (_typeChanged || _firstFrame || Input.IsChanged)
            {
                Success.SliceCount        = Input.SliceCount;
                _output.Spread.SliceCount = Input.SliceCount;
                _input.ResizeAndDismiss(Input.SliceCount, i => new byte[0]);

                for (int i = 0; i < Input.SliceCount; i++)
                {
                    if (Input[i] != null && _input[i].Length < Input[i].Length)
                    {
                        _input[i] = new byte[Input[i].Length];
                    }

                    if (Input[i] == null || Input[i].Read(_input[i], 0, (int)Input[i].Length) <= 0)
                    {
                        _output[i] = null;
                        continue;
                    }

                    try
                    {
                        if (_output[i] == null)
                        {
                            _output[i] = _ceras.Deserialize <object>(_input[i]);
                        }
                        else
                        {
                            var obj = _output[i];
                            _ceras.Deserialize(ref obj, _input[i]);
                            _output[i] = obj;
                        }
                        Success[i] = true;
                    }
                    catch
                    {
                        Success[i] = false;
                        _ceras     = CerasSerializerHelper.Serializer();
                        _output[i] = null;
                        continue;
                    }
                }
                _output.SetReflectedChanged(true);
            }
            else
            {
                _output.SetReflectedChanged(false);
            }

            if (_firstFrame)
            {
                _firstFrame = false;
            }
            if (_typeChanged)
            {
                _typeChanged = false;
            }
        }
 public ImmutableQueueFormatter()
 {
     CerasSerializer.AddFormatterConstructedType(typeof(ImmutableQueue <TItem>));
 }
Exemple #6
0
        /*
         *
         * internal static SerializeDelegate<T> GenerateSerializer2(CerasSerializer ceras, Schema schema, bool isSchemaFormatter, bool isStatic)
         * {
         *      var members = schema.Members;
         *      var refBufferArg = Parameter(typeof(byte[]).MakeByRefType(), "buffer");
         *      var refOffsetArg = Parameter(typeof(int).MakeByRefType(), "offset");
         *      var valueArg = Parameter(typeof(T), "value");
         *
         *      if (isStatic)
         *              valueArg = null;
         *
         *
         *      var body = new List<Expression>();
         *      var locals = new List<ParameterExpression>();
         *
         *      ParameterExpression startPos = null, size = null;
         *      if (isSchemaFormatter)
         *      {
         *              locals.Add(startPos = Variable(typeof(int), "startPos"));
         *              locals.Add(size = Variable(typeof(int), "size"));
         *      }
         *
         *
         *      Dictionary<Type, IFormatter> typeToFormatter = new Dictionary<Type, IFormatter>();
         *      foreach (var m in members.Where(m => !m.IsSkip).DistinctBy(m => m.MemberType))
         *              typeToFormatter.Add(m.MemberType, ceras.GetReferenceFormatter(m.MemberType));
         *
         *      Dictionary<Type, ConstantExpression> typeToFormatterExp = typeToFormatter.ToDictionary(x => x.Key, x => Constant(x.Value));
         *
         *
         *      //
         *      // Create tuple to hold all the formatters
         *      Type[] formatterTypes = typeToFormatter.Values.Select(f => f.GetType()).ToArray();
         *      var create = ReflectionHelper.ResolveMethod(typeof(Tuple), "Create", formatterTypes);
         *      var formatterContainer = create.Invoke(null, typeToFormatter.Values.Cast<object>().ToArray());
         *
         *      // Pass this as arg
         *      var tupleType = formatterContainer.GetType();
         *      var formattersArg = Parameter(tupleType, "formatterContainer");
         *      var newTypeToFormatter = new Dictionary<Type, (Expression getExp, object actualValue)>();
         *      foreach (var kvp in typeToFormatter)
         *      {
         *              var t = kvp.Key;
         *              var formatterInstance = kvp.Value;
         *              var prop = tupleType.GetProperties().First(p => p.PropertyType.FindClosedArg(typeof(IFormatter<>)) == t);
         *              newTypeToFormatter[t] = (MakeMemberAccess(formattersArg, prop), formatterInstance);
         *      }
         *      //
         *      //
         *
         *
         *      // Serialize all members
         *      foreach (var member in members)
         *      {
         *              if (member.IsSkip)
         *                      continue;
         *
         *              // Get the formatter and its Serialize method
         *              var formatterE = newTypeToFormatter[member.MemberType];
         *              var formatter = formatterE.actualValue;
         *              var serializeMethod = formatter.GetType().GetMethod(nameof(IFormatter<int>.Serialize));
         *              Debug.Assert(serializeMethod != null, "Can't find serialize method on formatter " + formatter.GetType().FullName);
         *
         *              // Access the field that we want to serialize
         *              var fieldExp = MakeMemberAccess(valueArg, member.MemberInfo);
         *
         *              // Call "Serialize"
         *              if (!isSchemaFormatter)
         *              {
         *                      var serializeCall = Call(formatterE.getExp, serializeMethod, refBufferArg, refOffsetArg, fieldExp);
         *                      body.Add(serializeCall);
         *              }
         *
         *      }
         *
         *      var serializeBlock = Block(variables: locals, expressions: body);
         *
         *
         *      if (isStatic)
         *              valueArg = Parameter(typeof(T), "value");
         *
         *
         *      var serialize2Type = typeof(SerializeDelegate2<,>);
         *      var innerDelegateType = serialize2Type.MakeGenericType(tupleType, typeof(T));
         *
         *      var lambda = Lambda(delegateType: innerDelegateType,
         *                                              body: serializeBlock,
         *                                              parameters: new ParameterExpression[]
         *                                              {
         *                                                      formattersArg,
         *                                                      refBufferArg, refOffsetArg, valueArg
         *                                              });
         *
         *      var innerDelegate = lambda.Compile();
         *
         *      var lambdaMethodInfo = innerDelegate.Method;
         *
         *      var dynAsm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("abc"), AssemblyBuilderAccess.RunAndSave);
         *      var dynMod = dynAsm.DefineDynamicModule("module");
         *      var dynType = dynMod.DefineType("type", TypeAttributes.Public);
         *      var dynMethod = dynType.DefineMethod("doSerialize", MethodAttributes.Public | MethodAttributes.Static, typeof(void), new Type[]
         *      {
         *              tupleType,
         *              typeof(byte[]).MakeByRefType(),
         *              typeof(int).MakeByRefType(),
         *              typeof(T)
         *      });
         *
         *      lambda.CompileToMethod(dynMethod);
         *
         *      var createdType = dynType.CreateType();
         *      var generatedMethodInfo = dynType.GetMethod(dynMethod.Name, dynMethod.GetParameters().Select(p => p.ParameterType).ToArray());
         *
         *      // Now bind to the created tuple!
         *      //var finalDelegate = Delegate.CreateDelegate(typeof(SerializeDelegate<T>), formatterContainer, lambdaMethodInfo);
         *      var finalDelegate = generatedMethodInfo.CreateDelegate(typeof(SerializeDelegate<T>), formatterContainer);
         *
         *      return (SerializeDelegate<T>)finalDelegate;
         * }
         *
         */


        internal static Expression <DeserializeDelegate <T> > GenerateDeserializer(CerasSerializer ceras, Schema schema, bool isSchemaFormatter, bool isStatic)
        {
            bool verifySizes = isSchemaFormatter && ceras.Config.VersionTolerance.VerifySizes;
            var  members     = schema.Members;
            var  typeConfig  = ceras.Config.GetTypeConfig(schema.Type, isStatic);
            var  tc          = typeConfig.TypeConstruction;

            bool constructObject = tc.HasDataArguments;             // Are we responsible for instantiating an object?
            HashSet <ParameterExpression> usedVariables = null;

            var bufferArg    = Parameter(typeof(byte[]), "buffer");
            var refOffsetArg = Parameter(typeof(int).MakeByRefType(), "offset");
            var refValueArg  = Parameter(typeof(T).MakeByRefType(), "value");

            if (isStatic)
            {
                refValueArg = null;
            }

            var body   = new List <Expression>();
            var locals = new List <ParameterExpression>(schema.Members.Count);


            ParameterExpression blockSize = null, offsetStart = null;

            if (isSchemaFormatter)
            {
                locals.Add(blockSize   = Variable(typeof(int), "blockSize"));
                locals.Add(offsetStart = Variable(typeof(int), "offsetStart"));
            }


            // MemberInfo -> Variable()
            Dictionary <MemberInfo, ParameterExpression> memberInfoToLocal = new Dictionary <MemberInfo, ParameterExpression>();

            foreach (var m in members)
            {
                if (m.IsSkip)
                {
                    continue;
                }

                var local = Variable(m.MemberType, m.MemberName + "_local");
                locals.Add(local);
                memberInfoToLocal.Add(m.MemberInfo, local);
            }

            // Type -> Constant(IFormatter<Type>)
            Dictionary <Type, ConstantExpression> typeToFormatter          = new Dictionary <Type, ConstantExpression>();

            foreach (var m in members.Where(m => !m.IsSkip).DistinctBy(m => m.MemberType))
            {
                typeToFormatter.Add(m.MemberType, Constant(ceras.GetReferenceFormatter(m.MemberType)));
            }


            //
            // 1. Read existing values into locals (Why? See explanation at the end of the file)
            foreach (var m in members)
            {
                if (constructObject)
                {
                    continue;                     // Can't read existing data when there is no object yet...
                }
                if (m.IsSkip)
                {
                    continue;                     // Member doesn't exist
                }
                // Init the local with the current value
                var local = memberInfoToLocal[m.MemberInfo];
                body.Add(Assign(local, MakeMemberAccess(refValueArg, m.MemberInfo)));
            }

            //
            // 2. Deserialize into local (faster and more robust than field/prop directly)
            foreach (var m in members)
            {
                if (isSchemaFormatter)
                {
                    // Read block size
                    // blockSize = ReadSize();
                    var readCall = Call(method: _sizeReadMethod, arg0: bufferArg, arg1: refOffsetArg);
                    body.Add(Assign(blockSize, Convert(readCall, typeof(int))));

                    if (verifySizes)
                    {
                        // Store the offset before reading the member so we can compare it later
                        body.Add(Assign(offsetStart, refOffsetArg));
                    }


                    if (m.IsSkip)
                    {
                        // Skip over the field
                        // offset += blockSize;
                        body.Add(AddAssign(refOffsetArg, blockSize));
                        continue;
                    }
                }

                if (m.IsSkip && !isSchemaFormatter)
                {
                    throw new InvalidOperationException("DynamicFormatter can not skip members in non-schema mode");
                }


                var formatterExp      = typeToFormatter[m.MemberType];
                var formatter         = formatterExp.Value;
                var deserializeMethod = formatter.GetType().GetMethod(nameof(IFormatter <int> .Deserialize));
                Debug.Assert(deserializeMethod != null, "Can't find deserialize method on formatter " + formatter.GetType().FullName);

                var local = memberInfoToLocal[m.MemberInfo];
                body.Add(Call(formatterExp, deserializeMethod, bufferArg, refOffsetArg, local));

                if (isSchemaFormatter && verifySizes)
                {
                    // Compare blockSize with how much we've actually read
                    // if ( offsetStart + blockSize != offset )
                    //     ThrowException();

                    body.Add(IfThen(test: NotEqual(Add(offsetStart, blockSize), refOffsetArg),
                                    ifTrue: Call(instance: null, _offsetMismatchMethod, offsetStart, refOffsetArg, blockSize)));
                }
            }

            //
            // 3. Create object instance (only when actually needed)
            if (constructObject)
            {
                // Create a helper array for the implementing type construction
                var memberParameters = (
                    from m in schema.Members
                    where !m.IsSkip
                    let local = memberInfoToLocal[m.MemberInfo]
                                select new MemberParameterPair {
                    LocalVar = local, Member = m.MemberInfo
                }
                    ).ToArray();

                usedVariables = new HashSet <ParameterExpression>();
                tc.EmitConstruction(schema, body, refValueArg, usedVariables, memberParameters);
            }

            //
            // 4. Write back values in one batch
            var orderedMembers = OrderMembersForWriteBack(members);

            foreach (var m in orderedMembers)
            {
                if (m.IsSkip)
                {
                    continue;
                }

                var local = memberInfoToLocal[m.MemberInfo];
                var type  = m.MemberType;

                if (usedVariables != null && usedVariables.Contains(local))
                {
                    // Member was already used in the constructor / factory method, no need to write it again
                    continue;
                }

                if (m.IsSkip)
                {
                    continue;
                }

                if (m.MemberInfo is FieldInfo fieldInfo)
                {
                    if (fieldInfo.IsInitOnly)
                    {
                        // Readonly field
                        var memberConfig = typeConfig.Members.First(x => x.Member == m.MemberInfo);
                        var rh           = memberConfig.ComputeReadonlyHandling();
                        DynamicFormatterHelpers.EmitReadonlyWriteBack(type, rh, fieldInfo, refValueArg, local, body);
                    }
                    else
                    {
                        // Normal assignment
                        body.Add(Assign(left: Field(refValueArg, fieldInfo),
                                        right: local));
                    }
                }
                else
                {
                    // Context
                    var p = (PropertyInfo)m.MemberInfo;

                    var setMethod = p.GetSetMethod(true);
                    body.Add(Call(instance: refValueArg, setMethod, local));
                }
            }


            var bodyBlock = Block(variables: locals, expressions: body);


            if (isStatic)
            {
                refValueArg = Parameter(typeof(T).MakeByRefType(), "value");
            }

            return(Lambda <DeserializeDelegate <T> >(bodyBlock, bufferArg, refOffsetArg, refValueArg));
        }
Exemple #7
0
        static (CerasSerializer, List <Type>) CreateSerializerAndTargets(IEnumerable <Assembly> asms)
        {
            // Find config method and create a SerializerConfig
            SerializerConfig config = new SerializerConfig();
            var configMethods       = asms.SelectMany(a => a.GetTypes())
                                      .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
                                      .Where(m => m.GetCustomAttribute <AotSerializerConfigAttribute>() != null)
                                      .ToArray();

            if (configMethods.Length > 1)
            {
                throw new Exception("Found more than one method with the CerasAutoGenConfig attribute!");
            }
            if (configMethods.Length == 1)
            {
                config = (SerializerConfig)configMethods[0].Invoke(null, null);
            }

            var ceras = new CerasSerializer(config);


            // Start with KnownTypes and user-marked types...
            HashSet <Type> newTypes = new HashSet <Type>();

            newTypes.AddRange(config.KnownTypes);
            newTypes.AddRange(asms.SelectMany(a => a.GetTypes()).Where(t => !t.IsAbstract && IsMarkedForAot(t)));


            // Go through each type, add all the member-types it wants to serialize as well
            HashSet <Type> processedTypes = new HashSet <Type>();

            while (newTypes.Any())
            {
                // Get first, remove from "to explore" list, and add it to the "done" list.
                var t = newTypes.First();

                if (t.IsArray)
                {
                    t = t.GetElementType();
                }

                newTypes.Remove(t);
                processedTypes.Add(t);

                if (CerasSerializer.IsPrimitiveType(t))
                {
                    // Skip int, string, Type, ...
                    continue;
                }

                if (t.IsAbstract || t.ContainsGenericParameters)
                {
                    // Can't explore abstract or open generics
                    continue;
                }

                // Explore the type, add all member types
                var schema = ceras.GetTypeMetaData(t).PrimarySchema;

                foreach (var member in schema.Members)
                {
                    if (!processedTypes.Contains(member.MemberType))
                    {
                        newTypes.Add(member.MemberType);
                    }
                }
            }


            // Only leave things that use DynamicFormatter, or have the marker attribute
            List <Type> targets         = new List <Type>();
            List <Type> debugIgnoreList = new List <Type>();           // list for types that were ignored

            foreach (var t in processedTypes)
            {
                if (CerasSerializer.IsPrimitiveType(t))
                {
                    continue;                     // Skip int, string, Type, ...
                }
                if (t.IsAbstract || t.ContainsGenericParameters)
                {
                    continue;                     // Abstract or open generics can't have instances...
                }
                var formatter     = ceras.GetSpecificFormatter(t);
                var formatterType = formatter.GetType();

                if (CerasHelpers.IsDynamicFormatter(formatterType) || CerasHelpers.IsSchemaDynamicFormatter(formatterType))
                {
                    targets.Add(t);                     // handled by Schema/DynamicFormatter; which definitely won't be available later...
                }
                else if (IsMarkedForAot(t))
                {
                    targets.Add(t);                     // explicitly marked by the user
                }
                else
                {
                    debugIgnoreList.Add(t);
                }
            }

            return(ceras, targets);

            bool IsMarkedForAot(Type t)
            {
                if (t.GetCustomAttributes(true).Any(a => a.GetType().FullName == Marker.FullName))
                {
                    return(true);                    // has 'Generate Formatter' attribute
                }
                return(false);
            }
        }
Exemple #8
0
        public void BlittingEnums()
        {
            // We will not use the reinterpret-formatter for enums usually, only if the user forces it.

            var stressTestValues = new[]
            {
                long.MinValue, long.MaxValue, -1, 0, 1, 5, 1000, 255, 256,
                rngByte, rngByte, rngByte,
                rngLong, rngLong, rngLong, rngLong, rngLong, rngLong,
            };


            var config = new SerializerConfig();

            config.OnConfigNewType = t => t.CustomResolver = (c, t2) => c.Advanced.GetFormatterResolver <ReinterpretFormatterResolver>().GetFormatter(t2);
            var ceras = new CerasSerializer(config);

            var typesToTest = new[]
            {
                typeof(TestEnumInt8),
                typeof(TestEnumUInt8),
                typeof(TestEnumInt16),
                typeof(TestEnumUInt16),
                typeof(TestEnumInt64),
                typeof(TestEnumUInt64),
            };

            var serializeMethod   = typeof(CerasSerializer).GetMethods().First(m => m.Name == nameof(CerasSerializer.Serialize) && m.GetParameters().Length == 1);
            var deserializeMethod = typeof(CerasSerializer).GetMethods().First(m => m.Name == nameof(CerasSerializer.Deserialize) && m.GetParameters().Length == 1);


            foreach (var t in typesToTest)
            {
                Type baseType     = t.GetEnumUnderlyingType();
                int  expectedSize = Marshal.SizeOf(baseType);

                var values = Enum.GetValues(t).Cast <object>().Concat(stressTestValues.Cast <object>());

                foreach (var v in values)
                {
                    var obj = Enum.ToObject(t, v);

                    // We must call Serialize<T>, and we can't use <object> because that would embed the type information
                    var data = (byte[])serializeMethod.MakeGenericMethod(t).Invoke(ceras, new object[] { obj });

                    Assert.True(data.Length == expectedSize);

                    var cloneObj = deserializeMethod.MakeGenericMethod(t).Invoke(ceras, new object[] { data });

                    Assert.True(obj.Equals(cloneObj));
                }
            }


            Assert.True(ceras.Serialize(TestEnumInt8.a).Length == 1);
            Assert.True(ceras.Serialize(TestEnumUInt8.a).Length == 1);

            Assert.True(ceras.Serialize(TestEnumInt16.a).Length == 2);
            Assert.True(ceras.Serialize(TestEnumUInt16.a).Length == 2);

            Assert.True(ceras.Serialize(TestEnumInt64.a).Length == 8);
            Assert.True(ceras.Serialize(TestEnumUInt64.a).Length == 8);
        }
 protected CollectionByProxyFormatter()
 {
     CerasSerializer.AddFormatterConstructedType(typeof(TCollection));
 }
 /// <summary>
 /// Creates a Ceras instance with legacy config (no version tolerance) in order for the server to use for database upgrades to the new serialized formats.
 /// </summary>
 public LegacyCeras()
 {
     mSerializerConfig = CreateLegacyConfig();
     mSerializer       = new CerasSerializer(mSerializerConfig);
 }
Exemple #11
0
        public unsafe void BigStructBlittedCorrectly()
        {
            BigStruct bigStruct     = new BigStruct();
            var       bigStructSize = Marshal.SizeOf(typeof(BigStruct));

            bigStruct.First  = 0xff;
            bigStruct.Second = 0.123456789;

            bigStruct.Third[0] = 0x5E;
            bigStruct.Third[1] = 0xE5;
            bigStruct.Third[2] = 0x99;

            bigStruct.Fourth = new Half4(rngShort, rngShort, rngShort, rngShort);
            bigStruct.Fifth  = '@';

            var v3Size = Marshal.SizeOf(typeof(Vector3));

            *((Vector3 *)(bigStruct.Sixth + v3Size * 0)) = new Vector3(rngFloat, rngFloat, rngFloat);
            *((Vector3 *)(bigStruct.Sixth + v3Size * 1)) = new Vector3(rngFloat, rngFloat, rngFloat);

            bigStruct.Eighth = 0x11_22_33_44___55_66_77_88;



            // Clone
            var ceras          = new CerasSerializer();
            var serializedData = ceras.Serialize(bigStruct);
            var clone          = ceras.Deserialize <BigStruct>(serializedData);


            // Direct equality
            Assert.Equal(bigStruct, clone);
            Assert.True(EqualityComparer <BigStruct> .Default.Equals(bigStruct, clone));


            // Manual equality
            Assert.Equal(bigStruct.First, clone.First);
            Assert.Equal(bigStruct.Second, clone.Second);

            Assert.Equal(bigStruct.Third[0], clone.Third[0]);
            Assert.Equal(bigStruct.Third[1], clone.Third[1]);
            Assert.Equal(bigStruct.Third[2], clone.Third[2]);

            Assert.Equal(bigStruct.Fourth, clone.Fourth);
            Assert.Equal(bigStruct.Fourth.Left, clone.Fourth.Left);
            Assert.Equal(bigStruct.Fourth.Right, clone.Fourth.Right);

            Assert.Equal(bigStruct.Fifth, clone.Fifth);
            Assert.Equal(*((Vector3 *)(bigStruct.Sixth + v3Size * 0)), *((Vector3 *)(clone.Sixth + v3Size * 0)));
            Assert.Equal(*((Vector3 *)(bigStruct.Sixth + v3Size * 1)), *((Vector3 *)(clone.Sixth + v3Size * 1)));


            // Binary equality
            var oriStructAddr   = &bigStruct;
            var cloneStructAddr = &clone;

            Assert.Equal(oriStructAddr[0], cloneStructAddr[0]);

            // Byte equality
            {
                byte *oriBytes   = (byte *)oriStructAddr;
                byte *cloneBytes = (byte *)cloneStructAddr;

                for (int i = 0; i < bigStructSize; i++)
                {
                    Assert.Equal(oriBytes[i], cloneBytes[i]);
                    Assert.Equal(*(oriBytes + i), *(cloneBytes + i));
                }
            }


            // Ensure the layout of the serialized data was not
            // changed somehow and that no extra bytes were added
            {
                Assert.True(serializedData.Length == bigStructSize);

                byte *oriBytes   = (byte *)oriStructAddr;
                byte *cloneBytes = (byte *)cloneStructAddr;

                for (int i = 0; i < bigStructSize; i++)
                {
                    Assert.True(serializedData[i] == *(oriBytes + i));
                    Assert.True(serializedData[i] == *(cloneBytes + i));
                }
            }
        }
Exemple #12
0
 public TypeFormatter(CerasSerializer serializer)
 {
     _serializer = serializer;
     _typeBinder = serializer.TypeBinder;
 }
Exemple #13
0
 public Encoding()
 {
     _ceras = new CerasSerializer();
 }
Exemple #14
0
 public CollectionFormatterResolver(CerasSerializer serializer)
 {
     _serializer = serializer;
 }
Exemple #15
0
        static void PerfTest()
        {
            // todo: compare against msgpack

            // 1.) Primitives
            // Compare encoding of a mix of small and large numbers to test var-int encoding speed
            var rng = new Random();

            List <int> numbers = new List <int>();

            for (int i = 0; i < 200; i++)
            {
                numbers.Add(i);
            }
            for (int i = 1000; i < 1200; i++)
            {
                numbers.Add(i);
            }
            for (int i = short.MaxValue + 1000; i < short.MaxValue + 1200; i++)
            {
                numbers.Add(i);
            }
            numbers = numbers.OrderBy(n => rng.Next(1000)).ToList();

            var ceras = new CerasSerializer();

            var cerasData = ceras.Serialize(numbers);



            // 2.) Object Data
            // Many fields/properties, some nesting



            /*
             * todo
             *
             * - prewarm proxy pool; prewarm
             *
             * - would ThreadsafeTypeKeyHashTable actually help for the cases where we need to type switch?
             *
             * - reference lookups take some time; we could disable them by default and instead let the user manually enable reference serialization per type
             *      config.EnableReference(typeof(MyObj));
             *
             * - directly inline all primitive reader/writer functions. Instead of creating an Int32Formatter the dynamic formatter directly calls the matching method
             *
             * - potentially improve number encoding speed (varint encoding is naturally not super fast, maybe we can apply some tricks...)
             *
             * - have DynamicFormatter generate its expressions, but inline the result directly to the reference formatter
             *
             * - reference proxies: use array instead of a list, don't return references to a pool, just reset them!
             *
             * - when we're later dealing with version tolerance, we write all the the type definitions first, and have a skip offset in front of each object
             *
             * - avoid overhead of "Formatter" classes for all primitives and directly use them, they can also be accessed through a static generic
             *
             * - would a specialized formatter for List<> help? maybe, we'd avoid interfaces vtable calls
             *
             * - use static generic caching where possible (rarely the case since ceras can be instantiated multiple times with different settings)
             *
             * - primitive arrays can be cast and blitted directly
             *
             * - optimize simple properties: serializing the backing field directly, don't call Get/Set (add a setting so it can be deactivated)
             */
        }
Exemple #16
0
        public void Setup()
        {
            //
            // Create example data
            var parent1 = new Person
            {
                Age       = -901,
                FirstName = "Parent 1",
                LastName  = "abc",
                Sex       = Sex.Male,
            };
            var parent2 = new Person
            {
                Age       = 7881964,
                FirstName = "Parent 2",
                LastName  = "xyz",
                Sex       = Sex.Female,
            };

            _person = new Person
            {
                Age       = 5,
                FirstName = "Riki",
                LastName  = "Example Person Object",
                Sex       = Sex.Unknown,
                Parent1   = parent1,
                Parent2   = parent2,
            };

            _list = Enumerable.Range(25000, 100).Select(x => new Person {
                Age = x, FirstName = "a", LastName = "b", Sex = Sex.Female
            }).ToArray();

            //
            // Config Serializers
            _wire = new Wire.Serializer(new Wire.SerializerOptions(knownTypes: new Type[] { typeof(Person), typeof(Person[]) }));

            _netSerializer = new NetSerializer.Serializer(rootTypes: new Type[] { typeof(Person), typeof(Person[]) });

            var config = new SerializerConfig();

            config.DefaultTargets = TargetMember.AllPublic;
            var knownTypes = new[] { typeof(Person), typeof(List <>), typeof(Person[]) };

            config.KnownTypes.AddRange(knownTypes);
            config.PreserveReferences = false;
            _ceras = new CerasSerializer(config);

            //
            // Run each serializer once to verify they work correctly!
            if (!Equals(RunCeras(_person), _person))
            {
                ThrowError();
            }
            if (!Equals(RunJson(_person), _person))
            {
                ThrowError();
            }
            if (!Equals(RunMessagePackCSharp(_person), _person))
            {
                ThrowError();
            }
            if (!Equals(RunProtobuf(_person), _person))
            {
                ThrowError();
            }
            if (!Equals(RunWire(_person), _person))
            {
                ThrowError();
            }
            if (!Equals(RunNetSerializer(_person), _person))
            {
                ThrowError();
            }

            void ThrowError() => throw new InvalidOperationException("Cannot continue with the benchmark because a serializer does not round-trip an object correctly. (Benchmark results will be wrong)");
        }
Exemple #17
0
 public DelegateFormatter(CerasSerializer ceras)
 {
     _ceras = ceras;
     _methodInfoFormatter = ceras.GetFormatter <MethodInfo>();
     _targetFormatter     = ceras.GetFormatter <object>();
 }
 public DynamicObjectFormatterResolver(CerasSerializer ceras)
 {
     _ceras = ceras;
     _versionToleranceMode = ceras.Config.VersionTolerance.Mode;
 }
Exemple #19
0
        internal static Expression <SerializeDelegate <T> > GenerateSerializer(CerasSerializer ceras, Schema schema, bool isSchemaFormatter, bool isStatic)
        {
            var members      = schema.Members;
            var refBufferArg = Parameter(typeof(byte[]).MakeByRefType(), "buffer");
            var refOffsetArg = Parameter(typeof(int).MakeByRefType(), "offset");
            var valueArg     = Parameter(typeof(T), "value");

            if (isStatic)
            {
                valueArg = null;
            }


            var body   = new List <Expression>();
            var locals = new List <ParameterExpression>();

            ParameterExpression startPos = null, size = null;

            if (isSchemaFormatter)
            {
                locals.Add(startPos = Variable(typeof(int), "startPos"));
                locals.Add(size     = Variable(typeof(int), "size"));
            }

            Dictionary <Type, ConstantExpression> typeToFormatter = new Dictionary <Type, ConstantExpression>();

            foreach (var m in members.Where(m => !m.IsSkip).DistinctBy(m => m.MemberType))
            {
                typeToFormatter.Add(m.MemberType, Constant(ceras.GetReferenceFormatter(m.MemberType)));
            }


            // Serialize all members
            foreach (var member in members)
            {
                if (member.IsSkip)
                {
                    continue;
                }

                // Get the formatter and its Serialize method
                var formatterExp    = typeToFormatter[member.MemberType];
                var formatter       = formatterExp.Value;
                var serializeMethod = formatter.GetType().GetMethod(nameof(IFormatter <int> .Serialize));
                Debug.Assert(serializeMethod != null, "Can't find serialize method on formatter " + formatter.GetType().FullName);

                // Access the field that we want to serialize
                var fieldExp = MakeMemberAccess(valueArg, member.MemberInfo);

                // Call "Serialize"
                if (!isSchemaFormatter)
                {
                    var serializeCall = Call(formatterExp, serializeMethod, refBufferArg, refOffsetArg, fieldExp);
                    body.Add(serializeCall);
                }
                else
                {
                    // remember current position
                    // startPos = offset;
                    body.Add(Assign(startPos, refOffsetArg));

                    // reserve space for the length prefix
                    // offset += 4;
                    body.Add(AddAssign(refOffsetArg, Constant(FieldSizePrefixBytes)));

                    // Serialize(...) write the actual data
                    body.Add(Call(
                                 instance: formatterExp,
                                 method: serializeMethod,
                                 arg0: refBufferArg,
                                 arg1: refOffsetArg,
                                 arg2: MakeMemberAccess(valueArg, member.MemberInfo)
                                 ));

                    // calculate the size of what we just wrote
                    // size = (offset - startPos) - 4;
                    body.Add(Assign(size, Subtract(Subtract(refOffsetArg, startPos), Constant(FieldSizePrefixBytes))));

                    // go back to where we started and write the size into the reserved space
                    // offset = startPos;
                    body.Add(Assign(refOffsetArg, startPos));

                    // WriteInt32( size )
                    body.Add(Call(
                                 method: _sizeWriteMethod,
                                 arg0: refBufferArg,
                                 arg1: refOffsetArg,
                                 arg2: Convert(size, _sizeType)
                                 ));

                    // continue after the written data
                    // offset = startPos + skipOffset;
                    body.Add(Assign(refOffsetArg, Add(Add(startPos, size), Constant(FieldSizePrefixBytes))));
                }
            }

            var serializeBlock = Block(variables: locals, expressions: body);


            if (isStatic)
            {
                valueArg = Parameter(typeof(T), "value");
            }

            return(Lambda <SerializeDelegate <T> >(serializeBlock, refBufferArg, refOffsetArg, valueArg));
        }
Exemple #20
0
 public MemberInfoFormatter(CerasSerializer serializer)
 {
     _stringFormatter = serializer.GetFormatter <string>();
     _typeFormatter   = (IFormatter <Type>)serializer.GetSpecificFormatter(typeof(Type));
 }
Exemple #21
0
 public UriFormatter()
 {
     CerasSerializer.AddFormatterConstructedType(typeof(Uri));
 }
Exemple #22
0
        /*
         #if NETFRAMEWORK
         * static void TestDynamic()
         * {
         *      dynamic dyn = new ExpandoObject();
         *      dyn.number = 5;
         *      dyn.list = new List<string> { "a", "b"};
         *      dyn.c = "c";
         *      dyn.func = new Func<string>(((object)dyn).ToString);
         *
         *      var ceras = new CerasSerializer();
         *      var data = ceras.Serialize(dyn);
         *      var dyn2 = ceras.Deserialize<dynamic>(data);
         * }
         #endif
         */

        static void TestBitmapFormatter()
        {
            var config = new SerializerConfig();

            config.Advanced.BitmapMode = BitmapMode.SaveAsBmp;
            var ceras = new CerasSerializer(config);

            var home      = Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%");
            var downloads = Path.Combine(home, "Downloads");

            var images = new Image[]
            {
                // todo: add test images
                Image.FromFile(Path.Combine(downloads, @".png")),
            };

            for (int iteration = 0; iteration < 5; iteration++)
            {
                var imgData1 = ceras.Serialize(images);
                var clones   = ceras.Deserialize <Image[]>(imgData1);

                for (var cloneIndex = 0; cloneIndex < clones.Length; cloneIndex++)
                {
                    var c = clones[cloneIndex];
                    c.Dispose();
                    clones[cloneIndex] = null;
                }
            }


            byte[] sharedBuffer = new byte[100];
            int    offset       = 0;

            foreach (var sourceImage in images)
            {
                offset += ceras.Serialize(sourceImage, ref sharedBuffer, offset);
            }
            offset += ceras.Serialize(images, ref sharedBuffer, offset);

            int writtenLength = offset;

            List <Image> clonedImages = new List <Image>();

            offset = 0;

            for (var i = 0; i < images.Length; i++)
            {
                Image img = null;
                ceras.Deserialize(ref img, sharedBuffer, ref offset);
                clonedImages.Add(img);
            }
            Image[] imageArrayClone = null;
            ceras.Deserialize(ref imageArrayClone, sharedBuffer, ref offset);

            // Ensure all bytes consumed again
            Debug.Assert(offset == writtenLength);

            foreach (var img in clonedImages)
            {
                img.Dispose();
            }
            foreach (var img in imageArrayClone)
            {
                img.Dispose();
            }
        }
Exemple #23
0
        static (CerasSerializer, List <Type>) CreateSerializerAndTargets(IEnumerable <Assembly> asms)
        {
            // Find config method and create a SerializerConfig
            SerializerConfig config = new SerializerConfig();
            var configMethods       = asms.SelectMany(a => a.GetTypes())
                                      .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
                                      .Where(m => m.GetCustomAttribute <CerasAutoGenConfigAttribute>() != null)
                                      .ToArray();

            if (configMethods.Length > 1)
            {
                throw new Exception("Found more than one method with the CerasAutoGenConfig attribute!");
            }
            if (configMethods.Length == 1)
            {
                config = (SerializerConfig)configMethods[0].Invoke(null, null);
            }

            config.VersionTolerance.Mode = VersionToleranceMode.Disabled;             // ensure VersionTolerance is off so we don't accidentally get 'SchemaDynamicFormatter'
            var ceras = new CerasSerializer(config);


            // Start with KnownTypes...
            HashSet <Type> newTypes = new HashSet <Type>();

            newTypes.AddRange(config.KnownTypes);

            // And also include all marked types
            var marker = typeof(CerasAutoGenFormatterAttribute);

            bool HasMarker(Type t) => t.GetCustomAttributes(true)
            .Any(a => a.GetType().FullName == marker.FullName);

            var markedTargets = asms.SelectMany(a => a.GetTypes())
                                .Where(t => !t.IsAbstract && HasMarker(t));

            newTypes.AddRange(markedTargets);


            // Go through each type, add all the member-types it wants to serialize as well
            HashSet <Type> allTypes = new HashSet <Type>();

            while (newTypes.Any())
            {
                // Get first, remove from "to explore" list, and add it to the "done" list.
                var t = newTypes.First();
                newTypes.Remove(t);
                allTypes.Add(t);


                if (CerasSerializer.IsPrimitiveType(t))
                {
                    // Skip int, string, Type, ...
                    continue;
                }

                if (t.IsAbstract || t.ContainsGenericParameters)
                {
                    // Can't explore abstract or open generics
                    continue;
                }

                // Explore the type, add all member types
                var schema = ceras.GetTypeMetaData(t).PrimarySchema;

                foreach (var member in schema.Members)
                {
                    if (!allTypes.Contains(member.MemberType))
                    {
                        newTypes.Add(member.MemberType);
                    }
                }
            }


            // Only leave things that use DynamicFormatter, or have the marker attribute
            List <Type> targets = new List <Type>();

            foreach (var t in allTypes)
            {
                var f     = ceras.GetSpecificFormatter(t);
                var fType = f.GetType();

                if (fType.IsGenericType && fType.GetGenericTypeDefinition().Name == typeof(DynamicFormatter <int>).GetGenericTypeDefinition().Name)
                {
                    targets.Add(t);
                }
                else if (HasMarker(t))
                {
                    targets.Add(t);
                }
            }

            return(ceras, targets);
        }
Exemple #24
0
        static void ExpressionTreesTest()
        {
            // Primitive test (private readonly in a base type)
            {
                SerializerConfig config = new SerializerConfig();
                config.ConfigType <ReadonlyTestClass>()
                .ConstructByUninitialized()
                .SetReadonlyHandling(ReadonlyFieldHandling.ForcedOverwrite)
                .SetTargetMembers(TargetMember.PrivateFields);

                var ceras = new CerasSerializer(config);

                var obj  = new ReadonlyTestClass("a");
                var data = ceras.Serialize(obj);

                var clone = ceras.Deserialize <ReadonlyTestClass>(data);

                Debug.Assert(obj.GetName() == clone.GetName());
                Debug.Assert(obj.GetBaseName() == clone.GetBaseName());

                Console.WriteLine();
            }

            // Small test 1
            {
                Expression <Func <string, int, char> > getCharAtIndex = (text, index) => text.ElementAt(index);
                MethodCallExpression body = (MethodCallExpression)getCharAtIndex.Body;

                // Serialize and deserialize delegate
                SerializerConfig config = new SerializerConfig();
                var ceras = new CerasSerializer(config);

                var data      = ceras.Serialize <object>(body);
                var dataAsStr = Encoding.ASCII.GetString(data).Replace('\0', ' ');

                var clonedExp = (MethodCallExpression)ceras.Deserialize <object>(data);

                Debug.Assert(clonedExp.Method == body.Method);
                Debug.Assert(clonedExp.Arguments.Count == body.Arguments.Count);
            }

            // Small test 2
            {
                // Test data
                string inputString = "abcdefgh";


                Expression <Func <string, int, char> > getCharAtIndex = (text, index) => (text.ElementAt(index).ToString() + text[index])[0];
                var  del1 = getCharAtIndex.Compile();
                char c1   = del1(inputString, 2);


                // Serialize and deserialize expression
                SerializerConfig config = new SerializerConfig();
                var ceras = new CerasSerializer(config);

                var data      = ceras.Serialize(getCharAtIndex);
                var dataAsStr = Encoding.ASCII.GetString(data).Replace('\0', ' ');

                var clonedExp = ceras.Deserialize <Expression <Func <string, int, char> > >(data);


                // Compile the restored expression, check if it works and returns the same result
                var del2 = clonedExp.Compile();

                // Check single case
                var c2 = del2(inputString, 2);
                Debug.Assert(c1 == c2);

                // Check all cases
                for (int i = 0; i < inputString.Length; i++)
                {
                    Debug.Assert(del1(inputString, i) == del2(inputString, i));
                }
            }
        }
 public CollectionFormatterResolver(CerasSerializer ceras)
 {
     _ceras = ceras;
 }
Exemple #26
0
        static void EnsureSealedTypesThrowsException()
        {
            //
            // 1. Check while serializing
            //
            var obj = new List <object>();

            obj.Add(5);
            obj.Add(DateTime.Now);
            obj.Add("asdasdas");
            obj.Add(new Person()
            {
                Name = "abc"
            });

            var config = new SerializerConfig();

            config.KnownTypes.Add(typeof(List <>));
            config.KnownTypes.Add(typeof(int));
            // Some types not added on purpose

            // Should be true by default!
            Debug.Assert(config.Advanced.SealTypesWhenUsingKnownTypes);

            var ceras = new CerasSerializer(config);

            try
            {
                ceras.Serialize(obj);

                Debug.Assert(false, "this line should not be reached, we want an exception here!");
            }
            catch (Exception e)
            {
                // all good
            }

            //
            // 2. Check while deserializing
            //
            config = new SerializerConfig();
            config.KnownTypes.Add(typeof(List <>));
            config.KnownTypes.Add(typeof(int));
            config.Advanced.SealTypesWhenUsingKnownTypes = false;
            ceras = new CerasSerializer(config);

            var data = ceras.Serialize(obj);

            config = new SerializerConfig();
            config.KnownTypes.Add(typeof(List <>));
            config.KnownTypes.Add(typeof(int));
            config.Advanced.SealTypesWhenUsingKnownTypes = true;
            ceras = new CerasSerializer(config);

            try
            {
                ceras.Deserialize <List <object> >(data);

                Debug.Assert(false, "this line should not be reached, we want an exception here!");
            }
            catch (Exception e)
            {
                // all good
            }
        }
 public DynamicObjectFormatterResolver(CerasSerializer serializer)
 {
     _serializer = serializer;
 }
Exemple #28
0
        static void ReadonlyTest()
        {
            // Test #1:
            // By default the setting is off. Fields are ignored.
            {
                SerializerConfig config = new SerializerConfig();
                CerasSerializer  ceras  = new CerasSerializer(config);

                ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "xyz", new ReadonlyFieldsTest.ContainerThingA {
                    Setting1 = 10, Setting2 = "asdasdas"
                });

                var data = ceras.Serialize(obj);

                var cloneNew = ceras.Deserialize <ReadonlyFieldsTest>(data);

                Debug.Assert(cloneNew.Int == 1);
                Debug.Assert(cloneNew.String == "a");
                Debug.Assert(cloneNew.Container == null);
            }

            // Test #2A:
            // In the 'Members' mode we expect an exception for readonly value-typed fields.
            {
                SerializerConfig config = new SerializerConfig();
                config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.Members;

                config.ConfigType <ReadonlyFieldsTest>()
                .ConfigMember(f => f.Int).Include()
                .ConfigMember(f => f.String).Include();


                CerasSerializer ceras = new CerasSerializer(config);

                ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA {
                    Setting1 = 555555, Setting2 = "555555555"
                });

                var data = ceras.Serialize(obj);

                ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(6, "66666", null);

                bool gotException = false;
                try
                {
                    var cloneNew = ceras.Deserialize <ReadonlyFieldsTest>(data);
                }
                catch (Exception ex)
                {
                    gotException = true;
                }

                Debug.Assert(gotException);                 // We want an exception
            }

            // Test #2B:
            // In the 'Members' mode (when not dealing with value-types)
            // we want Ceras to re-use the already existing object
            {
                SerializerConfig config = new SerializerConfig();
                config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.Members;

                config.ConfigType <ReadonlyFieldsTest>()
                .ConfigMember(f => f.Int).Exclude()
                .ConfigMember(f => f.String).Exclude()
                .ConfigMember(f => f.Container).Include(ReadonlyFieldHandling.Members);

                CerasSerializer ceras = new CerasSerializer(config);

                ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA {
                    Setting1 = 555555, Setting2 = "555555555"
                });

                var data = ceras.Serialize(obj);

                var newContainer = new ReadonlyFieldsTest.ContainerThingA {
                    Setting1 = -1, Setting2 = "this should get overwritten"
                };
                ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(6, "66666", newContainer);

                // populate existing data
                ceras.Deserialize <ReadonlyFieldsTest>(ref existingTarget, data);

                // The simple fields should have been ignored
                Debug.Assert(existingTarget.Int == 6);
                Debug.Assert(existingTarget.String == "66666");

                // The reference itself should not have changed
                Debug.Assert(existingTarget.Container == newContainer);

                // The content of the container should be changed now
                Debug.Assert(newContainer.Setting1 == 555555);
                Debug.Assert(newContainer.Setting2 == "555555555");
            }


            // Test #3
            // In 'ForcedOverwrite' mode Ceras should fix all possible mismatches by force (reflection),
            // which means that it should work exactly like as if the field were not readonly.
            {
                SerializerConfig config = new SerializerConfig();
                config.Advanced.ReadonlyFieldHandling = ReadonlyFieldHandling.ForcedOverwrite;
                CerasSerializer ceras = new CerasSerializer(config);

                // This time we want Ceras to fix everything, reference mismatches and value mismatches alike.

                ReadonlyFieldsTest obj = new ReadonlyFieldsTest(5, "55555", new ReadonlyFieldsTest.ContainerThingA {
                    Setting1 = 324, Setting2 = "1134"
                });

                var data = ceras.Serialize(obj);

                ReadonlyFieldsTest existingTarget = new ReadonlyFieldsTest(123, null, new ReadonlyFieldsTest.ContainerThingB());

                // populate existing object
                ceras.Deserialize <ReadonlyFieldsTest>(ref existingTarget, data);


                // Now we really check for everything...

                // Sanity check, no way this could happen, but lets make sure.
                Debug.Assert(ReferenceEquals(obj, existingTarget) == false);

                // Fields should be like in the original
                Debug.Assert(existingTarget.Int == 5);
                Debug.Assert(existingTarget.String == "55555");

                // The container type was wrong, Ceras should have fixed that by instantiating a different object
                // and writing that into the readonly field.
                var container = existingTarget.Container as ReadonlyFieldsTest.ContainerThingA;
                Debug.Assert(container != null);

                // Contents of the container should be correct as well
                Debug.Assert(container.Setting1 == 324);
                Debug.Assert(container.Setting2 == "1134");
            }

            // Test #4:
            // Everything should work fine when using the MemberConfig attribute as well
            {
                var ceras = new CerasSerializer();

                var obj = new ReadonlyFieldsTest2();
                obj.Numbers.Clear();
                obj.Numbers.Add(234);

                var data = ceras.Serialize(obj);

                var clone        = new ReadonlyFieldsTest2();
                var originalList = clone.Numbers;
                ceras.Deserialize(ref clone, data);

                Debug.Assert(originalList == clone.Numbers);           // actual reference should not have changed
                Debug.Assert(clone.Numbers.Count == 1);                // amount of entries should have changed
                Debug.Assert(clone.Numbers[0] == 234);                 // entry itself should be right
            }

            // todo: also test the case where the existing object does not match the expected type
        }
Exemple #29
0
 public PrimitiveResolver(CerasSerializer serializer)
 {
     _serializer = serializer;
 }
Exemple #30
0
 public ReflectionTypesFormatterResolver(CerasSerializer serializer)
 {
     _serializer = serializer;
 }