diff --git a/MsgPack/Formatters/DictionaryFormatter.cs b/MsgPack/Formatters/DictionaryFormatter.cs index 0784f68..01c7016 100644 --- a/MsgPack/Formatters/DictionaryFormatter.cs +++ b/MsgPack/Formatters/DictionaryFormatter.cs @@ -12,7 +12,11 @@ internal static class DictionaryFormatter { public static Tuple Build(Type typeKey, Type typeValue) { +#if IS_FXSERVER MethodInfo methodSerialize, methodDeserialize, methodObjectSerialize; +#else + MethodInfo methodDeserialize; +#endif Type typeKeyValuePair = typeof(KeyValuePair<,>).MakeGenericType(typeKey, typeValue); Type typeDictionary = typeof(Dictionary<,>).MakeGenericType(typeKey, typeValue); @@ -26,22 +30,34 @@ public static Tuple Build(Type typeKey, Type typeValue) { TypeBuilder typeBuilder = MsgPackRegistry.m_moduleBuilder.DefineType(name); +#if IS_FXSERVER methodSerialize = BuildSerializer(typeKey, typeValue, typeKeyValuePair, typeIDictionary, typeBuilder); BuildDeserializer(typeKey, typeValue, typeDictionary, typeBuilder); BuildObjectSerializer(typeIDictionary, methodSerialize, typeBuilder); +#else + BuildDeserializer(typeKey, typeValue, typeDictionary, typeBuilder); +#endif buildType = typeBuilder.CreateType(); } +#if IS_FXSERVER methodSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), typeIDictionary }); methodDeserialize = buildType.GetMethod("Deserialize"); methodObjectSerialize = buildType.GetMethod("Serialize", new[] { typeof(MsgPackSerializer), typeof(object) }); +#else + methodDeserialize = buildType.GetMethod("Deserialize"); +#endif - Serializer serializeMethod = new Serializer(methodSerialize, methodObjectSerialize); +#if IS_FXSERVER + Serializer serializeMethod = new Serializer(methodSerialize, methodObjectSerialize); MsgPackRegistry.RegisterSerializer(typeDictionary, serializeMethod); MsgPackRegistry.RegisterSerializer(typeIDictionary, serializeMethod); MsgPackRegistry.RegisterSerializer(typeIReadOnlyDictionary, serializeMethod); +#else + Serializer serializeMethod = new Serializer(); +#endif MsgPackRegistry.RegisterDeserializer(typeDictionary, methodDeserialize); MsgPackRegistry.RegisterDeserializer(typeIDictionary, methodDeserialize); diff --git a/MsgPack/Formatters/TypeFormatter.cs b/MsgPack/Formatters/TypeFormatter.cs index 736c717..393b3e0 100644 --- a/MsgPack/Formatters/TypeFormatter.cs +++ b/MsgPack/Formatters/TypeFormatter.cs @@ -1252,7 +1252,7 @@ private static DynamicArray GetReadableMembers(Type type) return members; } - private static DynamicArray GetWritableMembers(Type type) + internal static DynamicArray GetWritableMembers(Type type) { var members = new DynamicArray(type.GetMembers(BindingFlags.Instance | BindingFlags.Public)); members.RemoveAll(m => !((m is FieldInfo || (m is PropertyInfo p && p.CanWrite)) @@ -1261,7 +1261,7 @@ private static DynamicArray GetWritableMembers(Type type) return members; } - private static DynamicArray GetReadAndWritableKeyedMembers(Type type) + internal static DynamicArray GetReadAndWritableKeyedMembers(Type type) { var members = new DynamicArray(type.GetMembers(BindingFlags.Instance | BindingFlags.Public)); members.RemoveAll(m => !((m is FieldInfo || (m is PropertyInfo p && p.CanWrite && p.GetGetMethod()?.GetParameters().Length == 0)) @@ -1271,7 +1271,7 @@ private static DynamicArray GetReadAndWritableKeyedMembers(Type type return members; } - private static DynamicArray GetReadableIndexMembers(Type type) + internal static DynamicArray GetReadableIndexMembers(Type type) { var members = new DynamicArray(type.GetMembers(BindingFlags.Instance | BindingFlags.Public)); members.RemoveAll(m => !((m is FieldInfo || (m is PropertyInfo p && p.GetGetMethod()?.GetParameters().Length == 0)) @@ -1294,7 +1294,7 @@ private static void CallAndStore(ILGenerator g, MethodInfo methodReadInteger, Op g.Emit(storeLoc); } - private static void EmitDebugWriteLine(this ILGenerator g, string value) + internal static void EmitDebugWriteLine(this ILGenerator g, string value) { g.Emit(OpCodes.Ldstr, value); #if MONO_V2 diff --git a/MsgPack/MsgPackRegistry.cs b/MsgPack/MsgPackRegistry.cs index 7235d09..ddd0c25 100644 --- a/MsgPack/MsgPackRegistry.cs +++ b/MsgPack/MsgPackRegistry.cs @@ -1,5 +1,6 @@ using CitizenFX.MsgPack.Formatters; using System; +using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit; @@ -113,12 +114,13 @@ internal static void Serialize(MsgPackSerializer serializer, object obj) // Adding too much checks here could run slower than a hash map lookup, profile accordingly Type type = obj.GetType(); - if (type.IsPrimitive) + if (type.IsPrimitive || obj is string) { switch (obj) { case bool v: serializer.Serialize(v); break; case char v: serializer.Serialize(v); break; + case string v: serializer.Serialize(v); break; case byte v: serializer.Serialize(v); break; case ushort v: serializer.Serialize(v); break; @@ -132,28 +134,50 @@ internal static void Serialize(MsgPackSerializer serializer, object obj) case float v: serializer.Serialize(v); break; case double v: serializer.Serialize(v); break; - } + } } - else if (obj is Delegate del) - { - // add remote function delegate support ? - don't think if it's even planned or supported - serializer.Serialize(del); - } - // byte[] is a special type handled like a Binary object. - // It is not a primitive type, so i serialized as a binary object and deserialized as such. - else if (obj is byte[] b) + else if (obj is Delegate del) + { + // add remote function delegate support ? - don't think if it's even planned or supported + serializer.Serialize(del); + } +#if !IS_FXSERVER + else if (obj is IEnumerable e) + { + serializer.Serialize(e); + } +#endif + else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + var keyProp = type.GetProperty("Key"); + var valueProp = type.GetProperty("Value"); + + var key = keyProp.GetValue(obj); + var value = valueProp.GetValue(obj); + + serializer.Serialize(new KeyValuePair(key, value)); + } + // byte[] is a special type handled like a Binary object. + // It is not a primitive type, so i serialized as a binary object and deserialized as such. + else if (obj is byte[] b) { serializer.Serialize(b); } - // same applies to List + // same applies to List // we serialize it as a byte array and deserialize it as such // it's not elegant but i needed to handle it like this as byte is a binary format even in List // Ps: Who the fuck in fivem would retrieve and send List?????? - else if (obj is List lb) - { - serializer.Serialize(lb.ToArray()); - } - else if (TryGetSerializer(type, out var methodInfo)) + else if (obj is List lb) + { + serializer.Serialize(lb.ToArray()); + } +#if !IS_FXSERVER + else if (!type.IsPrimitive && !type.IsArray && !type.IsGenericType && type.FullName != "CitizenFX.Core.Player") + { + serializer.SerializeType(obj); + } +#endif + else if (TryGetSerializer(type, out var methodInfo)) { methodInfo.m_objectSerializer(serializer, obj); } @@ -190,14 +214,14 @@ internal static MethodInfo GetOrCreateSerializer(Type type) internal static MethodInfo GetOrCreateDeserializer(Type type) { - return TryGetDeserializer(type, out var methodInfo) + return TryGetDeserializer(type, out var methodInfo) ? methodInfo : CreateSerializer(type)?.Item2; } private static Tuple CreateSerializer(Type type) { - if (type.IsPrimitive) + if (type.IsPrimitive) { if (m_serializers.ContainsKey(type)) return new Tuple(m_serializers[type], m_serializers[type].m_method); @@ -206,7 +230,7 @@ private static Tuple CreateSerializer(Type type) } else if (type.IsArray) { - switch (type.GetArrayRank()) + switch (type.GetArrayRank()) { case 1: return ArrayFormatter.Build(type.GetElementType(), type); @@ -215,7 +239,7 @@ private static Tuple CreateSerializer(Type type) else if (type.IsGenericType) { var genericTypes = type.GetGenericArguments(); - switch (genericTypes.Length) + switch (genericTypes.Length) { case 1: { @@ -225,7 +249,7 @@ private static Tuple CreateSerializer(Type type) break; case 2: { - if (ImplementsGenericTypeDefinition(type, typeof(IDictionary<,>))) + if (ImplementsGenericTypeDefinition(type, typeof(IDictionary<,>))) return DictionaryFormatter.Build(genericTypes[0], genericTypes[1]); break; } diff --git a/MsgPack/MsgPackSerializer.cs b/MsgPack/MsgPackSerializer.cs index ad589a5..9920a36 100644 --- a/MsgPack/MsgPackSerializer.cs +++ b/MsgPack/MsgPackSerializer.cs @@ -1,16 +1,18 @@ using CitizenFX.Core; +using CitizenFX.MsgPack.Formatters; using System; using System.Collections; using System.Collections.Generic; +using System.Reflection; using System.Security; namespace CitizenFX.MsgPack { - /// - /// Serializer class to serialize any data to the MsgPack format. - /// - public class MsgPackSerializer - { + /// + /// Serializer class to serialize any data to the MsgPack format. + /// + public class MsgPackSerializer + { // NOTE: // 1. When adding any Serialize(T) method, make sure there's an equivalent T DeserializeAsT() in the MsgPackDeserializer class. // 2. Serialize(T) write headers, sizes, and pick the correct Write*([MsgPackCode,] T) method. @@ -19,206 +21,206 @@ public class MsgPackSerializer // TODO: look into and profile non-pinned alternatives for interop with C++ byte[] m_buffer; - ulong m_position; + ulong m_position; public MsgPackSerializer() - { - m_buffer = new byte[256]; - } - - public byte[] AcquireBuffer() - { - return m_buffer; - } - - public byte[] ToArray() - { - byte[] result = new byte[m_position]; - Array.Copy(m_buffer, result, (int)m_position); - return result; - } - - public void Reset() - { - m_position = 0; - } - - private void EnsureCapacity(uint size) - { - ulong requiredCapacity = m_position + size; - if (requiredCapacity >= (ulong)m_buffer.LongLength) - { - byte[] oldBuffer = m_buffer; - m_buffer = new byte[oldBuffer.Length * 2]; - Array.Copy(oldBuffer, m_buffer, oldBuffer.Length); + { + m_buffer = new byte[256]; + } + + public byte[] AcquireBuffer() + { + return m_buffer; + } + + public byte[] ToArray() + { + byte[] result = new byte[m_position]; + Array.Copy(m_buffer, result, (int)m_position); + return result; + } + + public void Reset() + { + m_position = 0; + } + + private void EnsureCapacity(uint size) + { + ulong requiredCapacity = m_position + size; + if (requiredCapacity >= (ulong)m_buffer.LongLength) + { + byte[] oldBuffer = m_buffer; + m_buffer = new byte[oldBuffer.Length * 2]; + Array.Copy(oldBuffer, m_buffer, oldBuffer.Length); } - } + } - public static byte[] SerializeToByteArray(object value) - { - var serializer = new MsgPackSerializer(); - serializer.Serialize(value); - return serializer.ToArray(); - } + public static byte[] SerializeToByteArray(object value) + { + var serializer = new MsgPackSerializer(); + serializer.Serialize(value); + return serializer.ToArray(); + } - #region Basic type serialization + #region Basic type serialization - public void Serialize(bool value) - { - Write(value ? (byte)MsgPackCode.True : (byte)MsgPackCode.False); - } + public void Serialize(bool value) + { + Write(value ? (byte)MsgPackCode.True : (byte)MsgPackCode.False); + } - public void Serialize(object v) => MsgPackRegistry.Serialize(this, v); + public void Serialize(object v) => MsgPackRegistry.Serialize(this, v); public void Serialize(sbyte v) - { - if (v < 0) - { - if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) - Write(unchecked((byte)v)); - else - WriteBigEndian(MsgPackCode.Int8, (sbyte)v); - } - else - Write(unchecked((byte)v)); - } - - public void Serialize(byte v) - { - if (v <= (byte)MsgPackCode.FixIntPositiveMax) - Write(v); - else - Write(MsgPackCode.UInt8, v); - } - - public void Serialize(short v) - { - if (v < 0) - { - if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) - Write(unchecked((byte)v)); - else if (v >= sbyte.MinValue) - WriteBigEndian(MsgPackCode.Int8, (sbyte)v); - else - WriteBigEndian(MsgPackCode.Int16, v); - } - else - Serialize(unchecked((ushort)v)); - } - - public void Serialize(Delegate d) - { + { + if (v < 0) + { + if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) + Write(unchecked((byte)v)); + else + WriteBigEndian(MsgPackCode.Int8, (sbyte)v); + } + else + Write(unchecked((byte)v)); + } + + public void Serialize(byte v) + { + if (v <= (byte)MsgPackCode.FixIntPositiveMax) + Write(v); + else + Write(MsgPackCode.UInt8, v); + } + + public void Serialize(short v) + { + if (v < 0) + { + if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) + Write(unchecked((byte)v)); + else if (v >= sbyte.MinValue) + WriteBigEndian(MsgPackCode.Int8, (sbyte)v); + else + WriteBigEndian(MsgPackCode.Int16, v); + } + else + Serialize(unchecked((ushort)v)); + } + + public void Serialize(Delegate d) + { // this works only if msgpack lib is built within fivem, it's not really elegant imho // but it works, so let's not break it now, shall we? // Do we want to make this work without depending from ReferenceFunctionManager // and make our own Canonicalization of the reference id? #if REMOTE_FUNCTION_ENABLED - ulong callbackId = d.Target is _RemoteHandler _pf + ulong callbackId = d.Target is _RemoteHandler _pf ? _pf.m_id : ExternalsManager.RegisterRemoteFunction(d.Method.ReturnType, new DynFunc(args => args.Length == 1 || args[1] == null ? dynFunc(args[0]) : null)); var bytes = Encoding.UTF8.GetBytes(callbackId.ToString()); - uint size = (uint)bytes.LongLength; - EnsureCapacity((uint)bytes.Length); - WriteExtraTypeHeader(size); - Write((byte)10); - Array.Copy(bytes, 0, m_buffer, (int)m_position, size); - m_position += size; + uint size = (uint)bytes.LongLength; + EnsureCapacity((uint)bytes.Length); + WriteExtraTypeHeader(size); + Write((byte)10); + Array.Copy(bytes, 0, m_buffer, (int)m_position, size); + m_position += size; #else var remote = MsgPackReferenceRegistrar.Register(MsgPackDeserializer.CreateDelegate(d)); - uint size = (uint)remote.Value.LongLength; - EnsureCapacity((uint)remote.Value.Length); - WriteExtraTypeHeader(size); - Write((byte)11); - Array.Copy(remote.Value, 0, m_buffer, (int)m_position, size); - m_position += size; + uint size = (uint)remote.Value.LongLength; + EnsureCapacity((uint)remote.Value.Length); + WriteExtraTypeHeader(size); + Write((byte)11); + Array.Copy(remote.Value, 0, m_buffer, (int)m_position, size); + m_position += size; #endif - } + } public void Serialize(ushort v) - { - if (v <= (byte)MsgPackCode.FixIntPositiveMax) - Write(unchecked((byte)v)); - else if (v <= byte.MaxValue) - Write(MsgPackCode.UInt8, unchecked((byte)v)); - else - WriteBigEndian(MsgPackCode.UInt16, v); - } - - public void Serialize(int v) - { - if (v < 0) - { - if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) - Write(unchecked((byte)v)); - else if (v >= sbyte.MinValue) - Write(MsgPackCode.Int8, unchecked((byte)v)); - else if (v >= short.MinValue) - WriteBigEndian(MsgPackCode.Int16, (short)v); - else - WriteBigEndian(MsgPackCode.Int32, (short)v); - } - else - Serialize(unchecked((uint)v)); - } - - public void Serialize(uint v) - { - if (v <= (byte)MsgPackCode.FixIntPositiveMax) - Write(unchecked((byte)v)); - else if (v <= byte.MaxValue) - Write(MsgPackCode.UInt8, unchecked((byte)v)); - else if (v <= ushort.MaxValue) - WriteBigEndian(MsgPackCode.UInt16, unchecked((ushort)v)); - else - WriteBigEndian(MsgPackCode.UInt32, v); - } - - public void Serialize(long v) - { - if (v < 0) - { - if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) - Write(unchecked((byte)v)); - else if (v >= sbyte.MinValue) - WriteBigEndian(MsgPackCode.Int8, (sbyte)v); - else if (v >= short.MinValue) - WriteBigEndian(MsgPackCode.Int16, (short)v); - else if (v >= int.MinValue) - WriteBigEndian(MsgPackCode.Int32, (int)v); - else - WriteBigEndian(MsgPackCode.Int64, v); - } - else - Serialize(unchecked((ulong)v)); - } - - public void Serialize(ulong v) - { - if (v <= (byte)MsgPackCode.FixIntPositiveMax) - Write(unchecked((byte)v)); - else if (v <= byte.MaxValue) - Write(MsgPackCode.Int8, unchecked((byte)v)); - else if (v <= ushort.MaxValue) - WriteBigEndian(MsgPackCode.UInt16, unchecked((ushort)v)); - else if (v <= uint.MaxValue) - WriteBigEndian(MsgPackCode.UInt32, unchecked((uint)v)); - else - WriteBigEndian(MsgPackCode.UInt64, v); - } - - public unsafe void Serialize(float v) => WriteBigEndian(MsgPackCode.Float32, *(uint*)&v); - public unsafe void Serialize(double v) => WriteBigEndian(MsgPackCode.Float64, *(ulong*)&v); - - [SecuritySafeCritical] + { + if (v <= (byte)MsgPackCode.FixIntPositiveMax) + Write(unchecked((byte)v)); + else if (v <= byte.MaxValue) + Write(MsgPackCode.UInt8, unchecked((byte)v)); + else + WriteBigEndian(MsgPackCode.UInt16, v); + } + + public void Serialize(int v) + { + if (v < 0) + { + if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) + Write(unchecked((byte)v)); + else if (v >= sbyte.MinValue) + Write(MsgPackCode.Int8, unchecked((byte)v)); + else if (v >= short.MinValue) + WriteBigEndian(MsgPackCode.Int16, (short)v); + else + WriteBigEndian(MsgPackCode.Int32, (short)v); + } + else + Serialize(unchecked((uint)v)); + } + + public void Serialize(uint v) + { + if (v <= (byte)MsgPackCode.FixIntPositiveMax) + Write(unchecked((byte)v)); + else if (v <= byte.MaxValue) + Write(MsgPackCode.UInt8, unchecked((byte)v)); + else if (v <= ushort.MaxValue) + WriteBigEndian(MsgPackCode.UInt16, unchecked((ushort)v)); + else + WriteBigEndian(MsgPackCode.UInt32, v); + } + + public void Serialize(long v) + { + if (v < 0) + { + if (v >= unchecked((sbyte)MsgPackCode.FixIntNegativeMin)) + Write(unchecked((byte)v)); + else if (v >= sbyte.MinValue) + WriteBigEndian(MsgPackCode.Int8, (sbyte)v); + else if (v >= short.MinValue) + WriteBigEndian(MsgPackCode.Int16, (short)v); + else if (v >= int.MinValue) + WriteBigEndian(MsgPackCode.Int32, (int)v); + else + WriteBigEndian(MsgPackCode.Int64, v); + } + else + Serialize(unchecked((ulong)v)); + } + + public void Serialize(ulong v) + { + if (v <= (byte)MsgPackCode.FixIntPositiveMax) + Write(unchecked((byte)v)); + else if (v <= byte.MaxValue) + Write(MsgPackCode.Int8, unchecked((byte)v)); + else if (v <= ushort.MaxValue) + WriteBigEndian(MsgPackCode.UInt16, unchecked((ushort)v)); + else if (v <= uint.MaxValue) + WriteBigEndian(MsgPackCode.UInt32, unchecked((uint)v)); + else + WriteBigEndian(MsgPackCode.UInt64, v); + } + + public unsafe void Serialize(float v) => WriteBigEndian(MsgPackCode.Float32, *(uint*)&v); + public unsafe void Serialize(double v) => WriteBigEndian(MsgPackCode.Float64, *(ulong*)&v); + + [SecuritySafeCritical] public unsafe void Serialize(string v) { fixed (char* p_value = v) { uint size = (uint)CString.UTF8EncodeLength(p_value, v.Length); uint totalSize = size + 1; - EnsureCapacity(totalSize); + EnsureCapacity(totalSize); if (size < (MsgPackCode.FixStrMax - MsgPackCode.FixStrMin)) Write(unchecked((byte)((uint)MsgPackCode.FixStrMin + size))); @@ -249,223 +251,334 @@ public unsafe void Serialize(string v) } } public unsafe void Serialize(CString v) - { - fixed (byte* p_value = v.value) - { - uint size = (uint)v.value.LongLength - 1u; - - if (size < (MsgPackCode.FixStrMax - MsgPackCode.FixStrMin)) - Write(unchecked((byte)((uint)MsgPackCode.FixStrMin + size))); - else if (size <= byte.MaxValue) - Write(MsgPackCode.Str8, unchecked((byte)size)); - else if (size <= ushort.MaxValue) - Write(MsgPackCode.Str16, unchecked((byte)size)); - else - Write(MsgPackCode.Str32, unchecked((byte)size)); - - EnsureCapacity(size); - Array.Copy(v.value, 0, m_buffer, (int)m_position, size); - m_position += size; - } - } - - public unsafe void Serialize(IEnumerable enumerable) - { - switch(enumerable) - { - case byte[] b: - fixed (byte* p_value = b) - { - var size = (uint)b.LongLength; - if (size <= byte.MaxValue) - Write(MsgPackCode.Bin8, unchecked((byte)size)); - else if (size <= ushort.MaxValue) - Write(MsgPackCode.Bin16, unchecked((byte)size)); - else - Write(MsgPackCode.Bin32, unchecked((byte)size)); - EnsureCapacity(size); - Array.Copy(b, 0, m_buffer, (int)m_position, size); - m_position += size; - } - break; + { + fixed (byte* p_value = v.value) + { + uint size = (uint)v.value.LongLength - 1u; + + if (size < (MsgPackCode.FixStrMax - MsgPackCode.FixStrMin)) + Write(unchecked((byte)((uint)MsgPackCode.FixStrMin + size))); + else if (size <= byte.MaxValue) + Write(MsgPackCode.Str8, unchecked((byte)size)); + else if (size <= ushort.MaxValue) + Write(MsgPackCode.Str16, unchecked((byte)size)); + else + Write(MsgPackCode.Str32, unchecked((byte)size)); + + EnsureCapacity(size); + Array.Copy(v.value, 0, m_buffer, (int)m_position, size); + m_position += size; } + } + public unsafe void Serialize(IEnumerable enumerable) + { + if (enumerable == null) + { + WriteNil(); + return; + } + switch (enumerable) + { + case byte[] b: + fixed (byte* p_value = b) + { + var size = (uint)b.LongLength; + if (size <= byte.MaxValue) + Write(MsgPackCode.Bin8, unchecked((byte)size)); + else if (size <= ushort.MaxValue) + Write(MsgPackCode.Bin16, unchecked((byte)size)); + else + Write(MsgPackCode.Bin32, unchecked((byte)size)); + EnsureCapacity(size); + Array.Copy(b, 0, m_buffer, (int)m_position, size); + m_position += size; + } + break; + case IDictionary dictionary: + WriteMapHeader((uint)dictionary.Count); + foreach (DictionaryEntry entry in dictionary) + { + Serialize(entry.Key); + Serialize(entry.Value); + } + break; + case IList list: + WriteArrayHeader((uint)list.Count); + for (int i = 0; i < list.Count; ++i) + Serialize(list[i]); + break; + } } - + #endregion #region Premade associative array serializers public void Serialize(IReadOnlyDictionary v) - { - WriteMapHeader((uint)v.Count); - foreach (var keyValue in v) - { - Serialize(keyValue.Key); - Serialize(keyValue.Value); - } - } - - public void Serialize(Dictionary v) => Serialize((IReadOnlyDictionary)v); - public void Serialize(IDictionary v) => Serialize((IReadOnlyDictionary)v); - - #endregion - - #region Premade array serializers - - public void Serialize(object[] v) - { - WriteArrayHeader((uint)v.Length); - - for (int i = 0; i < v.Length; ++i) - { - Serialize(v[i]); - } - } - - public void Serialize(string[] v) - { - WriteArrayHeader((uint)v.Length); - - for (int i = 0; i < v.Length; ++i) - { - Serialize(v[i]); - } - } - - #endregion - - - - #region Extra types - - public void Serialize(Callback v) - { - //WriteExtraTypeHeader(v.) - - throw new InvalidCastException($"Can't serialize {nameof(Callback)}, unsupported at this moment"); - } - - #endregion - - #region Direct write operations - - internal void WriteMapHeader(uint v) - { - if (v < (MsgPackCode.FixMapMax - MsgPackCode.FixMapMin)) - Write(unchecked((byte)((uint)MsgPackCode.FixMapMin + v))); - else if (v <= short.MaxValue) - WriteBigEndian(MsgPackCode.Map16, (short)v); - else - WriteBigEndian(MsgPackCode.Map32, v); - } - - internal void WriteArrayHeader(uint v) - { - if (v < (MsgPackCode.FixArrayMax - MsgPackCode.FixArrayMin)) - Write(unchecked((byte)((uint)MsgPackCode.FixArrayMin + v))); - else if (v <= short.MaxValue) - WriteBigEndian(MsgPackCode.Array16, (short)v); - else - WriteBigEndian(MsgPackCode.Array32, v); - } - - internal void WriteExtraTypeHeader(uint length, byte extType = 0) - { - switch (length) - { - case 0: throw new ArgumentException("Extra type can't be 0 sized"); - case 1: Write((byte)MsgPackCode.FixExt1); break; - case 2: Write((byte)MsgPackCode.FixExt2); break; - case 4: Write((byte)MsgPackCode.FixExt4); break; - case 8: Write((byte)MsgPackCode.FixExt8); break; - case 16: Write((byte)MsgPackCode.FixExt16); break; - } - - if (length <= 0xFFU) - Write(MsgPackCode.Ext8, (byte)length); - else if (length <= 0xFFFFU) - WriteBigEndian(MsgPackCode.Ext16, (ushort)length); - else - WriteBigEndian(MsgPackCode.Ext32, length); - } - - private void Write(byte code) - { - EnsureCapacity(1); - m_buffer[m_position++] = code; - } - - public void WriteNil() - { - Write((byte)MsgPackCode.Nil); - } - - private void Write(MsgPackCode code, byte value) - { - EnsureCapacity(2); - ulong pos = m_position; - m_position += 2; - m_buffer[pos] = (byte)code; - m_buffer[pos + 1] = value; - } - - private void WriteBigEndian(MsgPackCode code, ushort value) - { - EnsureCapacity(3); - ulong pos = m_position; - m_position += 3; - m_buffer[pos] = (byte)code; - if (BitConverter.IsLittleEndian) - { - m_buffer[pos + 1] = unchecked((byte)(value >> 8)); - m_buffer[pos + 2] = unchecked((byte)(value)); - } - else - { - m_buffer[pos + 1] = unchecked((byte)value); - m_buffer[pos + 2] = unchecked((byte)(value >> 8)); - } - } - - private unsafe void WriteBigEndian(MsgPackCode code, uint v) - { - EnsureCapacity(5); - fixed (byte* p_buffer = m_buffer) - { - byte* ptr = p_buffer + m_position; - m_position += 5; - - if (BitConverter.IsLittleEndian) - { - v = (v >> 16) | (v << 16); // swap adjacent 16-bit blocks - v = ((v & 0xFF00FF00u) >> 8) | ((v & 0x00FF00FFu) << 8); // swap adjacent 8-bit blocks - } - - *ptr = (byte)code; - *(uint*)(ptr + 1) = v; - } - } - - private unsafe void WriteBigEndian(MsgPackCode code, ulong v) - { - EnsureCapacity(9); - fixed (byte* p_buffer = m_buffer) - { - byte* ptr = p_buffer + m_position; - m_position += 9; - - if (BitConverter.IsLittleEndian) - { - v = (v >> 32) | (v << 32); // swap adjacent 32-bit blocks - v = ((v & 0xFFFF0000FFFF0000u) >> 16) | ((v & 0x0000FFFF0000FFFFu) << 16); // swap adjacent 16-bit blocks - v = ((v & 0xFF00FF00FF00FF00u) >> 8) | ((v & 0x00FF00FF00FF00FFu) << 8); // swap adjacent 8-bit blocks - } - - *ptr = (byte)code; - *(ulong*)(ptr + 1) = v; - } - } + { + WriteMapHeader((uint)v.Count); + foreach (var keyValue in v) + { + Serialize(keyValue.Key); + Serialize(keyValue.Value); + } + } + + public void Serialize(Dictionary v) => Serialize((IReadOnlyDictionary)v); + public void Serialize(IDictionary v) => Serialize((IReadOnlyDictionary)v); + + #endregion + + #region Premade array serializers + + public void Serialize(object[] v) + { + WriteArrayHeader((uint)v.Length); + + for (int i = 0; i < v.Length; ++i) + { + Serialize(v[i]); + } + } + + public void Serialize(string[] v) + { + WriteArrayHeader((uint)v.Length); + + for (int i = 0; i < v.Length; ++i) + { + Serialize(v[i]); + } + } + + #endregion + + + + #region Extra types + + public unsafe void SerializeType(object obj) + { + if (obj is null) + { + WriteNil(); + return; + } + var type = obj.GetType(); + if (type.GetCustomAttribute() is MsgPackSerializableAttribute serializable && serializable.Layout != Layout.Default) + { + if (serializable.Layout == Layout.Indexed) + { + Detail.DynamicArray allMembers = TypeFormatter.GetReadableIndexMembers(type); + MemberInfo[] members = new MemberInfo[allMembers.Count]; + + for (uint i = 0; i < allMembers.Count; ++i) + { + var member = allMembers[i]; + if (member.GetCustomAttribute() is IndexAttribute index) + { + if (members[index.Index] == null) + members[index.Index] = member; + else + throw new FormatException($"Duplicate index, can't add {member.Name} in slot {index.Index} as it's already taken by {members[index.Index].Name}"); + } + } + + if (members.Length == 0) + throw new ArgumentException($"Type {type} can't be serialized by arrays, no {nameof(IndexAttribute)} has been found on any field or property"); + + int length = members.Length; + WriteArrayHeader((uint)length); + for (var i = 0; i < length; i++) + { + switch (members[i]) + { + case FieldInfo field: + Serialize(field.GetValue(obj)); + break; + case PropertyInfo property: + Serialize(property.GetValue(obj)); + break; + + default: + throw new ArgumentException($"Member type {members[i].GetType()} is not supported"); + + } + } + } + else if (serializable.Layout == Layout.Keyed) + { + serializeMembers(obj, TypeFormatter.GetReadAndWritableKeyedMembers(type)); + } + } + else + { + serializeMembers(obj, TypeFormatter.GetWritableMembers(type)); + } + } + + private unsafe void serializeMembers(object obj, Detail.DynamicArray members) + { + var length = members.Count; + WriteMapHeader((uint)length); + for (var i = 0; i < length; i++) + { + var member = members[i]; + string memberName = member.GetCustomAttribute()?.Key ?? member.Name; + Serialize(memberName); + switch (members[i]) + { + case FieldInfo field: + Serialize(field.GetValue(obj)); + break; + case PropertyInfo property: + Serialize(property.GetValue(obj)); + break; + + default: + throw new ArgumentException($"Member type {members[i].GetType()} is not supported"); + } + } + + } + + public unsafe void Serialize(KeyValuePair v) + { + // Serializza come mappa con un solo elemento + WriteMapHeader(1); + Serialize(v.Key); + Serialize(v.Value); + return; + } + + public void Serialize(Callback v) + { + //WriteExtraTypeHeader(v.) + + throw new InvalidCastException($"Can't serialize {nameof(Callback)}, unsupported at this moment"); + } + + #endregion + + #region Direct write operations + + internal void WriteMapHeader(uint v) + { + if (v < (MsgPackCode.FixMapMax - MsgPackCode.FixMapMin)) + Write(unchecked((byte)((uint)MsgPackCode.FixMapMin + v))); + else if (v <= short.MaxValue) + WriteBigEndian(MsgPackCode.Map16, (short)v); + else + WriteBigEndian(MsgPackCode.Map32, v); + } + + internal void WriteArrayHeader(uint v) + { + if (v < (MsgPackCode.FixArrayMax - MsgPackCode.FixArrayMin)) + Write(unchecked((byte)((uint)MsgPackCode.FixArrayMin + v))); + else if (v <= short.MaxValue) + WriteBigEndian(MsgPackCode.Array16, (short)v); + else + WriteBigEndian(MsgPackCode.Array32, v); + } + + internal void WriteExtraTypeHeader(uint length, byte extType = 0) + { + switch (length) + { + case 0: throw new ArgumentException("Extra type can't be 0 sized"); + case 1: Write((byte)MsgPackCode.FixExt1); break; + case 2: Write((byte)MsgPackCode.FixExt2); break; + case 4: Write((byte)MsgPackCode.FixExt4); break; + case 8: Write((byte)MsgPackCode.FixExt8); break; + case 16: Write((byte)MsgPackCode.FixExt16); break; + } + + if (length <= 0xFFU) + Write(MsgPackCode.Ext8, (byte)length); + else if (length <= 0xFFFFU) + WriteBigEndian(MsgPackCode.Ext16, (ushort)length); + else + WriteBigEndian(MsgPackCode.Ext32, length); + } + + private void Write(byte code) + { + EnsureCapacity(1); + m_buffer[m_position++] = code; + } + + public void WriteNil() + { + Write((byte)MsgPackCode.Nil); + } + + private void Write(MsgPackCode code, byte value) + { + EnsureCapacity(2); + ulong pos = m_position; + m_position += 2; + m_buffer[pos] = (byte)code; + m_buffer[pos + 1] = value; + } + + private void WriteBigEndian(MsgPackCode code, ushort value) + { + EnsureCapacity(3); + ulong pos = m_position; + m_position += 3; + m_buffer[pos] = (byte)code; + if (BitConverter.IsLittleEndian) + { + m_buffer[pos + 1] = unchecked((byte)(value >> 8)); + m_buffer[pos + 2] = unchecked((byte)(value)); + } + else + { + m_buffer[pos + 1] = unchecked((byte)value); + m_buffer[pos + 2] = unchecked((byte)(value >> 8)); + } + } + + private unsafe void WriteBigEndian(MsgPackCode code, uint v) + { + EnsureCapacity(5); + fixed (byte* p_buffer = m_buffer) + { + byte* ptr = p_buffer + m_position; + m_position += 5; + + if (BitConverter.IsLittleEndian) + { + v = (v >> 16) | (v << 16); // swap adjacent 16-bit blocks + v = ((v & 0xFF00FF00u) >> 8) | ((v & 0x00FF00FFu) << 8); // swap adjacent 8-bit blocks + } + + *ptr = (byte)code; + *(uint*)(ptr + 1) = v; + } + } + + private unsafe void WriteBigEndian(MsgPackCode code, ulong v) + { + EnsureCapacity(9); + fixed (byte* p_buffer = m_buffer) + { + byte* ptr = p_buffer + m_position; + m_position += 9; + + if (BitConverter.IsLittleEndian) + { + v = (v >> 32) | (v << 32); // swap adjacent 32-bit blocks + v = ((v & 0xFFFF0000FFFF0000u) >> 16) | ((v & 0x0000FFFF0000FFFFu) << 16); // swap adjacent 16-bit blocks + v = ((v & 0xFF00FF00FF00FF00u) >> 8) | ((v & 0x00FF00FF00FF00FFu) << 8); // swap adjacent 8-bit blocks + } + + *ptr = (byte)code; + *(ulong*)(ptr + 1) = v; + } + } private void PrivatePackExtendedTypeValueCore(byte typeCode, byte[] body) { @@ -517,12 +630,12 @@ private void PrivatePackExtendedTypeValueCore(byte typeCode, byte[] body) } } private void WriteBigEndian(MsgPackCode code, sbyte value) => WriteBigEndian(code, unchecked((byte)value)); - private void WriteBigEndian(MsgPackCode code, short value) => WriteBigEndian(code, unchecked((ushort)value)); - private void WriteBigEndian(MsgPackCode code, int value) => WriteBigEndian(code, unchecked((uint)value)); - private void WriteBigEndian(MsgPackCode code, long value) => WriteBigEndian(code, unchecked((ulong)value)); - private unsafe void WriteBigEndian(MsgPackCode code, float value) => WriteBigEndian(code, *(uint*)&value); - private unsafe void WriteBigEndian(MsgPackCode code, double value) => WriteBigEndian(code, *(ulong*)&value); - - #endregion - } + private void WriteBigEndian(MsgPackCode code, short value) => WriteBigEndian(code, unchecked((ushort)value)); + private void WriteBigEndian(MsgPackCode code, int value) => WriteBigEndian(code, unchecked((uint)value)); + private void WriteBigEndian(MsgPackCode code, long value) => WriteBigEndian(code, unchecked((ulong)value)); + private unsafe void WriteBigEndian(MsgPackCode code, float value) => WriteBigEndian(code, *(uint*)&value); + private unsafe void WriteBigEndian(MsgPackCode code, double value) => WriteBigEndian(code, *(ulong*)&value); + + #endregion + } }