diff --git a/src/MessagePack/Formatters/PrimitiveFormatter.cs b/src/MessagePack/Formatters/PrimitiveFormatter.cs index 65d3cb103..0d9178565 100644 --- a/src/MessagePack/Formatters/PrimitiveFormatter.cs +++ b/src/MessagePack/Formatters/PrimitiveFormatter.cs @@ -6,6 +6,12 @@ using System; using System.Buffers; +using System.Collections.Generic; +using MessagePack.Internal; + +#if NET6_0_OR_GREATER +using System.Runtime.InteropServices; +#endif #pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name @@ -81,9 +87,14 @@ public void Serialize(ref MessagePackWriter writer, Int16[]? value, MessagePackS else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -111,6 +122,58 @@ public void Serialize(ref MessagePackWriter writer, Int16[]? value, MessagePackS } } +#if NET8_0_OR_GREATER + public sealed class Int16ListFormatter : IMessagePackFormatter?> + { + public static readonly Int16ListFormatter Instance = new Int16ListFormatter(); + + private Int16ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadInt16(); + } + + return list; + } + } +#endif + public sealed class Int32Formatter : IMessagePackFormatter { public static readonly Int32Formatter Instance = new Int32Formatter(); @@ -180,9 +243,14 @@ public void Serialize(ref MessagePackWriter writer, Int32[]? value, MessagePackS else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -210,6 +278,58 @@ public void Serialize(ref MessagePackWriter writer, Int32[]? value, MessagePackS } } +#if NET8_0_OR_GREATER + public sealed class Int32ListFormatter : IMessagePackFormatter?> + { + public static readonly Int32ListFormatter Instance = new Int32ListFormatter(); + + private Int32ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadInt32(); + } + + return list; + } + } +#endif + public sealed class Int64Formatter : IMessagePackFormatter { public static readonly Int64Formatter Instance = new Int64Formatter(); @@ -279,9 +399,14 @@ public void Serialize(ref MessagePackWriter writer, Int64[]? value, MessagePackS else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -309,6 +434,58 @@ public void Serialize(ref MessagePackWriter writer, Int64[]? value, MessagePackS } } +#if NET8_0_OR_GREATER + public sealed class Int64ListFormatter : IMessagePackFormatter?> + { + public static readonly Int64ListFormatter Instance = new Int64ListFormatter(); + + private Int64ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadInt64(); + } + + return list; + } + } +#endif + public sealed class UInt16Formatter : IMessagePackFormatter { public static readonly UInt16Formatter Instance = new UInt16Formatter(); @@ -378,9 +555,14 @@ public void Serialize(ref MessagePackWriter writer, UInt16[]? value, MessagePack else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -408,6 +590,58 @@ public void Serialize(ref MessagePackWriter writer, UInt16[]? value, MessagePack } } +#if NET8_0_OR_GREATER + public sealed class UInt16ListFormatter : IMessagePackFormatter?> + { + public static readonly UInt16ListFormatter Instance = new UInt16ListFormatter(); + + private UInt16ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadUInt16(); + } + + return list; + } + } +#endif + public sealed class UInt32Formatter : IMessagePackFormatter { public static readonly UInt32Formatter Instance = new UInt32Formatter(); @@ -477,9 +711,14 @@ public void Serialize(ref MessagePackWriter writer, UInt32[]? value, MessagePack else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -507,6 +746,58 @@ public void Serialize(ref MessagePackWriter writer, UInt32[]? value, MessagePack } } +#if NET8_0_OR_GREATER + public sealed class UInt32ListFormatter : IMessagePackFormatter?> + { + public static readonly UInt32ListFormatter Instance = new UInt32ListFormatter(); + + private UInt32ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadUInt32(); + } + + return list; + } + } +#endif + public sealed class UInt64Formatter : IMessagePackFormatter { public static readonly UInt64Formatter Instance = new UInt64Formatter(); @@ -576,9 +867,14 @@ public void Serialize(ref MessagePackWriter writer, UInt64[]? value, MessagePack else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -606,6 +902,58 @@ public void Serialize(ref MessagePackWriter writer, UInt64[]? value, MessagePack } } +#if NET8_0_OR_GREATER + public sealed class UInt64ListFormatter : IMessagePackFormatter?> + { + public static readonly UInt64ListFormatter Instance = new UInt64ListFormatter(); + + private UInt64ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadUInt64(); + } + + return list; + } + } +#endif + public sealed class SingleFormatter : IMessagePackFormatter { public static readonly SingleFormatter Instance = new SingleFormatter(); @@ -675,9 +1023,14 @@ public void Serialize(ref MessagePackWriter writer, Single[]? value, MessagePack else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -705,6 +1058,58 @@ public void Serialize(ref MessagePackWriter writer, Single[]? value, MessagePack } } +#if NET8_0_OR_GREATER + public sealed class SingleListFormatter : IMessagePackFormatter?> + { + public static readonly SingleListFormatter Instance = new SingleListFormatter(); + + private SingleListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadSingle(); + } + + return list; + } + } +#endif + public sealed class DoubleFormatter : IMessagePackFormatter { public static readonly DoubleFormatter Instance = new DoubleFormatter(); @@ -774,9 +1179,14 @@ public void Serialize(ref MessagePackWriter writer, Double[]? value, MessagePack else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -804,6 +1214,58 @@ public void Serialize(ref MessagePackWriter writer, Double[]? value, MessagePack } } +#if NET8_0_OR_GREATER + public sealed class DoubleListFormatter : IMessagePackFormatter?> + { + public static readonly DoubleListFormatter Instance = new DoubleListFormatter(); + + private DoubleListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadDouble(); + } + + return list; + } + } +#endif + public sealed class BooleanFormatter : IMessagePackFormatter { public static readonly BooleanFormatter Instance = new BooleanFormatter(); @@ -873,9 +1335,14 @@ public void Serialize(ref MessagePackWriter writer, Boolean[]? value, MessagePac else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -903,6 +1370,58 @@ public void Serialize(ref MessagePackWriter writer, Boolean[]? value, MessagePac } } +#if NET8_0_OR_GREATER + public sealed class BooleanListFormatter : IMessagePackFormatter?> + { + public static readonly BooleanListFormatter Instance = new BooleanListFormatter(); + + private BooleanListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadBoolean(); + } + + return list; + } + } +#endif + public sealed class ByteFormatter : IMessagePackFormatter { public static readonly ByteFormatter Instance = new ByteFormatter(); @@ -955,6 +1474,76 @@ public void Serialize(ref MessagePackWriter writer, Byte? value, MessagePackSeri } } +#if NET8_0_OR_GREATER + public sealed class ByteListFormatter : IMessagePackFormatter?> + { + public static readonly ByteListFormatter Instance = new ByteListFormatter(); + + private ByteListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.Write(CollectionsMarshal.AsSpan(value)); + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.NextMessagePackType == MessagePackType.Array) + { + int len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + options.Security.DepthStep(ref reader); + try + { + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + reader.CancellationToken.ThrowIfCancellationRequested(); + span[i] = reader.ReadByte(); + } + } + finally + { + reader.Depth--; + } + + return list; + } + else + { + var sequence = reader.ReadBytes(); + if (sequence == null) + { + return null; + } + + int len = checked((int)sequence.Value.Length); + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + sequence.Value.CopyTo(span); + return list; + } + } + } + +#endif + public sealed class SByteFormatter : IMessagePackFormatter { public static readonly SByteFormatter Instance = new SByteFormatter(); @@ -1024,9 +1613,14 @@ public void Serialize(ref MessagePackWriter writer, SByte[]? value, MessagePackS else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -1054,6 +1648,58 @@ public void Serialize(ref MessagePackWriter writer, SByte[]? value, MessagePackS } } +#if NET8_0_OR_GREATER + public sealed class SByteListFormatter : IMessagePackFormatter?> + { + public static readonly SByteListFormatter Instance = new SByteListFormatter(); + + private SByteListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadSByte(); + } + + return list; + } + } +#endif + public sealed class CharFormatter : IMessagePackFormatter { public static readonly CharFormatter Instance = new CharFormatter(); @@ -1123,9 +1769,14 @@ public void Serialize(ref MessagePackWriter writer, Char[]? value, MessagePackSe else { writer.WriteArrayHeader(value.Length); - for (int i = 0; i < value.Length; i++) + if (value.Length > 0) { - writer.Write(value[i]); + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif } } } @@ -1153,6 +1804,58 @@ public void Serialize(ref MessagePackWriter writer, Char[]? value, MessagePackSe } } +#if NET8_0_OR_GREATER + public sealed class CharListFormatter : IMessagePackFormatter?> + { + public static readonly CharListFormatter Instance = new CharListFormatter(); + + private CharListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.ReadChar(); + } + + return list; + } + } +#endif + public sealed class DateTimeFormatter : IMessagePackFormatter { public static readonly DateTimeFormatter Instance = new DateTimeFormatter(); diff --git a/src/MessagePack/Formatters/PrimitiveFormatter.tt b/src/MessagePack/Formatters/PrimitiveFormatter.tt index ca40b6b3c..1c8451142 100644 --- a/src/MessagePack/Formatters/PrimitiveFormatter.tt +++ b/src/MessagePack/Formatters/PrimitiveFormatter.tt @@ -30,6 +30,12 @@ using System; using System.Buffers; +using System.Collections.Generic; +using MessagePack.Internal; + +#if NET6_0_OR_GREATER +using System.Runtime.InteropServices; +#endif #pragma warning disable SA1402 // File may only contain a single type #pragma warning disable SA1649 // File name should match first type name @@ -88,7 +94,79 @@ namespace MessagePack.Formatters } } } -<# if(t.Name != "Byte") { #> +<# if(t.Name == "Byte") { #> + +#if NET8_0_OR_GREATER + public sealed class <#= t.Name #>ListFormatter : IMessagePackFormatter>?> + { + public static readonly <#= t.Name #>ListFormatter Instance = new <#= t.Name #>ListFormatter(); + + private <#= t.Name #>ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List<<#= t.Name #>>? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.Write(CollectionsMarshal.AsSpan(value)); + } + } + + public List<<#= t.Name #>>? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.NextMessagePackType == MessagePackType.Array) + { + int len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List(len); + options.Security.DepthStep(ref reader); + try + { + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + reader.CancellationToken.ThrowIfCancellationRequested(); + span[i] = reader.ReadByte(); + } + } + finally + { + reader.Depth--; + } + + return list; + } + else + { + var sequence = reader.ReadBytes(); + if (sequence == null) + { + return null; + } + + int len = checked((int)sequence.Value.Length); + var list = new List(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + sequence.Value.CopyTo(span); + return list; + } + } + } + +#endif +<# } #> +<# else if(t.Name == "DateTime") { #> public sealed class <#= t.Name #>ArrayFormatter : IMessagePackFormatter<<#= t.Name #>[]?> { @@ -137,5 +215,111 @@ namespace MessagePack.Formatters } } <# } #> +<# else { #> + + public sealed class <#= t.Name #>ArrayFormatter : IMessagePackFormatter<<#= t.Name #>[]?> + { + public static readonly <#= t.Name #>ArrayFormatter Instance = new <#= t.Name #>ArrayFormatter(); + + private <#= t.Name #>ArrayFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, <#= t.Name #>[]? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Length); + if (value.Length > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); +#if NET6_0_OR_GREATER + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetArrayDataReference(value), value.Length); +#else + UnsafeRefSerializeHelper.Serialize(ref writer, ref value[0], value.Length); +#endif + } + } + } + + public <#= t.Name #>[]? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return Array.Empty<<#= t.Name #>>(); + } + + var array = new <#= t.Name #>[len]; + for (int i = 0; i < array.Length; i++) + { + array[i] = reader.Read<#= t.Name #>(); + } + + return array; + } + } + +#if NET8_0_OR_GREATER + public sealed class <#= t.Name #>ListFormatter : IMessagePackFormatter>?> + { + public static readonly <#= t.Name #>ListFormatter Instance = new <#= t.Name #>ListFormatter(); + + private <#= t.Name #>ListFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, List<<#= t.Name #>>? value, MessagePackSerializerOptions options) + { + if (value == null) + { + writer.WriteNil(); + } + else + { + writer.WriteArrayHeader(value.Count); + if (value.Count > 0) + { + writer.CancellationToken.ThrowIfCancellationRequested(); + UnsafeRefSerializeHelper.Serialize(ref writer, ref MemoryMarshal.GetReference(CollectionsMarshal.AsSpan(value)), value.Count); + } + } + } + + public List<<#= t.Name #>>? Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + if (reader.TryReadNil()) + { + return default; + } + + var len = reader.ReadArrayHeader(); + if (len == 0) + { + return []; + } + + var list = new List<<#= t.Name #>>(len); + CollectionsMarshal.SetCount(list, len); + var span = CollectionsMarshal.AsSpan(list); + for (int i = 0; i < len; i++) + { + span[i] = reader.Read<#= t.Name #>(); + } + + return list; + } + } +#endif +<# } #> <# } #> } diff --git a/src/MessagePack/Internal/UnsafeRefSerializeHelper.cs b/src/MessagePack/Internal/UnsafeRefSerializeHelper.cs new file mode 100644 index 000000000..a5fd8f216 --- /dev/null +++ b/src/MessagePack/Internal/UnsafeRefSerializeHelper.cs @@ -0,0 +1,1566 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* + * The reason why serialize method signature is void Serialize(ref MessagePackWriter writer, ref T input, int length) + * 1. Vector128.Load method requires only ref T or T*, not ReadOnlySpan. + * 2. For the purpose of getting ref T, MemoryMarshal.GetArrayDataReference is performant than ReadOnlySpan conversion + MemoryMarshal.GetReference. + * 3. Unity IL2CPP does not treat ReadOnlySpan as first-class-citizen. + * + * When .NET team introduces the overload method for ReadOnlySpan, the signature can be changed. + */ + +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +#if NET8_0_OR_GREATER +using System.Runtime.Intrinsics; +#endif + +namespace MessagePack.Internal; + +internal static class UnsafeRefSerializeHelper +{ + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref bool input, int length) + { +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~15; + var alignedInputLength = (nuint)(uint)(length & mask); + if (alignedInputLength > 0) + { + var destination = writer.GetSpan((int)alignedInputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + // Load 16 bools. + var loaded = Vector128.LoadUnsafe(ref Unsafe.As(ref input), inputOffset); + + // true is not 0, but false is always 0 in C#. true -> 0. false -> -1. + var falses = Vector128.Equals(loaded, Vector128.Zero); + + // MessagePackCode.True is 0xc3(-61). MessagePackCode.False is 0xc2(-62). + var results = Vector128.Create((sbyte)MessagePackCode.True) + falses; + + // Store 16 values. + results.AsByte().StoreUnsafe(ref outputIterator, inputOffset); + } + + writer.Advance((int)alignedInputLength); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + { + var destination = writer.GetSpan(length); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint index = 0; index < (nuint)length; index++) + { + Unsafe.Add(ref outputIterator, index) = Unsafe.Add(ref input, index) ? MessagePackCode.True : MessagePackCode.False; + } + } + + writer.Advance(length); + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref sbyte input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref sbyte input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref sbyte input, int length) + { + const int maxOutputElementSize = sizeof(sbyte) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector256.IsHardwareAccelerated) + { + const int mask = ~31; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector256.Count) + { + var loaded = Vector256.LoadUnsafe(ref input, inputOffset); + + // Less than MinFixNegativeInt value requires 2 byte. + var lessThanMinFixNegativeInt = Vector256.ExtractMostSignificantBits(Vector256.LessThan(loaded, Vector256.Create((sbyte)MessagePackRange.MinFixNegativeInt))); + if (lessThanMinFixNegativeInt == 0) + { + loaded.AsByte().StoreUnsafe(ref Unsafe.Add(ref outputIterator, outputOffset)); + outputOffset += (nuint)Vector256.Count; + } + else + { + for (var i = 0; i < Vector256.Count; i++, lessThanMinFixNegativeInt >>= 1) + { + var inputValue = Unsafe.Add(ref input, inputOffset + (nuint)i); + if ((lessThanMinFixNegativeInt & 1) == 1) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = (byte)inputValue; + outputOffset += 2; + } + else + { + Unsafe.Add(ref outputIterator, outputOffset) = (byte)inputValue; + outputOffset += 1; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + else if (Vector128.IsHardwareAccelerated) + { + const int mask = ~15; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset); + + // Less than MinFixNegativeInt value requires 2 byte. + var lessThanMinFixNegativeInt = Vector128.ExtractMostSignificantBits(Vector128.LessThan(loaded, Vector128.Create((sbyte)MessagePackRange.MinFixNegativeInt))); + if (lessThanMinFixNegativeInt == 0) + { + loaded.AsByte().StoreUnsafe(ref Unsafe.Add(ref outputIterator, outputOffset)); + outputOffset += (nuint)Vector128.Count; + } + else + { + for (var i = 0; i < Vector128.Count; i++, lessThanMinFixNegativeInt >>= 1) + { + var inputValue = Unsafe.Add(ref input, inputOffset + (nuint)i); + if ((lessThanMinFixNegativeInt & 1) == 1) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = (byte)inputValue; + outputOffset += 2; + } + else + { + Unsafe.Add(ref outputIterator, outputOffset) = (byte)inputValue; + outputOffset += 1; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref short input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref short input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref short input, int length) + { + const int maxOutputElementSize = sizeof(short) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~7; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset); + + // Less than sbyte.MinValue value requires 3 byte. + var gteSByteMinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((short)sbyte.MinValue)); + + // Less than MinFixNegativeInt value requires 2 byte. + var gteMinFixNegativeInt = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((short)MessagePackRange.MinFixNegativeInt)); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create((short)MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((short)byte.MaxValue)); + + // 0 -> Int16, 3byte + // 1 -> Int8, 2byte + // 2 -> None, 1byte + // 3 -> UInt8, 2byte + // 4 -> UInt16, 3byte + var kinds = -(gteSByteMinValue + gteMinFixNegativeInt + gtMaxFixPositiveInt + gtByteMaxValue); + if (kinds == Vector128.Create((short)2)) + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 2, 4, 6, 8, 10, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt64().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(ulong); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14)).AsInt16(); + + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 3; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 2) + 1); + outputOffset += 2; + break; + case 2: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.AsByte().GetElement((i * 2) + 1); + outputOffset += 1; + break; + case 3: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 2) + 1); + outputOffset += 2; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 3; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref char input, int length) + { + Serialize(ref writer, ref Unsafe.As(ref input), length); + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref ushort input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref ushort input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref ushort input, int length) + { + const int maxOutputElementSize = sizeof(ushort) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~7; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * (sizeof(short) + 1)); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset).AsInt16(); + + // LessThan 0 means ushort max range and requires 3 byte. + var gte0 = Vector128.GreaterThanOrEqual(loaded, Vector128.Zero); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create((short)MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((short)byte.MaxValue)); + + // -1 -> UInt16, 3byte + // +0 -> None, 1byte + // +1 -> UInt8, 2byte + // +2 -> UInt16, 3byte + // Vector128.AllBitsSet means -1. + var kinds = Vector128.AllBitsSet - gte0 - gtMaxFixPositiveInt - gtByteMaxValue; + if (kinds == Vector128.Zero) + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 2, 4, 6, 8, 10, 12, 14, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt64().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(ulong); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14)); + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.GetElement((i * 2) + 1); + outputOffset += 1; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.GetElement((i * 2) + 1); + outputOffset += 2; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt16().GetElement(i)); + outputOffset += 3; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref int input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref int input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref int input, int length) + { + const int maxOutputElementSize = sizeof(int) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~3; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset); + + // Less than short.MinValue value requires 5 byte. + var gteInt16MinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((int)short.MinValue)); + + // Less than sbyte.MinValue value requires 3 byte. + var gteSByteMinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((int)sbyte.MinValue)); + + // Less than MinFixNegativeInt value requires 2 byte. + var gteMinFixNegativeInt = Vector128.GreaterThanOrEqual(loaded, Vector128.Create(MessagePackRange.MinFixNegativeInt)); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create(MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((int)byte.MaxValue)); + + // GreaterThan ushort.MaxValue value requires 5 byte. + var gtUInt16MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((int)ushort.MaxValue)); + + // 0 -> Int32, 5byte + // 1 -> Int16, 3byte + // 2 -> Int8, 2byte + // 3 -> None, 1byte + // 4 -> UInt8, 2byte + // 5 -> UInt16, 3byte + // 6 -> UInt32, 5byte + var kinds = -(gteInt16MinValue + gteSByteMinValue + gteMinFixNegativeInt + gtMaxFixPositiveInt + gtByteMaxValue + gtUInt16MaxValue); + if (kinds == Vector128.Create(3)) + { + // Reorder Big-Endian and Narrowing + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 4, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt32().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(uint); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)).AsInt32(); + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 5; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsInt16().GetElement((i * 2) + 1)); + outputOffset += 3; + break; + case 2: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 4) + 3); + outputOffset += 2; + break; + case 3: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.AsByte().GetElement((i * 4) + 3); + outputOffset += 1; + break; + case 4: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 4) + 3); + outputOffset += 2; + break; + case 5: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt16().GetElement((i * 2) + 1)); + outputOffset += 3; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 5; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref uint input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref uint input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref uint input, int length) + { + const int maxOutputElementSize = sizeof(uint) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~3; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset).AsInt32(); + + // LessThan 0 means ushort max range and requires 5 byte. + var gte0 = Vector128.GreaterThanOrEqual(loaded, Vector128.Zero); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create(MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((int)byte.MaxValue)); + + // GreaterThan ushort.MaxValue value requires 5 byte. + var gtUInt16MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((int)ushort.MaxValue)); + + // -1 -> UInt32, 5byte + // +0 -> None, 1byte + // +1 -> UInt8, 2byte + // +2 -> UInt16, 3byte + // +3 -> UInt32, 5byte + var kinds = Vector128.AllBitsSet - gte0 - gtMaxFixPositiveInt - gtByteMaxValue - gtUInt16MaxValue; + if (kinds == Vector128.Zero) + { + // Reorder Big-Endian and Narrowing + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 4, 8, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt32().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(uint); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)); + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.GetElement((i * 4) + 3); + outputOffset += 1; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.GetElement((i * 4) + 3); + outputOffset += 2; + break; + case 2: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt16().GetElement((i * 2) + 1)); + outputOffset += 3; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt32().GetElement(i)); + outputOffset += 5; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref long input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref long input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref long input, int length) + { + const int maxOutputElementSize = sizeof(long) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~1; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset); + + // Less than int.MinValue value requires 9 byte. + var gteInt32MinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((long)int.MinValue)); + + // Less than short.MinValue value requires 5 byte. + var gteInt16MinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((long)short.MinValue)); + + // Less than sbyte.MinValue value requires 3 byte. + var gteSByteMinValue = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((long)sbyte.MinValue)); + + // Less than MinFixNegativeInt value requires 2 byte. + var gteMinFixNegativeInt = Vector128.GreaterThanOrEqual(loaded, Vector128.Create((long)MessagePackRange.MinFixNegativeInt)); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create((long)MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)byte.MaxValue)); + + // GreaterThan ushort.MaxValue value requires 5 byte. + var gtUInt16MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)ushort.MaxValue)); + + // GreaterThan uint.MaxValue value requires 5 byte. + var gtUInt32MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)uint.MaxValue)); + + // 0 -> Int64, 9byte + // 1 -> Int32, 5byte + // 2 -> Int16, 3byte + // 3 -> Int8, 2byte + // 4 -> None, 1byte + // 5 -> UInt8, 2byte + // 6 -> UInt16, 3byte + // 7 -> UInt32, 5byte + // 8 -> UInt64, 9byte + var kinds = -(gteInt32MinValue + gteInt16MinValue + gteSByteMinValue + gteMinFixNegativeInt + gtMaxFixPositiveInt + gtByteMaxValue + gtUInt16MaxValue + gtUInt32MaxValue); + if (kinds == Vector128.Create(4L)) + { + // Reorder Big-Endian and Narrowing + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt16().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(ushort); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)).AsInt64(); + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 9; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsInt32().GetElement((i * 2) + 1)); + outputOffset += 5; + break; + case 2: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsInt16().GetElement((i * 4) + 3)); + outputOffset += 3; + break; + case 3: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Int8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 8) + 7); + outputOffset += 2; + break; + case 4: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.AsByte().GetElement((i * 8) + 7); + outputOffset += 1; + break; + case 5: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.AsByte().GetElement((i * 8) + 7); + outputOffset += 2; + break; + case 6: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt16().GetElement((i * 4) + 3)); + outputOffset += 3; + break; + case 7: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt32().GetElement((i * 2) + 1)); + outputOffset += 5; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(i)); + outputOffset += 9; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref ulong input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref ulong input, int length) + { + for (int i = 0; i < length; i++) + { + writer.Write(Unsafe.Add(ref input, i)); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref ulong input, int length) + { + const int maxOutputElementSize = sizeof(ulong) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector128.IsHardwareAccelerated) + { + const int mask = ~1; + for (var alignedInputLength = (nuint)(length & mask); alignedInputLength > 0; alignedInputLength = (nuint)(length & mask)) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & mask; + } + + var destination = writer.GetSpan((int)alignedInputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint inputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count) + { + var loaded = Vector128.LoadUnsafe(ref input, inputOffset).AsInt64(); + + // LessThan 0 means ushort max range and requires 5 byte. + var gte0 = Vector128.GreaterThanOrEqual(loaded, Vector128.Zero); + + // GreaterThan MaxFixPositiveInt value requires 2 byte. + var gtMaxFixPositiveInt = Vector128.GreaterThan(loaded, Vector128.Create((long)MessagePackRange.MaxFixPositiveInt)); + + // GreaterThan byte.MaxValue value requires 3 byte. + var gtByteMaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)byte.MaxValue)); + + // GreaterThan ushort.MaxValue value requires 5 byte. + var gtUInt16MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)ushort.MaxValue)); + + // GreaterThan ushort.MaxValue value requires 5 byte. + var gtUInt32MaxValue = Vector128.GreaterThan(loaded, Vector128.Create((long)uint.MaxValue)); + + // -1 -> UInt64, 9byte + // +0 -> None, 1byte + // +1 -> UInt8, 2byte + // +2 -> UInt16, 3byte + // +3 -> UInt32, 5byte + // +4 -> UInt64, 9byte + var kinds = Vector128.AllBitsSet - gte0 - gtMaxFixPositiveInt - gtByteMaxValue - gtUInt16MaxValue - gtUInt32MaxValue; + if (kinds == Vector128.Zero) + { + // Reorder Big-Endian and Narrowing + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)).AsUInt16().GetElement(0); + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset), shuffled); + outputOffset += sizeof(ushort); + } + else + { + // Reorder Big-Endian + var shuffled = Vector128.Shuffle(loaded.AsByte(), Vector128.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)); + for (var i = 0; i < Vector128.Count; i++) + { + switch (kinds.GetElement(i)) + { + case 0: + Unsafe.Add(ref outputIterator, outputOffset) = shuffled.GetElement((i * 8) + 7); + outputOffset += 1; + break; + case 1: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt8; + Unsafe.Add(ref outputIterator, outputOffset + 1) = shuffled.GetElement((i * 8) + 7); + outputOffset += 2; + break; + case 2: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt16().GetElement((i * 4) + 3)); + outputOffset += 3; + break; + case 3: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt32().GetElement((i * 2) + 1)); + outputOffset += 5; + break; + default: + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.UInt64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.AsUInt64().GetElement(i)); + outputOffset += 9; + break; + } + } + } + } + + writer.Advance((int)outputOffset); + length -= (int)alignedInputLength; + input = ref Unsafe.Add(ref input, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var destination = writer.GetSpan(inputLength * maxOutputElementSize); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + nuint outputOffset = 0; + for (nuint index = 0; index < (nuint)inputLength; index++) + { + outputOffset += ReverseWriteUnknown(ref Unsafe.Add(ref outputIterator, outputOffset), Unsafe.Add(ref input, index)); + } + + writer.Advance((int)outputOffset); + length -= inputLength; + input = ref Unsafe.Add(ref input, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref float input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref float input, int length) + { + ref var inputIterator = ref Unsafe.As(ref input); + const int maxOutputElementSize = sizeof(float) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var outputLength = inputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint index = 0, outputOffset = 0; index < (nuint)inputLength; index++, outputOffset += maxOutputElementSize) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), Unsafe.Add(ref inputIterator, index)); + } + + writer.Advance(outputLength); + length -= inputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, inputLength); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref float input, int length) + { + ref var inputIterator = ref Unsafe.As(ref input); + const int maxOutputElementSize = sizeof(float) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector256.IsHardwareAccelerated) + { + for (var alignedInputLength = (nuint)(length & (~7)); alignedInputLength > 0; alignedInputLength = (nuint)(length & (~7))) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & (~7); + } + + var outputLength = (int)alignedInputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint inputOffset = 0, outputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector256.Count, outputOffset += (nuint)Vector256.Count * maxOutputElementSize) + { + // Reorder Little Endian bytes to Big Endian. + var shuffled = Vector256.Shuffle(Vector256.LoadUnsafe(ref inputIterator, inputOffset).AsByte(), Vector256.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12, 19, 18, 17, 16, 23, 22, 21, 20, 27, 26, 25, 24, 31, 30, 29, 28)).AsUInt32(); + + // Write 8 Big-Endian floats. + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 5) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 6), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 10) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 11), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 15) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 16), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 20) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 21), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 25) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 26), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 30) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 31), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 35) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 35), shuffled.GetElement(0)); + } + + writer.Advance(outputLength); + length -= (int)alignedInputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, alignedInputLength); + } + } + else if (Vector128.IsHardwareAccelerated) + { + for (var alignedInputLength = (nuint)(length & (~3)); alignedInputLength > 0; alignedInputLength = (nuint)(length & (~3))) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & (~3); + } + + var outputLength = (int)alignedInputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint inputOffset = 0, outputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count, outputOffset += (nuint)Vector128.Count * maxOutputElementSize) + { + // Reorder Little Endian bytes to Big Endian. + var shuffled = Vector128.Shuffle(Vector128.LoadUnsafe(ref inputIterator, inputOffset).AsByte(), Vector128.Create((byte)3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12)).AsUInt32(); + + // Write 4 Big-Endian floats. + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 5) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 6), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 10) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 11), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 15) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 16), shuffled.GetElement(0)); + } + + writer.Advance(outputLength); + length -= (int)alignedInputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var outputLength = inputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint index = 0, outputOffset = 0; index < (nuint)inputLength; index++, outputOffset += maxOutputElementSize) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref inputIterator, index))); + } + + writer.Advance(outputLength); + length -= inputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, inputLength); + } + } + + /// Unsafe serialize method without parameter checks nor cancellation. + /// MessagePackWriter. + /// Must not be null reference. + /// Must be greater than 0. + internal static void Serialize(ref MessagePackWriter writer, ref double input, int length) + { + if (BitConverter.IsLittleEndian) + { + LittleEndianSerialize(ref writer, ref input, length); + } + else + { + BigEndianSerialize(ref writer, ref input, length); + } + } + + private static void BigEndianSerialize(ref MessagePackWriter writer, ref double input, int length) + { + ref var inputIterator = ref Unsafe.As(ref input); + const int maxOutputElementSize = sizeof(double) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var outputLength = inputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint index = 0, outputOffset = 0; index < (nuint)inputLength; index++, outputOffset += maxOutputElementSize) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), Unsafe.Add(ref inputIterator, index)); + } + + writer.Advance(outputLength); + length -= inputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, inputLength); + } + } + + private static void LittleEndianSerialize(ref MessagePackWriter writer, ref double input, int length) + { + ref var inputIterator = ref Unsafe.As(ref input); + const int maxOutputElementSize = sizeof(double) + 1; + const int maxInputSize = int.MaxValue / maxOutputElementSize; + +#if NET8_0_OR_GREATER + unchecked + { + if (Vector256.IsHardwareAccelerated) + { + for (var alignedInputLength = (nuint)(length & (~3)); alignedInputLength > 0; alignedInputLength = (nuint)(length & (~3))) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & (~3); + } + + var outputLength = (int)alignedInputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint inputOffset = 0, outputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector256.Count, outputOffset += (nuint)Vector256.Count * maxOutputElementSize) + { + // Reorder Little Endian bytes to Big Endian. + var shuffled = Vector256.Shuffle(Vector256.LoadUnsafe(ref inputIterator, inputOffset).AsByte(), Vector256.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24)).AsUInt64(); + + // Write 4 Big-Endian doubles. + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 9) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 10), shuffled.GetElement(1)); + Unsafe.Add(ref outputIterator, outputOffset + 18) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 19), shuffled.GetElement(2)); + Unsafe.Add(ref outputIterator, outputOffset + 27) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 28), shuffled.GetElement(3)); + } + + writer.Advance(outputLength); + length -= (int)alignedInputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, alignedInputLength); + } + } + else if (Vector128.IsHardwareAccelerated) + { + for (var alignedInputLength = (nuint)(length & (~1)); alignedInputLength > 0; alignedInputLength = (nuint)(length & (~1))) + { + if (alignedInputLength >= maxInputSize) + { + alignedInputLength = maxInputSize & (~1); + } + + var outputLength = (int)alignedInputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint inputOffset = 0, outputOffset = 0; inputOffset < alignedInputLength; inputOffset += (nuint)Vector128.Count, outputOffset += (nuint)Vector128.Count * maxOutputElementSize) + { + // Reorder Little Endian bytes to Big Endian. + var shuffled = Vector128.Shuffle(Vector128.LoadUnsafe(ref inputIterator, inputOffset).AsByte(), Vector128.Create((byte)7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8)).AsUInt64(); + + // Write 2 Big-Endian doubles. + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), shuffled.GetElement(0)); + Unsafe.Add(ref outputIterator, outputOffset + 9) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 10), shuffled.GetElement(1)); + } + + writer.Advance(outputLength); + length -= (int)alignedInputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, alignedInputLength); + } + } + } +#endif + + while (length > 0) + { + var inputLength = length; + if (inputLength > maxInputSize) + { + inputLength = maxInputSize; + } + + var outputLength = inputLength * maxOutputElementSize; + var destination = writer.GetSpan(outputLength); + ref var outputIterator = ref MemoryMarshal.GetReference(destination); + for (nuint index = 0, outputOffset = 0; index < (nuint)inputLength; index++, outputOffset += maxOutputElementSize) + { + Unsafe.Add(ref outputIterator, outputOffset) = MessagePackCode.Float64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref outputIterator, outputOffset + 1), BinaryPrimitives.ReverseEndianness(Unsafe.Add(ref inputIterator, index))); + } + + writer.Advance(outputLength); + length -= inputLength; + inputIterator = ref Unsafe.Add(ref inputIterator, inputLength); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, byte value) + { + if (value <= MessagePackRange.MaxFixPositiveInt) + { + destination = value; + return 1; + } + else + { + destination = MessagePackCode.UInt8; + Unsafe.Add(ref destination, 1) = value; + return 2; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, sbyte value) + { + if (value < MessagePackRange.MinFixNegativeInt) + { + destination = MessagePackCode.Int8; + Unsafe.Add(ref destination, 1) = unchecked((byte)value); + return 2; + } + else + { + destination = unchecked((byte)value); + return 1; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, ushort value) + { + if (value <= byte.MaxValue) + { + return ReverseWriteUnknown(ref destination, unchecked((byte)value)); + } + else + { + destination = MessagePackCode.UInt16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 3; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, short value) + { + if (value < 0) + { + if (value >= sbyte.MinValue) + { + return ReverseWriteUnknown(ref destination, unchecked((sbyte)value)); + } + else + { + destination = MessagePackCode.Int16; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 3; + } + } + else + { + return ReverseWriteUnknown(ref destination, unchecked((ushort)value)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, uint value) + { + if (value <= ushort.MaxValue) + { + return ReverseWriteUnknown(ref destination, unchecked((ushort)value)); + } + else + { + destination = MessagePackCode.UInt32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 5; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, int value) + { + if (value < 0) + { + if (value >= short.MinValue) + { + return ReverseWriteUnknown(ref destination, unchecked((short)value)); + } + else + { + destination = MessagePackCode.Int32; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 5; + } + } + else + { + return ReverseWriteUnknown(ref destination, unchecked((uint)value)); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, ulong value) + { + if (value <= uint.MaxValue) + { + return ReverseWriteUnknown(ref destination, unchecked((uint)value)); + } + else + { + destination = MessagePackCode.UInt64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 9; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static nuint ReverseWriteUnknown(ref byte destination, long value) + { + if (value < 0) + { + if (value >= int.MinValue) + { + return ReverseWriteUnknown(ref destination, unchecked((int)value)); + } + else + { + destination = MessagePackCode.Int64; + Unsafe.WriteUnaligned(ref Unsafe.Add(ref destination, 1), BinaryPrimitives.ReverseEndianness(value)); + return 9; + } + } + else + { + return ReverseWriteUnknown(ref destination, unchecked((ulong)value)); + } + } +} diff --git a/src/MessagePack/Resolvers/BuiltinResolver.cs b/src/MessagePack/Resolvers/BuiltinResolver.cs index 1f5da3a47..9da05ea6d 100644 --- a/src/MessagePack/Resolvers/BuiltinResolver.cs +++ b/src/MessagePack/Resolvers/BuiltinResolver.cs @@ -125,6 +125,20 @@ internal static class BuiltinResolverGetFormatterHelper { typeof(string[]), NullableStringArrayFormatter.Instance }, // well known collections +#if NET8_0_OR_GREATER + { typeof(List), Int16ListFormatter.Instance }, + { typeof(List), Int32ListFormatter.Instance }, + { typeof(List), Int64ListFormatter.Instance }, + { typeof(List), UInt16ListFormatter.Instance }, + { typeof(List), UInt32ListFormatter.Instance }, + { typeof(List), UInt64ListFormatter.Instance }, + { typeof(List), SingleListFormatter.Instance }, + { typeof(List), DoubleListFormatter.Instance }, + { typeof(List), BooleanListFormatter.Instance }, + { typeof(List), ByteListFormatter.Instance }, + { typeof(List), SByteListFormatter.Instance }, + { typeof(List), CharListFormatter.Instance }, +#else { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, @@ -136,8 +150,9 @@ internal static class BuiltinResolverGetFormatterHelper { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, - { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, +#endif + { typeof(List), new ListFormatter() }, { typeof(List), new ListFormatter() }, { typeof(object[]), new ArrayFormatter() }, diff --git a/src/MessagePack/net8.0/PublicAPI.Unshipped.txt b/src/MessagePack/net8.0/PublicAPI.Unshipped.txt index f4791fc12..9e5540129 100644 --- a/src/MessagePack/net8.0/PublicAPI.Unshipped.txt +++ b/src/MessagePack/net8.0/PublicAPI.Unshipped.txt @@ -13,6 +13,27 @@ const MessagePack.ReservedExtensionTypeCodes.UnityVector3 = 31 -> sbyte const MessagePack.ReservedExtensionTypeCodes.UnityVector4 = 32 -> sbyte MessagePack.CompositeResolverAttribute MessagePack.CompositeResolverAttribute.CompositeResolverAttribute(params System.Type![]! resolvers) -> void +MessagePack.Formatters.BooleanListFormatter +MessagePack.Formatters.BooleanListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.BooleanListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.ByteListFormatter +MessagePack.Formatters.ByteListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.ByteListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.CharListFormatter +MessagePack.Formatters.CharListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.CharListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.DoubleListFormatter +MessagePack.Formatters.DoubleListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.DoubleListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.Int16ListFormatter +MessagePack.Formatters.Int16ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.Int16ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.Int32ListFormatter +MessagePack.Formatters.Int32ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.Int32ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.Int64ListFormatter +MessagePack.Formatters.Int64ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.Int64ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void MessagePack.Formatters.Matrix3x2Formatter MessagePack.Formatters.Matrix3x2Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Matrix3x2 MessagePack.Formatters.Matrix3x2Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Matrix3x2 value, MessagePack.MessagePackSerializerOptions! options) -> void @@ -27,6 +48,21 @@ MessagePack.Formatters.PriorityQueueFormatter.Serialize(ref MessagePack.Formatters.QuaternionFormatter MessagePack.Formatters.QuaternionFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Quaternion MessagePack.Formatters.QuaternionFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Quaternion value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.SByteListFormatter +MessagePack.Formatters.SByteListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.SByteListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.SingleListFormatter +MessagePack.Formatters.SingleListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.SingleListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.UInt16ListFormatter +MessagePack.Formatters.UInt16ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.UInt16ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.UInt32ListFormatter +MessagePack.Formatters.UInt32ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.UInt32ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void +MessagePack.Formatters.UInt64ListFormatter +MessagePack.Formatters.UInt64ListFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Collections.Generic.List? +MessagePack.Formatters.UInt64ListFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Collections.Generic.List? value, MessagePack.MessagePackSerializerOptions! options) -> void MessagePack.Formatters.Vector2Formatter MessagePack.Formatters.Vector2Formatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions! options) -> System.Numerics.Vector2 MessagePack.Formatters.Vector2Formatter.Serialize(ref MessagePack.MessagePackWriter writer, System.Numerics.Vector2 value, MessagePack.MessagePackSerializerOptions! options) -> void @@ -58,9 +94,21 @@ MessagePack.Internal.GeneratedAssemblyMessagePackResolverAttribute.ResolverType. MessagePack.ReservedExtensionTypeCodes MessagePack.Resolvers.SourceGeneratedFormatterResolver MessagePack.Resolvers.SourceGeneratedFormatterResolver.GetFormatter() -> MessagePack.Formatters.IMessagePackFormatter? +static readonly MessagePack.Formatters.BooleanListFormatter.Instance -> MessagePack.Formatters.BooleanListFormatter! +static readonly MessagePack.Formatters.ByteListFormatter.Instance -> MessagePack.Formatters.ByteListFormatter! +static readonly MessagePack.Formatters.CharListFormatter.Instance -> MessagePack.Formatters.CharListFormatter! +static readonly MessagePack.Formatters.DoubleListFormatter.Instance -> MessagePack.Formatters.DoubleListFormatter! +static readonly MessagePack.Formatters.Int16ListFormatter.Instance -> MessagePack.Formatters.Int16ListFormatter! +static readonly MessagePack.Formatters.Int32ListFormatter.Instance -> MessagePack.Formatters.Int32ListFormatter! +static readonly MessagePack.Formatters.Int64ListFormatter.Instance -> MessagePack.Formatters.Int64ListFormatter! static readonly MessagePack.Formatters.Matrix3x2Formatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! static readonly MessagePack.Formatters.Matrix4x4Formatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! static readonly MessagePack.Formatters.QuaternionFormatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! +static readonly MessagePack.Formatters.SByteListFormatter.Instance -> MessagePack.Formatters.SByteListFormatter! +static readonly MessagePack.Formatters.SingleListFormatter.Instance -> MessagePack.Formatters.SingleListFormatter! +static readonly MessagePack.Formatters.UInt16ListFormatter.Instance -> MessagePack.Formatters.UInt16ListFormatter! +static readonly MessagePack.Formatters.UInt32ListFormatter.Instance -> MessagePack.Formatters.UInt32ListFormatter! +static readonly MessagePack.Formatters.UInt64ListFormatter.Instance -> MessagePack.Formatters.UInt64ListFormatter! static readonly MessagePack.Formatters.Vector2Formatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! static readonly MessagePack.Formatters.Vector3Formatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! static readonly MessagePack.Formatters.Vector4Formatter.Instance -> MessagePack.Formatters.IMessagePackFormatter! diff --git a/tests/MessagePack.Tests/PrimitiveCollectionTests.cs b/tests/MessagePack.Tests/PrimitiveCollectionTests.cs new file mode 100644 index 000000000..8fea53220 --- /dev/null +++ b/tests/MessagePack.Tests/PrimitiveCollectionTests.cs @@ -0,0 +1,95 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using Xunit; + +namespace MessagePack.Tests; + +public class PrimitiveCollectionTests +{ + private static T Convert(T value) + { + var binary = MessagePackSerializer.Serialize(value); + var newValue = MessagePackSerializer.Deserialize(binary); + return newValue; + } + + public static object[][] CollectionTestData = [ + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + [Array.Empty()], + + [new bool[15] { true, true, false, false, true, false, false, false, false, true, true, true, true, true, true }], + [new sbyte[15] { 2, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, }], // 1byte + [new short[15] { 2, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, }], // 1byte + [new int[15] { 2, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, }], // 1byte + [new long[15] { 2, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, }], // 1byte + [new sbyte[15] { -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new short[15] { -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new int[15] { -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new long[15] { -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new short[15] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121 }], // 3byte, 2byte, 1byte + [new int[15] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121 }], // 3byte, 2byte, 1byte + [new long[15] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121 }], // 3byte, 2byte, 1byte + [new int[15] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1 }], // 5byte, 3byte, 2byte, 1byte + [new long[15] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1 }], // 5byte, 3byte, 2byte, 1byte + [new long[15] { long.MinValue, uint.MaxValue, short.MinValue, byte.MaxValue, sbyte.MaxValue, long.MaxValue, int.MinValue, short.MaxValue, sbyte.MinValue, -2, long.MaxValue - 1000L, uint.MaxValue - 1000L, short.MinValue + 1000L, sbyte.MaxValue + 10L, 0, }], + [new ushort[15] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ushort)sbyte.MaxValue }], // 1byte + [new uint[15] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ushort)sbyte.MaxValue }], // 1byte + [new ulong[15] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ushort)sbyte.MaxValue }], // 1byte + + [new bool[16] { true, true, false, false, true, false, false, false, false, true, true, true, true, true, true, false }], + [new sbyte[16] { 3, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, }], // 1byte + [new short[16] { 3, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, }], // 1byte + [new int[16] { 3, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, }], // 1byte + [new long[16] { 3, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, }], // 1byte + [new sbyte[16] { 120, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new short[16] { 120, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new int[16] { 120, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new long[16] { 120, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -116, 121, -114, }], // 2byte, 1byte + [new short[16] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121, 1024 }], // 3byte, 2byte, 1byte + [new int[16] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121, 1024 }], // 3byte, 2byte, 1byte + [new long[16] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -116, 121, 1024 }], // 3byte, 2byte, 1byte + [new int[16] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1, -65539 }], // 5byte, 3byte, 2byte, 1byte + [new long[16] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1, -65539 }], // 5byte, 3byte, 2byte, 1byte + [new long[16] { long.MinValue, uint.MaxValue, short.MinValue, byte.MaxValue, sbyte.MaxValue, long.MaxValue, int.MinValue, short.MaxValue, sbyte.MinValue, -2, long.MaxValue - 1000L, uint.MaxValue - 1000L, short.MinValue + 1000L, sbyte.MaxValue + 10L, 0, -65539 }], + [new ushort[16] { sbyte.MaxValue - 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ushort)sbyte.MaxValue, }], // 1byte + [new uint[16] { sbyte.MaxValue - 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (uint)sbyte.MaxValue, }], // 1byte + [new ulong[16] { sbyte.MaxValue - 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ulong)sbyte.MaxValue, }], // 1byte + + [new bool[17] { true, true, false, false, true, false, false, false, false, true, true, true, true, true, true, false, false }], + [new sbyte[17] { 4, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, }], // 1byte + [new short[17] { 4, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, }], // 1byte + [new int[17] { 4, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, }], // 1byte + [new long[17] { 4, -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, }], // 1byte + [new sbyte[17] { -32, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -117, 121, -114, 120, }], // 2byte, 1byte + [new short[17] { -32, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -117, 121, -114, 120, }], // 2byte, 1byte + [new int[17] { -32, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -117, 121, -114, 120, }], // 2byte, 1byte + [new long[17] { -32, -128, 127, -126, 126, -124, 125, -122, 124, -120, 123, -118, 122, -117, 121, -114, 120, }], // 2byte, 1byte + [new short[17] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -117, 121, 1024, 240, }], // 3byte, 2byte, 1byte + [new int[17] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -117, 121, 1024, 240, }], // 3byte, 2byte, 1byte + [new long[17] { short.MinValue, -128, 127, short.MaxValue, -124, 125, 129, 124, -120, 255, -118, 122, 256, -117, 121, 1024, 240, }], // 3byte, 2byte, 1byte + [new int[17] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1, -65539, -31, }], // 5byte, 3byte, 2byte, 1byte + [new long[17] { int.MinValue, ushort.MaxValue, sbyte.MinValue, sbyte.MaxValue, int.MaxValue, short.MinValue, byte.MaxValue, 0, int.MaxValue / 2, ushort.MaxValue - 4, -100, -1, int.MinValue + 4, short.MaxValue - 4, 1, -65539, -31, }], // 5byte, 3byte, 2byte, 1byte + [new long[17] { long.MinValue, uint.MaxValue, short.MinValue, byte.MaxValue, sbyte.MaxValue, long.MaxValue, int.MinValue, short.MaxValue, sbyte.MinValue, -2, long.MaxValue - 1000L, uint.MaxValue - 1000L, short.MinValue + 1000L, sbyte.MaxValue + 10L, 0, -65539, -31, }], + [new ushort[17] { sbyte.MaxValue - 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ushort)sbyte.MaxValue, sbyte.MaxValue - 1, }], // 1byte + [new uint[17] { sbyte.MaxValue - 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (uint)sbyte.MaxValue, sbyte.MaxValue - 1, }], // 1byte + [new ulong[17] { sbyte.MaxValue - 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, (ulong)sbyte.MaxValue, sbyte.MaxValue - 1, }], // 1byte + ]; + + [Theory] + [MemberData(nameof(CollectionTestData))] + public void ConcreteCollectionTest(T[] x) + { + Convert(x).IsStructuralEqual(x); + var list = new List(x); + Convert(list).IsStructuralEqual(list); + } +}