diff options
author | Andrew Arnott <andrew.arnott@microsoft.com> | 2022-05-24 05:47:44 +0300 |
---|---|---|
committer | Andrew Arnott <andrewarnott@live.com> | 2022-05-24 19:08:29 +0300 |
commit | c7ef724b09e12284062fb5534d74c78164685fbc (patch) | |
tree | e63c48aa3fd02b6ce7ab72fd04f296259cf862c6 | |
parent | 50b549fe1ffa6346675a55e5bb914c0a8def3c37 (diff) |
Add built-in support for .NET 6 `DateOnly` and `TimeOnly` types
`DateOnly` requires 5 bytes (typically).
`TimeOnly` requires 6 bytes for second resolution or 8 bytes for ticks resolution. This is automatically selected based on the original value.
Closes #1240
# Conflicts:
# src/MessagePack/net6.0/PublicAPI.Unshipped.txt
6 files changed, 145 insertions, 18 deletions
@@ -1499,24 +1499,36 @@ var resolver = MessagePack.Resolvers.CompositeResolver.Create( ## Reserved Extension Types -MessagePack for C# already used some MessagePack extension type codes, be careful to use same ext code. +MessagePack for C# already used some MessagePack extension type codes, be careful to avoid using the same ext code for other purposes. + +Range | Reserved for +--|-- +\[-128, -1\] | Reserved by the msgpack spec for predefined types +\[30, 120) | Reserved for this library's use to support common types in .NET + +This leaves the following ranges for your use: + +- \[0, 30) +- \[120, 127] + +Within the *reserved* ranges, this library defines or implements extensions that use these type codes: | Code | Type | Use by | -| --- | --- | --- | -| -1 | DateTime | MessagePack-spec reserved for timestamp | -| 30 | Vector2[] | for Unity, UnsafeBlitFormatter | -| 31 | Vector3[] | for Unity, UnsafeBlitFormatter | -| 32 | Vector4[] | for Unity, UnsafeBlitFormatter | -| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter | -| 34 | Color[] | for Unity, UnsafeBlitFormatter | -| 35 | Bounds[] | for Unity, UnsafeBlitFormatter | -| 36 | Rect[] | for Unity, UnsafeBlitFormatter | -| 37 | Int[] | for Unity, UnsafeBlitFormatter | -| 38 | Float[] | for Unity, UnsafeBlitFormatter | -| 39 | Double[] | for Unity, UnsafeBlitFormatter | -| 98 | All | MessagePackCompression.Lz4BlockArray | -| 99 | All | MessagePackCompression.Lz4Block | -| 100 | object | TypelessFormatter | +| ---- | ---- | --- | +| -1 | DateTime | MessagePack-spec reserved for timestamp | +| 30 | Vector2[] | for Unity, UnsafeBlitFormatter | +| 31 | Vector3[] | for Unity, UnsafeBlitFormatter | +| 32 | Vector4[] | for Unity, UnsafeBlitFormatter | +| 33 | Quaternion[] | for Unity, UnsafeBlitFormatter | +| 34 | Color[] | for Unity, UnsafeBlitFormatter | +| 35 | Bounds[] | for Unity, UnsafeBlitFormatter | +| 36 | Rect[] | for Unity, UnsafeBlitFormatter | +| 37 | Int[] | for Unity, UnsafeBlitFormatter | +| 38 | Float[] | for Unity, UnsafeBlitFormatter | +| 39 | Double[] | for Unity, UnsafeBlitFormatter | +| 98 | All | MessagePackCompression.Lz4BlockArray | +| 99 | All | MessagePackCompression.Lz4Block | +| 100 | object | TypelessFormatter | ## Unity support diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/DateTimeFormatters.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/DateTimeFormatters.cs index 06d2ef80..07da2097 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/DateTimeFormatters.cs +++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Formatters/DateTimeFormatters.cs @@ -70,4 +70,50 @@ namespace MessagePack.Formatters return array; } } + +#if NET6_0_OR_GREATER + /// <summary> + /// Serializes a <see cref="DateOnly"/> value as an ordinary <see cref="int"/> using the <see cref="DateOnly.DayNumber"/>. + /// </summary> + public sealed class DateOnlyFormatter : IMessagePackFormatter<DateOnly> + { + public static readonly DateOnlyFormatter Instance = new DateOnlyFormatter(); + + private DateOnlyFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, DateOnly value, MessagePackSerializerOptions options) + { + writer.Write(value.DayNumber); + } + + public DateOnly Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + return DateOnly.FromDayNumber(reader.ReadInt32()); + } + } + + /// <summary> + /// Serializes a <see cref="TimeOnly"/> value as an extension, recording either seconds or ticks depending on the resolution required. + /// </summary> + public sealed class TimeOnlyFormatter : IMessagePackFormatter<TimeOnly> + { + public static readonly TimeOnlyFormatter Instance = new TimeOnlyFormatter(); + + private TimeOnlyFormatter() + { + } + + public void Serialize(ref MessagePackWriter writer, TimeOnly value, MessagePackSerializerOptions options) + { + writer.Write(value.Ticks); + } + + public TimeOnly Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) + { + return new TimeOnly(reader.ReadInt64()); + } + } +#endif } diff --git a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/BuiltinResolver.cs b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/BuiltinResolver.cs index 000b9fbd..0defd99f 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/BuiltinResolver.cs +++ b/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Resolvers/BuiltinResolver.cs @@ -64,9 +64,13 @@ namespace MessagePack.Internal { typeof(byte), ByteFormatter.Instance }, { typeof(sbyte), SByteFormatter.Instance }, { typeof(DateTime), DateTimeFormatter.Instance }, +#if NET6_0_OR_GREATER + { typeof(DateOnly), DateOnlyFormatter.Instance }, + { typeof(TimeOnly), TimeOnlyFormatter.Instance }, +#endif { typeof(char), CharFormatter.Instance }, - // Nulllable Primitive + // Nullable Primitive { typeof(Int16?), NullableInt16Formatter.Instance }, { typeof(Int32?), NullableInt32Formatter.Instance }, { typeof(Int64?), NullableInt64Formatter.Instance }, diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs index 12abda44..35123476 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs +++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/StandardClassLibraryFormatterTests.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Linq; +using Nerdbank.Streams; using Xunit; using Xunit.Abstractions; @@ -77,5 +77,60 @@ namespace MessagePack.Tests byte[] byte_array = MessagePackSerializer.Deserialize<byte[]>(input); Assert.Equal(new byte[] { 1, 2, 3 }, byte_array); } + +#if NET6_0_OR_GREATER + [Fact] + public void DateOnly() + { + var value = new DateOnly(2012, 3, 5); + this.AssertRoundtrip(value); + this.AssertRoundtrip<DateOnly?>(value); + this.AssertRoundtrip(new[] { value }); + } + + [Fact] + public void TimeOnly() + { + TimeOnly lowRes = new TimeOnly(5, 4, 3); + this.AssertRoundtrip(lowRes); + this.AssertRoundtrip<TimeOnly?>(lowRes); + this.AssertRoundtrip(new[] { lowRes }); + + TimeOnly mediumRes = new TimeOnly(5, 4, 3, 2); + this.AssertRoundtrip(mediumRes); + this.AssertRoundtrip<TimeOnly?>(mediumRes); + this.AssertRoundtrip(new[] { mediumRes }); + + TimeOnly highRes = new TimeOnly(lowRes.Ticks + 1); + this.AssertRoundtrip(highRes); + this.AssertRoundtrip(System.TimeOnly.MaxValue); + } +#endif + + private void AssertRoundtrip<T>(T value) + { + Assert.Equal(value, this.Roundtrip(value, breakupBuffer: false)); + Assert.Equal(value, this.Roundtrip(value, breakupBuffer: true)); + } + + private T Roundtrip<T>(T value, bool breakupBuffer = false) + { + byte[] msgpack = MessagePackSerializer.Serialize(value, MessagePackSerializerOptions.Standard); + this.logger.WriteLine("{0} 0x{1}", value, TestUtilities.ToHex(msgpack)); + + if (breakupBuffer) + { + using (Sequence<byte> seq = new Sequence<byte>()) + { + seq.Append(msgpack.AsMemory(0, msgpack.Length - 1)); + seq.Append(msgpack.AsMemory(msgpack.Length - 1, 1)); + return MessagePackSerializer.Deserialize<T>(seq, MessagePackSerializerOptions.Standard); + } + } + else + { + return MessagePackSerializer.Deserialize<T>(msgpack, MessagePackSerializerOptions.Standard); + } + } } } diff --git a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/TestUtilities.cs b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/TestUtilities.cs index 0a4bf9fb..abc85b0f 100644 --- a/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/TestUtilities.cs +++ b/src/MessagePack.UnityClient/Assets/Scripts/Tests/ShareTests/TestUtilities.cs @@ -15,6 +15,8 @@ namespace MessagePack.Tests /// Gets a value indicating whether the mono runtime is executing this code. /// </summary> internal static bool IsRunningOnMono => Type.GetType("Mono.Runtime") != null; + + internal static string ToHex(byte[] buffer) => BitConverter.ToString(buffer).Replace("-", string.Empty).ToLowerInvariant(); } public class NullTestOutputHelper : ITestOutputHelper diff --git a/src/MessagePack/net6.0/PublicAPI.Unshipped.txt b/src/MessagePack/net6.0/PublicAPI.Unshipped.txt index 9ba64088..78006a92 100644 --- a/src/MessagePack/net6.0/PublicAPI.Unshipped.txt +++ b/src/MessagePack/net6.0/PublicAPI.Unshipped.txt @@ -1,6 +1,14 @@ +MessagePack.Formatters.DateOnlyFormatter +MessagePack.Formatters.DateOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.DateOnly +MessagePack.Formatters.DateOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.DateOnly value, MessagePack.MessagePackSerializerOptions options) -> void MessagePack.Formatters.StringInterningFormatter MessagePack.Formatters.StringInterningFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> string MessagePack.Formatters.StringInterningFormatter.Serialize(ref MessagePack.MessagePackWriter writer, string value, MessagePack.MessagePackSerializerOptions options) -> void MessagePack.Formatters.StringInterningFormatter.StringInterningFormatter() -> void +MessagePack.Formatters.TimeOnlyFormatter +MessagePack.Formatters.TimeOnlyFormatter.Deserialize(ref MessagePack.MessagePackReader reader, MessagePack.MessagePackSerializerOptions options) -> System.TimeOnly +MessagePack.Formatters.TimeOnlyFormatter.Serialize(ref MessagePack.MessagePackWriter writer, System.TimeOnly value, MessagePack.MessagePackSerializerOptions options) -> void MessagePack.MessagePackSerializerOptions.CompressionMinLength.get -> int MessagePack.MessagePackSerializerOptions.WithCompressionMinLength(int compressionMinLength) -> MessagePack.MessagePackSerializerOptions +static readonly MessagePack.Formatters.DateOnlyFormatter.Instance -> MessagePack.Formatters.DateOnlyFormatter +static readonly MessagePack.Formatters.TimeOnlyFormatter.Instance -> MessagePack.Formatters.TimeOnlyFormatter |