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); }
/// <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("]"); }
/// <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); } }
/// <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(); } }
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))); }