예제 #1
0
        public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter)
        {
            if (!type.IsArray)
            {
                formatter = null;
                return(false);
            }

            if (type.GetArrayRank() == 1)
            {
                if (FormatterUtilities.IsPrimitiveArrayType(type.GetElementType()))
                {
                    formatter = (IFormatter)Activator.CreateInstance(typeof(PrimitiveArrayFormatter <>).MakeGenericType(type.GetElementType()));
                }
                else
                {
                    formatter = (IFormatter)Activator.CreateInstance(typeof(ArrayFormatter <>).MakeGenericType(type.GetElementType()));
                }
            }
            else
            {
                formatter = (IFormatter)Activator.CreateInstance(typeof(MultiDimensionalArrayFormatter <,>).MakeGenericType(type, type.GetElementType()));
            }

            return(true);
        }
예제 #2
0
        /// <summary>
        /// Writes a primitive array to the stream.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The primitive array to write.</param>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        /// <exception cref="System.ArgumentNullException">array</exception>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            Action <string, T> writer = (Action <string, T>) this.primitiveTypeWriters[typeof(T)];

            this.WriteInt64(JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG, array.Length);
            this.WriteEntry(JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG, "[");
            this.forceNoSeparatorNextLine = true;
            this.PushArray();

            for (int i = 0; i < array.Length; i++)
            {
                writer(null, array[i]);
            }

            this.PopArray();
            this.StartNewLine(true);
            this.writer.Write("]");
        }
예제 #3
0
        /// <summary>
        /// Reads a primitive array value. This call will succeed if the next entry is an <see cref="EntryType.PrimitiveArray" />.
        /// <para />
        /// If the call fails (and returns <c>false</c>), it will skip the current entry value, unless that entry is an <see cref="EntryType.EndOfNode" /> or an <see cref="EntryType.EndOfArray" />.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The resulting primitive array.</param>
        /// <returns>
        ///   <c>true</c> if reading a primitive array succeeded, otherwise <c>false</c>
        /// </returns>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override bool ReadPrimitiveArray <T>(out T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            this.PeekEntry();

            if (this.peekedEntryType == EntryType.PrimitiveArray)
            {
                this.PushArray();

                if (this.peekedEntryName != JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG)
                {
                    this.Context.Config.DebugContext.LogError("Array entry wasn't preceded by an array length entry!");
                    array = null; // No array content for you!
                    return(false);
                }
                else
                {
                    int intLength;

                    if (int.TryParse(this.peekedEntryContent, NumberStyles.Any, CultureInfo.InvariantCulture, out intLength) == false)
                    {
                        this.Context.Config.DebugContext.LogError("Failed to parse array length: " + this.peekedEntryContent);
                        array = null; // No array content for you!
                        return(false);
                    }

                    this.ReadToNextEntry();

                    if (this.peekedEntryName != JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG)
                    {
                        this.Context.Config.DebugContext.LogError("Failed to find primitive array content entry after array length entry!");
                        array = null; // No array content for you!
                        return(false);
                    }

                    this.peekedEntryType = null;

                    Func <T> reader = (Func <T>) this.primitiveArrayReaders[typeof(T)];
                    array = new T[intLength];

                    for (int i = 0; i < intLength; i++)
                    {
                        array[i] = reader();
                    }

                    this.ExitArray();
                    return(true);
                }
            }
            else
            {
                this.SkipEntry();
                array = null;
                return(false);
            }
        }
        /// <summary>
        /// Reads a primitive array value. This call will succeed if the next entry is an <see cref="EntryType.PrimitiveArray" />.
        /// <para />
        /// If the call fails (and returns <c>false</c>), it will skip the current entry value, unless that entry is an <see cref="EntryType.EndOfNode" /> or an <see cref="EntryType.EndOfArray" />.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The resulting primitive array.</param>
        /// <returns>
        ///   <c>true</c> if reading a primitive array succeeded, otherwise <c>false</c>
        /// </returns>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override bool ReadPrimitiveArray <T>(out T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (this.peekedEntryType != EntryType.PrimitiveArray)
            {
                this.SkipEntry();
                array = null;
                return(false);
            }

            if (typeof(T) == typeof(byte))
            {
                array = (T[])(object)ProperBitConverter.HexStringToBytes(this.peekedEntryData);
                return(true);
            }
            else
            {
                this.PeekEntry();

                long length;

                if (this.peekedEntryType != EntryType.PrimitiveArray)
                {
                    this.Context.Config.DebugContext.LogError("Expected entry of type '" + EntryType.StartOfArray + "' when reading primitive array but got entry of type '" + this.peekedEntryType + "'.");
                    this.SkipEntry();
                    array = new T[0];
                    return(false);
                }

                if (!long.TryParse(this.peekedEntryData, NumberStyles.Any, CultureInfo.InvariantCulture, out length))
                {
                    this.Context.Config.DebugContext.LogError("Failed to parse primitive array length from entry data '" + this.peekedEntryData + "'.");
                    this.SkipEntry();
                    array = new T[0];
                    return(false);
                }

                this.ConsumeCurrentEntry();
                this.PushArray();

                array = new T[length];

                Func <T> reader = (Func <T>) this.primitiveTypeReaders[typeof(T)];

                for (int i = 0; i < length; i++)
                {
                    array[i] = reader();
                }

                this.ExitArray();
                return(true);
            }
        }
예제 #5
0
        /// <summary>
        /// Writes a primitive array to the stream.
        /// </summary>
        /// <typeparam name="T">The element type of the primitive array. Valid element types can be determined using <see cref="FormatterUtilities.IsPrimitiveArrayType(Type)" />.</typeparam>
        /// <param name="array">The primitive array to write.</param>
        /// <exception cref="System.ArgumentException">Type  + typeof(T).Name +  is not a valid primitive array type.</exception>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            int bytesPerElement = PrimitiveSizes[typeof(T)];
            int byteCount       = array.Length * bytesPerElement;

            // Write entry flag
            this.Stream.WriteByte((byte)BinaryEntryType.PrimitiveArray);

            // Write array length
            ProperBitConverter.GetBytes(this.buffer, 0, array.Length);
            this.Stream.Write(this.buffer, 0, 4);

            // Write size of an element in bytes
            ProperBitConverter.GetBytes(this.buffer, 0, bytesPerElement);
            this.Stream.Write(this.buffer, 0, 4);

            // Write the actual array content
            if (typeof(T) == typeof(byte))
            {
                // We can include a special case for byte arrays, as there's no need to copy that to a buffer
                var byteArray = (byte[])(object)array;
                this.Stream.Write(byteArray, 0, byteCount);
            }
            else
            {
                // Otherwise we copy to a buffer in order to write the entire array into the stream with one call
                using (var tempBuffer = Buffer <byte> .Claim(byteCount))
                {
                    if (BitConverter.IsLittleEndian)
                    {
                        // We always store in little endian, so we can do a direct memory mapping, which is a lot faster
                        UnsafeUtilities.MemoryCopy(array, tempBuffer.Array, byteCount, 0, 0);
                    }
                    else
                    {
                        // We have to convert each individual element to bytes, since the byte order has to be reversed
                        Action <byte[], int, T> toBytes = (Action <byte[], int, T>)PrimitiveGetBytesMethods[typeof(T)];
                        var b = tempBuffer.Array;

                        for (int i = 0; i < array.Length; i++)
                        {
                            toBytes(b, i * bytesPerElement, array[i]);
                        }
                    }

                    this.Stream.Write(tempBuffer.Array, 0, byteCount);
                }
            }
        }
        /// <summary>
        /// Not yet documented.
        /// </summary>
        public override void WritePrimitiveArray <T>(T[] array)
        {
            if (FormatterUtilities.IsPrimitiveArrayType(typeof(T)) == false)
            {
                throw new ArgumentException("Type " + typeof(T).Name + " is not a valid primitive array type.");
            }

            if (typeof(T) == typeof(byte))
            {
                string hex = ProperBitConverter.BytesToHexString((byte[])(object)array);

                this.Nodes.Add(new SerializationNode()
                {
                    Name  = string.Empty,
                    Entry = EntryType.PrimitiveArray,
                    Data  = hex
                });
            }
            else
            {
                this.Nodes.Add(new SerializationNode()
                {
                    Name  = string.Empty,
                    Entry = EntryType.PrimitiveArray,
                    Data  = array.LongLength.ToString(CultureInfo.InvariantCulture)
                });

                this.PushArray();

                Action <string, T> writer = (Action <string, T>) this.primitiveTypeWriters[typeof(T)];

                for (int i = 0; i < array.Length; i++)
                {
                    writer(string.Empty, array[i]);
                }

                this.EndArrayNode();
            }
        }
예제 #7
0
        private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy)
        {
            if (FormatterUtilities.IsPrimitiveType(type))
            {
                throw new ArgumentException("Cannot create formatters for a primitive type like " + type.Name);
            }

            bool canSelfFormat = type.ImplementsOrInherits(typeof(ISelfFormatter));

            // If the type should always self format, there is no need to explore further.
            // Otherwise, we go through the below checks first to see whether a custom
            // formatter is defined.
            if (canSelfFormat && type.IsDefined <AlwaysFormatsSelfAttribute>())
            {
                return((IFormatter)Activator.CreateInstance(typeof(SelfFormatterFormatter <>).MakeGenericType(type)));
            }

            // First, allow the FormatterResolve event to resolve the formatter if possible
            // We always hold the lock in the CreateFormatter method, so we can safely
            //  invoke the event without worrying about other threads changing it.
            if (FormatterResolvePrivate != null)
            {
                Type genericInterface = typeof(IFormatter <>).MakeGenericType(type);

                foreach (var del in FormatterResolvePrivate.GetInvocationList())
                {
                    IFormatter result = del.Method.Invoke(del.Target, new object[] { type }) as IFormatter;

                    if (result != null && result.GetType().ImplementsOrInherits(genericInterface))
                    {
                        return(result);
                    }
                }
            }

            // Then try to find a custom formatter
            {
                Type formatterType;

                if (CustomFormatterTypes.TryGetValue(type, out formatterType))
                {
                    return((IFormatter)Activator.CreateInstance(formatterType));
                }
            }

            if (type.IsGenericType)
            {
                // Then try to find a custom generic formatter.
                // IE, if we're trying to serialize Dictionary<string, int>, we might have a formatter that declares it can handle
                // Dictionary<TKey, TValue>. If so, we can use that.
                Type genericTypeDefinition = type.GetGenericTypeDefinition();
                Type formatterGenericTypeDefinition;

                if (CustomGenericFormatterTypes.TryGetValue(genericTypeDefinition, out formatterGenericTypeDefinition))
                {
                    var formatterType = formatterGenericTypeDefinition.MakeGenericType(type.GetGenericArguments());
                    return((IFormatter)Activator.CreateInstance(formatterType));
                }
            }

            // Quick hack to support types derived from dictionary
            if (type.ImplementsOpenGenericClass(typeof(Dictionary <,>)) && type.GetConstructor(Type.EmptyTypes) != null)
            {
                var dictArgs      = type.GetArgumentsOfInheritedOpenGenericClass(typeof(Dictionary <,>));
                var formatterType = typeof(DerivedDictionaryFormatter <, ,>).MakeGenericType(type, dictArgs[0], dictArgs[1]);
                return((IFormatter)Activator.CreateInstance(formatterType));
            }

            // If there were no custom formatters found, the type can format itself
            if (canSelfFormat)
            {
                return((IFormatter)Activator.CreateInstance(typeof(SelfFormatterFormatter <>).MakeGenericType(type)));
            }

            // Delegates get special behaviour, as they're weird
            if (typeof(Delegate).IsAssignableFrom(type))
            {
                return((IFormatter)Activator.CreateInstance(typeof(DelegateFormatter <>).MakeGenericType(type)));
            }

            // Types get special behaviour, as they are often instances of special runtime types like System.MonoType which cannot be addressed at compile time.
            if (typeof(Type).IsAssignableFrom(type))
            {
                return(new TypeFormatter());
            }

            if (type.IsArray)
            {
                // Custom behaviour for all arrays that don't have specific custom formatters
                if (type.GetArrayRank() == 1)
                {
                    if (FormatterUtilities.IsPrimitiveArrayType(type.GetElementType()))
                    {
                        return((IFormatter)Activator.CreateInstance(typeof(PrimitiveArrayFormatter <>).MakeGenericType(type.GetElementType())));
                    }
                    else
                    {
                        return((IFormatter)Activator.CreateInstance(typeof(ArrayFormatter <>).MakeGenericType(type.GetElementType())));
                    }
                }
                else
                {
                    return((IFormatter)Activator.CreateInstance(typeof(MultiDimensionalArrayFormatter <,>).MakeGenericType(type, type.GetElementType())));
                }
            }

            // If the type implements ISerializable, use the SerializableFormatter
            if (type.ImplementsOrInherits(typeof(ISerializable)))
            {
                return((IFormatter)Activator.CreateInstance(typeof(SerializableFormatter <>).MakeGenericType(type)));
            }

            // If the type can be treated as a generic collection, do that
            {
                Type elementType;

                if (GenericCollectionFormatter.CanFormat(type, out elementType))
                {
                    return((IFormatter)Activator.CreateInstance(typeof(GenericCollectionFormatter <,>).MakeGenericType(type, elementType)));
                }
            }

            // If we can, emit a formatter to handle serialization of this object
            {
                if (EmitUtilities.CanEmit)
                {
                    return(FormatterEmitter.GetEmittedFormatter(type, policy));
                }
            }

            if (EmitUtilities.CanEmit)
            {
                Debug.LogWarning("Fallback to reflection for type " + type.Name + " when emit is possible on this platform.");
            }

            // Finally, we fall back to a reflection-based formatter if nothing else has been found
            return((IFormatter)Activator.CreateInstance(typeof(ReflectionFormatter <>).MakeGenericType(type)));
        }