/// <summary> /// Reads a basic value from the stream. /// </summary> /// <param name="typeInfo">The type to be deserialized.</param> /// <param name="reader">The stream containing serialized data.</param> /// <param name="baseValue">The default value for this field.</param> /// <returns>The deserialized value.</returns> internal static object ReadValue(TypeInfo typeInfo, IReader reader, object baseValue) { object result = null; int n; var info = typeInfo.info & SerializationTypeInfo.VALUE_MASK; switch (info) { case SerializationTypeInfo.UserDefined: n = reader.ReadInt32(); if (n >= 0) { var itemType = typeInfo.type; if (baseValue == null) { result = ConstructorDelegator.CreateInstance(itemType); } else { result = baseValue; } FastSerializationManager.GetFastDeserializationMapping(itemType). Deserialize(result, reader); } break; case SerializationTypeInfo.SByte: result = reader.ReadSByte(); break; case SerializationTypeInfo.Byte: result = reader.ReadByte(); break; case SerializationTypeInfo.Boolean: result = reader.ReadByte() == 1; break; case SerializationTypeInfo.Int16: result = reader.ReadInt16(); break; case SerializationTypeInfo.UInt16: result = reader.ReadUInt16(); break; case SerializationTypeInfo.Int32: result = reader.ReadInt32(); break; case SerializationTypeInfo.UInt32: result = reader.ReadUInt32(); break; case SerializationTypeInfo.Int64: result = reader.ReadInt64(); break; case SerializationTypeInfo.UInt64: result = reader.ReadUInt64(); break; case SerializationTypeInfo.Single: result = reader.ReadSingle(); break; case SerializationTypeInfo.Double: result = reader.ReadDouble(); break; case SerializationTypeInfo.String: result = reader.ReadKleiString(); break; case SerializationTypeInfo.Enumeration: result = Enum.ToObject(typeInfo.type, reader.ReadInt32()); break; case SerializationTypeInfo.Vector2I: result = reader.ReadVector2I(); break; case SerializationTypeInfo.Vector2: result = reader.ReadVector2(); break; case SerializationTypeInfo.Vector3: result = reader.ReadVector3(); break; case SerializationTypeInfo.Array: reader.ReadInt32(); result = ReadArray(typeInfo, reader, true); break; case SerializationTypeInfo.Pair: n = reader.ReadInt32(); if (n >= 0) { TypeInfo keyType = typeInfo.subTypes[0], valueType = typeInfo.subTypes[1]; object key = ReadValue(keyType, reader, null); object value = ReadValue(valueType, reader, null); result = KeyValuePairDelegator.GetDelegates(keyType.type, valueType.type). CreateInstance(key, value); } break; case SerializationTypeInfo.Dictionary: reader.ReadInt32(); n = reader.ReadInt32(); if (n >= 0) { // Preallocate dictionary to correct size result = DictionaryDelegator.CreateInstance(typeInfo. genericInstantiationType, n); var dict = result as System.Collections.IDictionary; var keyType = typeInfo.subTypes[0]; var valueType = typeInfo.subTypes[1]; var values = ListPool <object, FastDeserializationMapping> .Allocate(); for (int i = 0; i < n; i++) { values.Add(ReadValue(valueType, reader, null)); } for (int i = 0; i < n; i++) { dict.Add(ReadValue(keyType, reader, null), values[i]); } values.Recycle(); } break; case SerializationTypeInfo.HashSet: reader.ReadInt32(); result = ReadArray(typeInfo, reader, false); if (result != null) { result = CollectionDelegator.CreateInstance(typeInfo. genericInstantiationType, result); } break; case SerializationTypeInfo.List: case SerializationTypeInfo.Queue: reader.ReadInt32(); result = ReadArray(typeInfo, reader, true); if (result != null) { // Due to how POD lists/queues are encoded, must go through a temporary // array to make best usage of ReadArrayFast, sorry memory usage result = CollectionDelegator.CreateInstance(typeInfo. genericInstantiationType, result); } break; case SerializationTypeInfo.Colour: result = reader.ReadColour(); break; default: throw new ArgumentException("Unknown type " + info); } return(result); }
/// <summary> /// Serializes a value to the specified stream. /// </summary> /// <param name="writer">The stream where the data will be serialized.</param> /// <param name="typeInfo">The type to be serialized.</param> /// <param name="data">The data to serialize.</param> public static void WriteValue(BinaryWriter writer, TypeInfo typeInfo, object data) { switch (typeInfo.info & SerializationTypeInfo.VALUE_MASK) { case SerializationTypeInfo.UserDefined: if (data != null) { long startPos = writer.BaseStream.Position; writer.Write(0); long basePos = writer.BaseStream.Position; GetFastSerializationTemplate(typeInfo.type).SerializeData(data, writer); WriteLength(writer, startPos, basePos); } else { writer.Write(-1); } break; case SerializationTypeInfo.SByte: writer.Write((sbyte)data); break; case SerializationTypeInfo.Byte: writer.Write((byte)data); break; case SerializationTypeInfo.Boolean: writer.Write((byte)((data is bool which && which) ? 1 : 0)); break; case SerializationTypeInfo.Int16: writer.Write((short)data); break; case SerializationTypeInfo.UInt16: writer.Write((ushort)data); break; case SerializationTypeInfo.Int32: writer.Write((int)data); break; case SerializationTypeInfo.UInt32: writer.Write((uint)data); break; case SerializationTypeInfo.Int64: writer.Write((long)data); break; case SerializationTypeInfo.UInt64: writer.Write((ulong)data); break; case SerializationTypeInfo.Single: writer.WriteSingleFast((float)data); break; case SerializationTypeInfo.Double: writer.Write((double)data); break; case SerializationTypeInfo.String: writer.WriteKleiString((string)data); break; case SerializationTypeInfo.Enumeration: writer.Write((int)data); break; case SerializationTypeInfo.Vector2I: if (data is Vector2I vector2i) { writer.Write(vector2i.x); writer.Write(vector2i.y); } else { writer.Write(0); writer.Write(0); } break; case SerializationTypeInfo.Vector2: if (data is Vector2 vector2) { writer.WriteSingleFast(vector2.x); writer.WriteSingleFast(vector2.y); } else { writer.WriteSingleFast(0.0f); writer.WriteSingleFast(0.0f); } break; case SerializationTypeInfo.Vector3: if (data is Vector3 vector3) { writer.WriteSingleFast(vector3.x); writer.WriteSingleFast(vector3.y); writer.WriteSingleFast(vector3.z); } else { for (int i = 0; i < 3; i++) { writer.WriteSingleFast(0.0f); } } break; case SerializationTypeInfo.Array: if (data is Array array) { var elementType = typeInfo.subTypes[0]; int n = array.Length; long startPos = writer.BaseStream.Position; writer.Write(0); writer.Write(n); long basePos = writer.BaseStream.Position; if (Helper.IsPOD(elementType.info)) { WriteArrayPOD(writer, elementType, array); } else if (Helper.IsValueType(elementType.info)) { var template = GetFastSerializationTemplate(elementType.type); for (int i = 0; i < n; i++) { template.SerializeData(array.GetValue(i), writer); } } else { for (int i = 0; i < n; i++) { WriteValue(writer, elementType, array.GetValue(i)); } } WriteLength(writer, startPos, basePos); } else { writer.Write(0); writer.Write(-1); } break; case SerializationTypeInfo.Pair: if (data != null) { TypeInfo keyType = typeInfo.subTypes[0], valueType = typeInfo.subTypes[1]; var delegator = KeyValuePairDelegator.GetDelegates(keyType.type, valueType.type); long startPos = writer.BaseStream.Position; writer.Write(0); long basePos = writer.BaseStream.Position; WriteValue(writer, keyType, delegator.GetKey(data)); WriteValue(writer, valueType, delegator.GetValue(data)); WriteLength(writer, startPos, basePos); } else { writer.Write(4); writer.Write(-1); } break; case SerializationTypeInfo.Dictionary: if (data is IDictionary dict) { TypeInfo keyType = typeInfo.subTypes[0], valueType = typeInfo.subTypes[1]; long startPos = writer.BaseStream.Position; writer.Write(0); writer.Write(dict.Count); long basePos = writer.BaseStream.Position; foreach (object value in dict.Values) { WriteValue(writer, valueType, value); } foreach (object key in dict.Keys) { WriteValue(writer, keyType, key); } WriteLength(writer, startPos, basePos); } else { writer.Write(0); writer.Write(-1); } break; case SerializationTypeInfo.List: case SerializationTypeInfo.Queue: if (data is ICollection list) { var elementType = typeInfo.subTypes[0]; long startPos = writer.BaseStream.Position; writer.Write(0); writer.Write(list.Count); long basePos = writer.BaseStream.Position; if (Helper.IsPOD(elementType.info)) { WriteListPOD(writer, elementType, list); } else if (Helper.IsValueType(elementType.info)) { var template = GetFastSerializationTemplate(elementType.type); foreach (object element in list) { template.SerializeData(element, writer); } } else { foreach (object element in list) { WriteValue(writer, elementType, element); } } WriteLength(writer, startPos, basePos); } else { writer.Write(0); writer.Write(-1); } break; case SerializationTypeInfo.HashSet: if (data is IEnumerable enumerable) { var elementType = typeInfo.subTypes[0]; long startPos = writer.BaseStream.Position; writer.Write(0); writer.Write(0); long basePos = writer.BaseStream.Position; int n = 0; // No special case handling for POD for hash sets if (Helper.IsValueType(elementType.info)) { var template = GetFastSerializationTemplate(elementType.type); foreach (object element in enumerable) { template.SerializeData(element, writer); n++; } } else { foreach (object element in enumerable) { WriteValue(writer, elementType, element); n++; } } // Element count must be written along with the length long endPos = writer.BaseStream.Position; writer.BaseStream.Position = startPos; writer.Write((int)(endPos - basePos)); writer.Write(n); writer.BaseStream.Position = endPos; } else { writer.Write(0); writer.Write(-1); } break; case SerializationTypeInfo.Colour: if (data is Color color) { writer.Write((byte)(color.r * 255f)); writer.Write((byte)(color.g * 255f)); writer.Write((byte)(color.b * 255f)); writer.Write((byte)(color.a * 255f)); } else { for (int i = 0; i < 4; i++) { writer.Write((byte)0); } } break; default: throw new ArgumentException("Unable to serialize type: " + typeInfo.type. FullName); } }