public KeyValuePairFormatterResolver(CerasSerializer serializer) { _serializer = serializer; }
public ReferenceFormatter(CerasSerializer serializer) { _serializer = serializer; _typeFormatter = (TypeFormatter)serializer.GetSpecificFormatter(typeof(Type)); }
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>)); }
/* * * 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)); }
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); } }
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); }
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)); } } }
public TypeFormatter(CerasSerializer serializer) { _serializer = serializer; _typeBinder = serializer.TypeBinder; }
public Encoding() { _ceras = new CerasSerializer(); }
public CollectionFormatterResolver(CerasSerializer serializer) { _serializer = serializer; }
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) */ }
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)"); }
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; }
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)); }
public MemberInfoFormatter(CerasSerializer serializer) { _stringFormatter = serializer.GetFormatter <string>(); _typeFormatter = (IFormatter <Type>)serializer.GetSpecificFormatter(typeof(Type)); }
public UriFormatter() { CerasSerializer.AddFormatterConstructedType(typeof(Uri)); }
/* #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(); } }
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); }
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; }
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; }
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 }
public PrimitiveResolver(CerasSerializer serializer) { _serializer = serializer; }
public ReflectionTypesFormatterResolver(CerasSerializer serializer) { _serializer = serializer; }