예제 #1
0
 public static void AddUnityFormatters(this SerializerConfig config) => config.OnResolveFormatter.Add(GetFormatter);
예제 #2
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 == VersionTolerance.AutomaticEmbedded)
            {
                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.");
            }

            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 DynamicObjectFormatter, 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();
                }
            }
        }
예제 #3
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();
        }