diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..012a22a --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,15 @@ + + + + true + + + + + + + + + + + diff --git a/bench/bench.csproj b/bench/bench.csproj index c5a530c..315d038 100644 --- a/bench/bench.csproj +++ b/bench/bench.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/src/Serde.MsgPack.csproj b/src/Serde.MsgPack.csproj index 7c0a8b9..692dc82 100644 --- a/src/Serde.MsgPack.csproj +++ b/src/Serde.MsgPack.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/reader/ArrayBufReader.cs b/src/reader/ArrayBufReader.cs index 4cdeb27..a3870bd 100644 --- a/src/reader/ArrayBufReader.cs +++ b/src/reader/ArrayBufReader.cs @@ -15,6 +15,6 @@ public void Advance(int count) public bool FillBuffer(int fillCount) { - return false; + return _offset + fillCount <= _buffer.Length; } } \ No newline at end of file diff --git a/src/reader/MsgPackReader.Collections.cs b/src/reader/MsgPackReader.Collections.cs index 7036829..5a09976 100644 --- a/src/reader/MsgPackReader.Collections.cs +++ b/src/reader/MsgPackReader.Collections.cs @@ -1,4 +1,6 @@ +using System.Buffers; + namespace Serde.MsgPack; partial class MsgPackReader @@ -6,7 +8,8 @@ partial class MsgPackReader private struct DeserializeCollection(MsgPackReader deserializer, bool isDict, int length) : ITypeDeserializer { private int _index; - int? ITypeDeserializer.SizeOpt => isDict switch { + int? ITypeDeserializer.SizeOpt => isDict switch + { true => length / 2, false => length, }; @@ -130,5 +133,18 @@ void ITypeDeserializer.SkipValue(ISerdeInfo info, int index) { throw new NotImplementedException(); } + + DateTimeOffset ITypeDeserializer.ReadDateTimeOffset(ISerdeInfo info, int index) + { + var next = deserializer.ReadDateTimeOffset(); + _index++; + return next; + } + + void ITypeDeserializer.ReadBytes(ISerdeInfo info, int index, IBufferWriter writer) + { + deserializer.ReadBytes(writer); + _index++; + } } } \ No newline at end of file diff --git a/src/reader/MsgPackReader.ITypeDeserializer.cs b/src/reader/MsgPackReader.ITypeDeserializer.cs index 6b6bb02..38ea92c 100644 --- a/src/reader/MsgPackReader.ITypeDeserializer.cs +++ b/src/reader/MsgPackReader.ITypeDeserializer.cs @@ -1,4 +1,6 @@ +using System.Buffers; + namespace Serde.MsgPack; partial class MsgPackReader @@ -71,5 +73,13 @@ int ITypeDeserializer.TryReadIndex(ISerdeInfo map, out string? errorName) return ITypeDeserializer.IndexNotFound; } } + + DateTimeOffset ITypeDeserializer.ReadDateTimeOffset(ISerdeInfo info, int index) + => deserializer.ReadDateTimeOffset(); + + void ITypeDeserializer.ReadBytes(ISerdeInfo info, int index, IBufferWriter writer) + { + deserializer.ReadBytes(writer); + } } } \ No newline at end of file diff --git a/src/reader/MsgPackReader.cs b/src/reader/MsgPackReader.cs index 8946973..617f864 100644 --- a/src/reader/MsgPackReader.cs +++ b/src/reader/MsgPackReader.cs @@ -1,8 +1,10 @@ +using System.Buffers; using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Runtime.CompilerServices; using System.Text; using Serde.IO; @@ -349,6 +351,32 @@ private string ReadString() return str; } + public DateTimeOffset ReadDateTimeOffset() + { + return DateTime.Parse(ReadString(), styles: DateTimeStyles.RoundtripKind); + } + + public void ReadBytes(IBufferWriter writer) + { + var b = EatByteOrThrow(); + int length = b switch + { + 0xc4 => EatByteOrThrow(), + 0xc5 => ReadBigEndianU16(), // 16-bit length + 0xc6 => checked((int)ReadBigEndianU32()), // 32-bit length + _ => throw new DeserializeException($"Expected bytes, got 0x{b:x}"), + }; + if (!_reader.FillBuffer(length)) + { + ThrowEof(); + } + var inputSpan = _reader.Span[..length]; + var outSpan = writer.GetSpan(length); + inputSpan.CopyTo(outSpan); + _reader.Advance(length); + writer.Advance(length); + } + private ReadOnlySpan ReadUtf8Span() { var b = EatByteOrThrow(); diff --git a/src/writer/MsgPackWriter.EnumSerializer.cs b/src/writer/MsgPackWriter.EnumSerializer.cs index 0be0867..78d9b5d 100644 --- a/src/writer/MsgPackWriter.EnumSerializer.cs +++ b/src/writer/MsgPackWriter.EnumSerializer.cs @@ -35,5 +35,9 @@ public void WriteValue(ISerdeInfo typeInfo, int index, T value, ISerialize public void WriteU16(ISerdeInfo typeInfo, int index, ushort u16) => writer.WriteU16(u16); public void WriteU32(ISerdeInfo typeInfo, int index, uint u32) => writer.WriteU32(u32); public void WriteU64(ISerdeInfo typeInfo, int index, ulong u64) => writer.WriteU64(u64); + public void WriteDateTimeOffset(ISerdeInfo typeInfo, int index, DateTimeOffset dateTimeOffset) + => writer.WriteDateTimeOffset(dateTimeOffset); + public void WriteBytes(ISerdeInfo typeInfo, int index, ReadOnlyMemory bytes) + => writer.WriteBytes(bytes); } } \ No newline at end of file diff --git a/src/writer/MsgPackWriter.ISerializeCollection.cs b/src/writer/MsgPackWriter.ISerializeCollection.cs index 3f0b815..afa1625 100644 --- a/src/writer/MsgPackWriter.ISerializeCollection.cs +++ b/src/writer/MsgPackWriter.ISerializeCollection.cs @@ -58,5 +58,11 @@ public void WriteU8(ISerdeInfo typeInfo, int index, byte b) public void WriteValue(ISerdeInfo typeInfo, int index, T value, ISerialize serialize) where T : class? => serialize.Serialize(value, writer); + + public void WriteDateTimeOffset(ISerdeInfo typeInfo, int index, DateTimeOffset dateTimeOffset) + => writer.WriteDateTimeOffset(dateTimeOffset); + + public void WriteBytes(ISerdeInfo typeInfo, int index, ReadOnlyMemory bytes) + => writer.WriteBytes(bytes); } } \ No newline at end of file diff --git a/src/writer/MsgPackWriter.ISerializeType.cs b/src/writer/MsgPackWriter.ISerializeType.cs index baccd46..8bafbfd 100644 --- a/src/writer/MsgPackWriter.ISerializeType.cs +++ b/src/writer/MsgPackWriter.ISerializeType.cs @@ -111,4 +111,16 @@ void ITypeSerializer.WriteNull(ISerdeInfo typeInfo, int index) WritePropertyName(typeInfo, index); WriteNull(); } + + void ITypeSerializer.WriteDateTimeOffset(ISerdeInfo typeInfo, int index, DateTimeOffset dt) + { + WritePropertyName(typeInfo, index); + WriteDateTimeOffset(dt); + } + + void ITypeSerializer.WriteBytes(ISerdeInfo typeInfo, int index, ReadOnlyMemory bytes) + { + WritePropertyName(typeInfo, index); + WriteBytes(bytes); + } } \ No newline at end of file diff --git a/src/writer/MsgPackWriter.cs b/src/writer/MsgPackWriter.cs index 33db05d..22f24e2 100644 --- a/src/writer/MsgPackWriter.cs +++ b/src/writer/MsgPackWriter.cs @@ -137,6 +137,43 @@ public void WriteNull() _out.Add(0xc0); } + public void WriteDateTimeOffset(DateTimeOffset dt) + { + WriteString(dt.ToString("O")); + } + + public void WriteBytes(ReadOnlyMemory bytes) + { + byte code = bytes.Length switch + { + <= 0xff => 0xc4, + <= 0xffff => 0xc5, + _ => 0xc6 + }; + var prefixLen = code switch + { + 0xc4 => 2, + 0xc5 => 3, + _ => 5 + }; + var span = _out.GetAppendSpan(prefixLen + bytes.Length); + _out.Count += prefixLen + bytes.Length; + span[0] = code; + switch (prefixLen) + { + case 2: + span[1] = (byte)bytes.Length; + break; + case 3: + WriteBigEndian((ushort)bytes.Length); + break; + case 5: + WriteBigEndian((uint)bytes.Length); + break; + } + bytes.Span.CopyTo(span[prefixLen..]); + } + public void WriteI8(sbyte b) => WriteI64(b); private static readonly Encoding _utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); diff --git a/tests/RoundTripTests.cs b/tests/RoundTripTests.cs index d46a839..952b19a 100644 --- a/tests/RoundTripTests.cs +++ b/tests/RoundTripTests.cs @@ -181,6 +181,13 @@ public void TestFloat() AssertRoundTrip(float.PositiveInfinity, F32Proxy.Instance); } + [Fact] + public void TestBytes() + { + var bytes = new byte[] { 1, 2, 3, 4, 5 }; + AssertRoundTrip(bytes, ByteArrayProxy.Instance); + } + private static void AssertRoundTrip(T expected) where T : ISerializeProvider, IDeserializeProvider, IEquatable { diff --git a/tests/Serde.MsgPack.Tests.csproj b/tests/Serde.MsgPack.Tests.csproj index 1952564..6490b44 100644 --- a/tests/Serde.MsgPack.Tests.csproj +++ b/tests/Serde.MsgPack.Tests.csproj @@ -8,12 +8,12 @@ - - - - - - + + + + + +