示例#1
0
        void LeaveRecursive(RecursionMode leavingMode)
        {
            _recursionDepth--;

            if (_recursionDepth == 0)
            {
                // Leave the data as it is, we'll use it for the next call
                _mode = RecursionMode.Idle;
            }
            else
            {
                // We need to restore the previous data
                _instanceDataPool.ReturnObject(InstanceData);
                InstanceData = _recursionStack.Pop();
            }
        }
示例#2
0
        void EnterRecursive(RecursionMode enteringMode)
        {
            _recursionDepth++;

            if (_recursionDepth == 1)
            {
                // First level, just use the existing data block
                _mode = enteringMode;
            }
            else
            {
                if (_mode != enteringMode)
                {
                    throw new InvalidOperationException("Cannot start a serialization call while a deserialization is still in progress (and vice versa)");
                }

                // Next level of recursion
                _recursionStack.Push(InstanceData);
                InstanceData = _instanceDataPool.RentObject();
            }
        }
示例#3
0
        /// <summary>
        /// Creates a new CerasSerializer, be sure to check out the tutorial.
        /// </summary>
        public CerasSerializer(SerializerConfig config = null)
        {
            Config = config ?? new SerializerConfig();

            if (Config.ExternalObjectResolver == null)
            {
                Config.ExternalObjectResolver = new ErrorResolver();
            }

            if (Config.Advanced.UseReinterpretFormatter && Config.VersionTolerance.Mode != VersionToleranceMode.Disabled)
            {
                throw new NotSupportedException("You can not use 'UseReinterpretFormatter' together with version tolerance. Either disable version tolerance, or use the old formatter for blittable types by setting 'Config.Advanced.UseReinterpretFormatter' to false.");
            }

            if (Config.Advanced.AotMode != AotMode.None && Config.VersionTolerance.Mode != VersionToleranceMode.Disabled)
            {
                throw new NotSupportedException("You can not use 'AotMode.Enabled' and version tolerance at the same time for now. If you would like this feature implemented, please open an issue on GitHub explaining your use-case, or join the Discord server.");
            }

            TypeBinder          = Config.Advanced.TypeBinder ?? new NaiveTypeBinder();
            DiscardObjectMethod = Config.Advanced.DiscardObjectMethod;

            _userResolvers = Config.OnResolveFormatter.ToArray();

            // Int, Float, Enum, ...
            _resolvers.Add(new PrimitiveResolver(this));

            // Fast native copy for unmanaged types;
            // can not handle generic structs like ValueTuple<> because they always have to be ".IsAutoLayout"
            _resolvers.Add(new ReinterpretFormatterResolver(this));

            // DateTime, Guid, KeyValuePair, Tuple, ...
            _resolvers.Add(new StandardFormatterResolver(this));

            // Array, List, Dictionary, ICollection<T>, ...
            _resolvers.Add(new CollectionFormatterResolver(this));

            // String Formatter should never be wrapped in a RefFormatter, that's too slow and not necessary
            IFormatter stringFormatter;

            if (Config.Advanced.SizeLimits.MaxStringLength < uint.MaxValue)
            {
                stringFormatter = new MaxSizeStringFormatter(Config.Advanced.SizeLimits.MaxStringLength);
            }
            else
            {
                stringFormatter = new StringFormatter();
            }
            InjectDependencies(stringFormatter);
            SetFormatters(typeof(string), stringFormatter, stringFormatter);

            //
            // Type formatter is the basis for all complex objects,
            // It is special and has its own caching system (so no wrapping in a ReferenceFormatter)
            var typeFormatter = new TypeFormatter(this);

            var runtimeType = GetType().GetType();

            SetFormatters(typeof(Type), typeFormatter, typeFormatter);
            SetFormatters(runtimeType, typeFormatter, typeFormatter);

            // MemberInfos (FieldInfo, RuntimeFieldInfo, ...)
            _resolvers.Add(new ReflectionFormatterResolver(this));

            // DynamicObjectResolver is a special case, so it is not in the resolver-list
            // That is because we only want to have specific resolvers in the resolvers-list
            _dynamicResolver = new DynamicObjectFormatterResolver(this);

            // System.Linq.Expressions - mostly handled by special configurations and DynamicFormatter, but there are some special cases.
            _resolvers.Add(new ExpressionFormatterResolver());


            //
            // Basic setup is done
            // Now calculate the protocol checksum
            _knownTypes = Config.KnownTypes.ToArray();
            if (Config.KnownTypes.Distinct().Count() != _knownTypes.Length)
            {
                // We want a *good* error message. Simply saying "can't contain any type multiple times" is not enough.
                // We have to figure out which types are there more than once.
                HashSet <Type> hashSet         = new HashSet <Type>();
                List <Type>    foundDuplicates = new List <Type>();

                for (int i = 0; i < _knownTypes.Length; i++)
                {
                    var t = _knownTypes[i];
                    if (!hashSet.Add(t))
                    {
                        if (!foundDuplicates.Contains(t))
                        {
                            foundDuplicates.Add(t);
                        }
                    }
                }

                var duplicateTypesStr = string.Join(", ", foundDuplicates.Select(t => t.Name));

                throw new Exception("KnownTypes can not contain any type multiple times! Your KnownTypes collection contains the following types more than once: " + duplicateTypesStr);
            }

            //
            // Generate checksum
            {
                foreach (var t in _knownTypes)
                {
                    ProtocolChecksum.Add(t.FullName);

                    if (t.IsEnum)
                    {
                        // Enums are a special case, they are classes internally, but they only have one field ("__value")
                        // We're always serializing them in binary with their underlying type, so there's no reason changes like Adding/Removing/Renaming
                        // enum-members could ever cause any binary incompatibility
                        //
                        // A change in the base-type however WILL cause problems!
                        ProtocolChecksum.Add(t.GetEnumUnderlyingType().FullName);
                        continue;
                    }

                    if (!t.ContainsGenericParameters)
                    {
                        var meta = GetTypeMetaData(t);
                        if (meta.PrimarySchema != null)
                        {
                            foreach (var m in meta.PrimarySchema.Members)
                            {
                                ProtocolChecksum.Add(m.MemberType.FullName);
                                ProtocolChecksum.Add(m.MemberName);

                                foreach (var a in m.MemberInfo.GetCustomAttributes(true))
                                {
                                    ProtocolChecksum.Add(a.ToString());
                                }
                            }
                        }
                    }
                }

                ProtocolChecksum.Finish();
            }

            //
            // We can already pre-warm formatters
            // - dynamic serializers generate their code
            // - reference formatters generate their wrappers
            foreach (var t in _knownTypes)
            {
                if (!t.ContainsGenericParameters)
                {
                    GetReferenceFormatter(t);
                }
            }



            //
            // Finally we need "instance data"
            _instanceDataPool = new FactoryPool <InstanceData>(p =>
            {
                var d                    = new InstanceData();
                d.CurrentRoot            = null;
                d.ObjectCache            = new ObjectCache();
                d.TypeCache              = new TypeCache(_knownTypes);
                d.EncounteredSchemaTypes = new HashSet <Type>();

                return(d);
            });
            InstanceData = _instanceDataPool.RentObject();

            if (Config.Advanced.SealTypesWhenUsingKnownTypes)
            {
                if (_knownTypes.Length > 0)
                {
                    typeFormatter.Seal();
                }
            }
        }
示例#4
0
        RecursionMode _mode = RecursionMode.Idle;         // while in one mode we cannot enter the others


        public CerasSerializer(SerializerConfig config = null)
        {
            Config = config ?? new SerializerConfig();

            // Check if the config is even valid
            if (Config.EmbedChecksum && !Config.GenerateChecksum)
            {
                throw new InvalidOperationException($"{nameof(Config.GenerateChecksum)} must be true if {nameof(Config.EmbedChecksum)} is true!");
            }
            if (Config.EmbedChecksum && Config.PersistTypeCache)
            {
                throw new InvalidOperationException($"You can't have '{nameof(Config.EmbedChecksum)}' and also have '{nameof(Config.PersistTypeCache)}' because adding new types changes the checksum. You can use '{nameof(Config.GenerateChecksum)}' alone, but the checksum might change after every serialization call...");
            }

            if (Config.ExternalObjectResolver == null)
            {
                Config.ExternalObjectResolver = new ErrorResolver();
            }


            _userResolver = Config.OnResolveFormatter;

            // Int, Float, Enum, String
            _resolvers.Add(new PrimitiveResolver(this));

            _resolvers.Add(new ReflectionTypesFormatterResolver(this));
            _resolvers.Add(new KeyValuePairFormatterResolver(this));
            _resolvers.Add(new CollectionFormatterResolver(this));

            // DateTime, Guid
            _resolvers.Add(new BclFormatterResolver());

            // DynamicObjectResolver is a special case, so it is not in the resolver-list
            // That is because we only want to have specific resolvers in the resolvers-list
            _dynamicResolver = new DynamicObjectFormatterResolver(this);

            // Type formatter is the basis for all complex objects
            var typeFormatter = new TypeFormatter(this);

            _specificFormatters.Add(typeof(Type), typeFormatter);

            //if (Config.KnownTypes.Count > 0)
            //	if (Config.SealTypesWhenUsingKnownTypes)
            //		typeFormatter.Seal();

            _typeFormatter = (IFormatter <Type>)GetSpecificFormatter(typeof(Type));


            //
            // Basic setup is done
            // Now we're adding our "known types"
            // generating serializers and updating the protocol checksum
            //

            Config.KnownTypes.Seal();
            foreach (var t in Config.KnownTypes)
            {
                if (Config.GenerateChecksum)
                {
                    ProtocolChecksum.Add(t.FullName);

                    if (t.IsEnum)
                    {
                        // Enums are a special case, they are classes internally, but they only have one field ("__value")
                        // We're always serializing them in binary with their underlying type, so there's no reason changes like Adding/Removing/Renaming
                        // enum-members could ever cause any binary incompatibility
                        //
                        // A change in the base-type however WILL cause problems!
                        //
                        // Note, even without this if() everything would be ok, since the code below also writes the field-type
                        // but it is better to *explicitly* do this here and explain why we're doing it!
                        ProtocolChecksum.Add(t.GetEnumUnderlyingType().FullName);

                        continue;
                    }


                    var members = DynamicObjectFormatter <object> .GetSerializableMembers(t, Config.DefaultTargets, Config.ShouldSerializeMember);

                    foreach (var m in members)
                    {
                        ProtocolChecksum.Add(m.MemberType.FullName);
                        ProtocolChecksum.Add(m.Name);

                        foreach (var a in m.MemberInfo.GetCustomAttributes(true))
                        {
                            ProtocolChecksum.Add(a.ToString());
                        }
                    }
                }
            }

            if (Config.GenerateChecksum)
            {
                ProtocolChecksum.Finish();
            }

            //
            // Finally we need "instance data"
            _instanceDataPool = new FactoryPool <InstanceData>(p =>
            {
                var d         = new InstanceData();
                d.CurrentRoot = null;
                d.ObjectCache = new ObjectCache();
                d.TypeCache   = new ObjectCache();

                foreach (var t in Config.KnownTypes)
                {
                    d.TypeCache.RegisterObject(t);                     // For serialization
                    d.TypeCache.AddKnownType(t);                       // For deserialization
                }

                return(d);
            });
            InstanceData = _instanceDataPool.RentObject();
        }